python-3.xunit-testingwhile-loopsimplify

Simplifying code for placing ships in Battleship game in Python and conducting Unit Testing


I am currently working on a class project to build the game Battleship in Python. Another user here graciously helped me understand the coding for my board, and now I have come to the step to randomly enter the ships ('S') into positions on the board. For this project, each ship, 'S' is limited to one space on the board. I have developed the code:

randomRow = random.randint(0, grid_size - 1)
randomCol = random.randint(0, grid_size - 1)  
myBoard[randomRow][randomCol] = 'S'

and entered these 3 lines five times to get the five ships needed for my game, however I feel like there must be a cleaner way to enter this information, perhaps with a while loop? In addition to this, my teacher has asked us to confirm that if a ship, 'S', has been placed, no other ship may be entered in the same spot. If I enter the code above five times, I always get five different spots, but I am guessing if I use a while loop, or something else to place the ships, I will need an additional argument to be sure each ship is placed on a different spot, and am stuck in figuring out how to do this.

Lastly, we have been asked to write two Unit Tests for this step. One which will check that the appropriate number of ships (5) have been placed, and one which will check that the other spaces are blank (in my code, denoted by '.' for open water). I am very ignorant when it comes to Unit Testing, and was wondering if anyone could share good resources for beginners on what a Unit Test is, how to implement one, etc. Examples are very helpful to me as well.

For context, here is the full code for this program I have thus far:

import random

# Legend:
# 1. "." = water
# 2. "S" = ship position
# 3. "O" = water that was shot with bullet, a miss because it hit no ship
# 4. "X" = ship sunk!

#global variable for grid size
grid_size = 10
#global variable for grid
myBoard = [['.']*grid_size for i in range(grid_size)]
# display the grid (2d array)

def main():
    # Call drawBoard function with myBoard
    drawBoard(myBoard)
    user_guess(myBoard)

def drawBoard(myBoard):
    # Print header
    print(end="  ")
    for i in range(grid_size):
        print(i, end=" ")
    print()

    # Print rows
    for i in range(grid_size):
        print(i, end=" ")
        for j in range(grid_size):
            print(myBoard[i][j], end=" ") # change grid to myBoard
        print()
      
# Place ships
randomRow = random.randint(0, grid_size - 1)
randomCol = random.randint(0, grid_size - 1)  
myBoard[randomRow][randomCol] = 'S'

randomRow = random.randint(0, grid_size - 1)
randomCol = random.randint(0, grid_size - 1)  
myBoard[randomRow][randomCol] = 'S'

randomRow = random.randint(0, grid_size - 1)
randomCol = random.randint(0, grid_size - 1)  
myBoard[randomRow][randomCol] = 'S'

randomRow = random.randint(0, grid_size - 1)
randomCol = random.randint(0, grid_size - 1)  
myBoard[randomRow][randomCol] = 'S'

randomRow = random.randint(0, grid_size - 1)
randomCol = random.randint(0, grid_size - 1)  
myBoard[randomRow][randomCol] = 'S'

# Call main function
main()

While trying to figure out how to combine my code above with a while loop, I found an example on Youtube, and tried to adopt it to my program, along the lines of:

def create_ships(myBoard):
    for ship in range(5):
        while myBoard[ship_row][ship_column] == "S":
            ship_row, ship_column = get_ship_location()
        myBoard[ship_row][ship_column] = "S"

But was unsure of where to place the previous three lines of code in relation to this function. I tried placing it inside the function, but then when I ran the code, my ships did not print. I am at a loss for how to combine these two elements. Should I include a statement while ship < 5, do something? Any advice would be greatly appreciated. Thank you!


Solution

  • Your current code can place two ships in the same location, but the odds are low.

    Try this to only place a ship if the spot doesn't already have a ship:

    for _ in range(5):
        while True:  # continuously loop...
            randomRow = random.randrange(grid_size)
            randomCol = random.randrange(grid_size)
            # ...until an open space is selected
            if myBoard[randomRow][randomCol] == '.':
                break
        myBoard[randomRow][randomCol] = 'S'
    

    Place more ships, like 99, to see that it works correctly and leaves only one open space in a 10x10 grid.

    As far as a unit test is concerned if you make this code a function to pass in the number of ships to place, and a test function that calls that function and places 100 ships, then it can verify that every grid position was accessed and filled.