I'm relatively new to Python, I decided to make a simple battleship game. Is there anything I should do to make it shorter, or in practice, better?from pprint import pprint as ppimport randomRows = 0Columns = 0turns = 0Answer = NaNprint(Welcome to battleship!)while (Rows > 10) or (Columns > 10) or (Rows <= 0) or (Columns <= 0):    Rows = int(input(Please enter the number of rows you want. \n))    Columns = int(input(Please enter the number of columns you want. \n))def create_grid(Rows, Columns):    #Creates the 2D Data Grid    grid = []    for row in range(Rows):        row = []        for col in range(Columns):            row.append(' ')        grid.append(row)    return gridgrid = create_grid(Rows,Columns)def display_grid(grid, Columns):    #Prints the labels for the grid    column_names = 'abcdefghijklmnopqrstuvwxyz'[:Columns]    print('  | ' + ' | '.join(column_names.upper()) + ' |')    for number, row in enumerate(grid):       print(number + 1, '| ' + ' | '.join(row) + ' |')grid = create_grid(Rows, Columns)display_grid(grid, Columns)def random_row(grid):    #Makes a random row integer    return random.randint(1,len(grid))def random_col(grid):    #Makes a random column integer    return random.randint(1,len(grid[0]))def update_gridHit(grid, GuessRow, GuessColumn):    grid[GuessRow-1][GuessColumn-1] = 'O'def update_gridMiss(grid, GuessRow, GuessColumn):    grid[GuessRow-1][GuessColumn-1] = 'X'ShipRow = random_row(grid)ShipColumn = random_col(grid)#Testing purposes only, comment out if needed.print(ShipRow)print(ShipColumn)while (turns != 5):    GuessRow = int(input(What row do you guess? \n))    GuessColumn = int(input(What column do you guess? \n))    if (GuessRow == ShipRow) and (GuessColumn == ShipColumn):        turns += 1        update_gridHit(grid, GuessRow, GuessColumn)        display_grid(grid, Columns)        print(You hit the battleship! Congratulations!)        break    else:        if (GuessRow < 1 or GuessRow > Rows) or (GuessColumn < 1 or GuessColumn > Columns):            #Warning if the guess is out of the board            print(Outside the set grid. Please pick a number within it your Rows and Columns.)        elif (grid[GuessRow-1][GuessColumn-1] == X):            #If X is there than print that it missed            print(You guessed that already.)        else:            #Updates the grid with an X saying that you missed the ship            turns += 1            print(You missed the ship.)            update_gridMiss(grid, GuessRow, GuessColumn)            display_grid(grid, Columns)    if (turns >= 5):        print(Game over! You ran out of tries)
Python simple battleship game
python;beginner;game;python 3.x
Variable namingIn general, you should follow PEP8, the Python style guide. In particular, it suggests using lowercase for variable names. That is, rows instead of Rows.User input conditionswhile (Rows > 10) or (Columns > 10) or (Rows <= 0) or (Columns <= 0):    Rows = int(input(Please enter the number of rows you want. \n))    Columns = int(input(Please enter the number of columns you want. \n))So, if I enter a correct row, but a wrong column, it'll ask again. Without telling me what I did wrong.I'd suggestrows = 0while not (0 < rows <= 10):    rows = int(input(Please enter the number of rows you want [1-10].))And a similar loop for columns. This has one problem: What if the user does not enter a number but something else? 'Pizza' or 'one'. In that case, the int throws an exception, and that should be handled as well.rows = 0while not (0 < rows <= 10):    try:        rows = int(input(Please enter the number of rows you want [1-10].))    except ValueError:        pass  # Just re-ask the question.Mixing statements and definitionsThe code as given has the formdefine functionrun some statementsdefine functionrun some statementsIn general, you should usedefine functiondefine functiondefine functiondefine function main:    statements    statementsif __name__ == __main__:   main()Re-using variablesNotice how in create_grid, the variable row has two purposes: It is both a number (due to the for row in range(Rows)), and a list. Please don't do that. Because the number row is unused, we should indicate that to the readers by using the conventional _ as variable name.def create_grid(Rows, Columns):    #Creates the 2D Data Grid    grid = []    for _ in range(Rows):        row = []        for _ in range(Columns):            row.append(' ')        grid.append(row)    return grid(Note: I myself prefer using a double underscore __ instead, but convention is _.)EncapsulationNot sure if you are at the level of classes yet, but I'd suggest learning about them.Instead ofdef create_grid(Rows, Columns):    #Creates the 2D Data Grid    grid = []    for row in range(Rows):        row = []        for col in range(Columns):            row.append(' ')        grid.append(row)    return gridgrid = create_grid(Rows,Columns)You can writeclass Grid(object):    def __init__(self, columns, rows):        self.columns = columns        self.rows = rows        self._grid = []        for __ in range(rows):            row = []            for __ in range(columns):                row.append(' ')            self._grid.append(row)Now, that last part (assigning self._grid) can be done 'nicer' (and faster!).self._grid = [ [' ' for __ in range(columns)] for __ in range(rows)]Useless call to .upper.In display_grid, you've written    column_names = 'abcdefghijklmnopqrstuvwxyz'[:Columns]    print('  | ' + ' | '.join(column_names.upper()) + ' |')Why not    column_names = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'[:columns]    print('  | ' + ' | '.join(column_names) + ' |')That's a bit nicer, right?Using enumerateThe builtin enumerate has a second argument: start. You can use it to signal counting should start at 1 instead:    for number, row in enumerate(grid, 1):       print(number, '| ' + ' | '.join(row) + ' |')display_grid as a method on the classIf you follow my suggestion of turning the grid into a class, you can writedef display(self):        Displays a grid.        column_names = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'[:self.columns]    print('  | ' + ' | '.join(column_names) + ' |')    for number, row in enumerate(self._grid, 1):       print(number, '| ' + ' | '.join(row) + ' |')But that's just a suggestion.grid = create_grid(Rows, Columns)display_grid(grid, Columns)DocstringsIn Python, it is the convention to add the documentation of a method/function in a docstring.Instead ofdef random_row(grid):    #Makes a random row integer    return random.randint(1,len(grid))writedef random_row(grid):        Makes a random row integer    (There are also a lot of conventions on how to phrase a docstring. I won't get into that for now.)Parenthesis in conditionalThe code sayswhile (turns != 5):It could (and should) bewhile turns != 5:That is, you don't need the ( and ) there.Weird order in the guess loopNow, the following is just preference. But to me, the final loop is unclear. I've written it as pseudo-code below.The final part looks like this (pseudocode)while (turns != 5):    get_guess    if hit:        turns += 1        notify_success        break    else:        if out_of_bounds:            # Print out-of-bounds message        elif already_guessed:            # Print already guessed message        else:            # Update missif (turns >= 5):    # Print 'you lost'What I notice is that it first checks for hit, before checking if the input is valid (in bounds, and not already guessed).I'd suggest cleaning it up as follows:while turns != 5:    while True:        get_guess()        if out_of_bounds:            # Print out-of-bounds message        elif already_guessed:            # Print already guessed message        else:            break    turns += 1    if hit:        # Update grit hit.        # Print hit message.        break    else:        # Update grid miss        # Print miss message.    display_grid()