A couple of weeks ago we talked about the game of Tic Tac Toe. I thought it would be fit to talk about another game, Battleships. Both games are excellent beginner games for programming and Python makes it very easy to implement them without a lot of hassle. You will notice that this article is a little different from my regular work. Normally I have the output of the function embedded. Since this is a game, like Tic Tac Toe, it is hard to capture the game in a single screen shoot. I will have some screen shoots toward the end of the article when I talk about printing out the board. Before we begin let us lay some ground rules for the game:

1. This is a modified version of the game Battleships.
2. The coordinates used for this game are all numeric.
3. The game will be played against a random computer player.
4. The computer will place random ships that are hidden from the user.
5. The turns alternate between player regards of hit or miss.

Let us begin by laying out our main logic for the game. Here is some pseudo code that will help keep us inline:

1. Setup the boards for both computer and user
2. Place ships
3. Alternate turns asking for coordinates and check win condition.

That is just a very rough outline. Of course the game itself is a lot more complex than this. So let’s get started. First we need to think about data representation. We need to have 2 boards that are 10 X 10. That is easy, it is just a list of lists with 10 lists. We will say that a cell is empty if it contains a “-1” value. So we just need to initiate this list of lists like this:

```
#setup blank 10x10 board
board = []
for i in range(10):
board_row = []
for j in range(10):
board_row.append(-1)
board.append(board_row)

#setup user and computer boards
user_board = copy.deepcopy(board)
comp_board = copy.deepcopy(board)

```

Notice that we had to use the Python copy module to make sure we are not pointing to the same memory. There are other ways to this, like having the code duplicated. This is a topic for another day, in the mean time, here is a nice article about Deep and Shallow copies in Python. You may want to note that some of this principles are actually global and have parallels in other languages. So we have the boards, but what about this ships? Well, that is easy, what if we have a dictionaries attached to our list of list as the final element. The dictionary will look like this:

```
ships = {"Aircraft Carrier":5,
"Battleship":4,
"Submarine":3,
"Destroyer":3,
"Patrol Boat":2}

```

Ok. So we have the easy part taken away. Now we have to look at how to place the ships. That is a little more complex. Lets first think about our logic. We need to figure out first that the coordinates entered by the user are valid. Then we have to ask orientation of the ship. After that we need to make sure that a ship can be actually placed. For example, what if we are trying to place a Patrol Boat at row: 10 col: 8, vertically? We cannot place the ship there. Horizontally we can, but that is another entry. So we will need to validate the request. If all is good, we will use the first letter of the ship name as a place holder on the board. Seeing the code might hep a little, so here it is:

```
def user_place_ships(board,ships):

for ship in ships.keys():

#get coordinates from user and vlidate the postion
valid = False
while(not valid):

print_board("u",board)
print "Placing a/an " + ship
x,y = get_coor()
ori = v_or_h()
valid = validate(board,ships[ship],x,y,ori)
if not valid:
print "Cannot place a ship there.\nPlease take a look at the board and try again."
raw_input("Hit ENTER to continue")

#place the ship
board = place_ship(board,ships[ship],ship[0],ori,x,y)
print_board("u",board)

raw_input("Done placing user ships. Hit ENTER to continue")
return board

```

You will notice that we call 5 functions we did not talk about yet. So let us talk about them. We will talk about the printing function in a little bit. We will take a look first at the get_coor() function. Before you get cold feet I do warn you that this function is lengthy. Keep in mind that the function accounts for user errors and will make sure that the only input it will accept and return is 2 values that are between 1 - 10. Nothing more, nothing less.

```
def get_coor():

while (True):
user_input = raw_input("Please enter coordinates (row,col) ? ")
try:
#see that user entered 2 values seprated by comma
coor = user_input.split(",")
if len(coor) != 2:
raise Exception("Invalid entry, too few/many coordinates.");

#check that 2 values are integers
coor[0] = int(coor[0])-1
coor[1] = int(coor[1])-1

#check that values of integers are between 1 and 10 for both coordinates
if coor[0] > 9 or coor[0] < 0 or coor[1] > 9 or coor[1] < 0:
raise Exception("Invalid entry. Please use values between 1 to 10 only.")

#if everything is ok, return coordinates
return coor

except ValueError:
print "Invalid entry. Please enter only numeric values for coordinates"
except Exception as e:
print e

```

Ok. So that was a mouth full. How about the next one, the v_or_h()? That is a little more straight forward, it only return V or H. This should be simple:

```
def v_or_h():

#get ship orientation from user
while(True):
user_input = raw_input("vertical or horizontal (v,h) ? ")
if user_input == "v" or user_input == "h":
return user_input
else:
print "Invalid input. Please only enter v or h"

```

Recall that we are doing all this to place the user ships. So at this point we have a valid coordinate from the user and an orientation. Now we go back to our placement issue. We need to make sue that the type of ship can be placed where the user wants it. To do this we just need to check that it can physically be placed within the board limits and that the cells are empty (-1).

```
def validate(board,ship,x,y,ori):

#validate the ship can be placed at given coordinates
if ori == "v" and x+ship > 10:
return False
elif ori == "h" and y+ship > 10:
return False
else:
if ori == "v":
for i in range(ship):
if board[x+i][y] != -1:
return False
elif ori == "h":
for i in range(ship):
if board[x][y+i] != -1:
return False

```

You should note that this function returns a boolean value of True or False. This is so we can use it when we write the computer version of place ships, we can use the validate() function without writing a new one. Now all we have look at, excluding the print function is the actual placement of the ships. Recall that all we need to do at this point is put the intial character of the ships name.

```
def place_ship(board,ship,s,ori,x,y):

#place ship based on orientation
if ori == "v":
for i in range(ship):
board[x+i][y] = s
elif ori == "h":
for i in range(ship):
board[x][y+i] = s

return board

```

You will notice that this looks very much like a function that we can reuse, just like the validate() function. Perhaps now is a good time to look at the ship placement for the computer agent.

```
def computer_place_ships(board,ships):

for ship in ships.keys():

#genreate random coordinates and vlidate the postion
valid = False
while(not valid):

x = random.randint(1,10)-1
y = random.randint(1,10)-1
o = random.randint(0,1)
if o == 0:
ori = "v"
else:
ori = "h"
valid = validate(board,ships[ship],x,y,ori)

#place the ship
print "Computer placing a/an " + ship
board = place_ship(board,ships[ship],ship[0],ori,x,y)

return board

```

The main difference between the user and computer ship placement is that the computer uses randomization to place the ships while the user enters the information. At this point we have our setup complete and can play the game. The 2 functions that will handle the game are user_move and computer_move. Let us take a look at both:

```
def user_move(board):

#get coordinates from the user and try to make move
#if move is a hit, check ship sunk and win condition
while(True):
x,y = get_coor()
res = make_move(board,x,y)
if res == "hit":
print "Hit at " + str(x+1) + "," + str(y+1)
check_sink(board,x,y)
board[x][y] = '\$'
if check_win(board):
return "WIN"
elif res == "miss":
print "Sorry, " + str(x+1) + "," + str(y+1) + " is a miss."
board[x][y] = "*"
elif res == "try again":
print "Sorry, that coordinate was already hit. Please try again"

if res != "try again":
return board

def computer_move(board):

#generate user coordinates from the user and try to make move
#if move is a hit, check ship sunk and win condition
while(True):
x = random.randint(1,10)-1
y = random.randint(1,10)-1
res = make_move(board,x,y)
if res == "hit":
print "Hit at " + str(x+1) + "," + str(y+1)
check_sink(board,x,y)
board[x][y] = '\$'
if check_win(board):
return "WIN"
elif res == "miss":
print "Sorry, " + str(x+1) + "," + str(y+1) + " is a miss."
board[x][y] = "*"

if res != "try again":

return board

```

Fist thing you will notice is the infomus function get_coor(). We have used it before and it will work for us again. Now we have 3 more function we need to look at in order to understand how this works. We will start with make_move() this function looks like this:

```
def make_move(board,x,y):

#make a move on the board and return the result, hit, miss or try again for repeat hit
if board[x][y] == -1:
return "miss"
elif board[x][y] == '*' or board[x][y] == '\$':
return "try again"
else:
return "hit"

```

All this function really does is looks at the board in a particular cell and returns a value at that cell. Based of the result the board will be updated if needed. Now we need to check if a ship got sunk. This is easy. If you recall we have a dictionary containing the ships and the amount of hits they can take. This is the last element in the board data representation. So this is not as bad as you might think. We just need to figure out what ship got hit and if it has more damage points.

```
def check_sink(board,x,y):

#figure out what ship was hit
if board[x][y] == "A":
ship = "Aircraft Carrier"
elif board[x][y] == "B":
ship = "Battleship"
elif board[x][y] == "S":
ship = "Submarine"
elif board[x][y] == "D":
ship = "Destroyer"
elif board[x][y] == "P":
ship = "Patrol Boat"

#mark cell as hit and check if sunk
board[-1][ship] -= 1
if board[-1][ship] == 0:
print ship + " Sunk"

```

Cool. Now for the last function, check_win(). Now this can be done better, but I was tired at this point so I went for simple. If you think about it, a board is in a win condition if all the ships are gone. That means that in our representation, all the cells must conation a -1 for empty, a \$ for a hit or a * for a miss. Any other character is a ship and there fore not a win. So the function can look like this:

```
def check_win(board):

#simple for loop to check all cells in 2d board
#if any cell contains a char that is not a hit or a miss return false
for i in range(10):
for j in range(10):
if board[i][j] != -1 and board[i][j] != '*' and board[i][j] != '\$':
return False
return True

```

So now we have our puzzle almost complete. The last pice we need is a good print function. This function is built upon the Tic Tac Toe function, but is a little more complex. Recall that there are 2 printing functions that we need. When the users sees the computer board, the ships should be hidden. However, when the user is presented with their own board, they should see all the ships. In wither case, the hits and misses should be displayed and the ‘-1’ should not be printed. This took me a while to get working properly. Mostly because I wanted it to look good, for me. Here is the printing function:

```
def print_board(s,board):

# WARNING: This function was crafted with a lot of attention. Please be aware that any
#          modifications to this function will result in a poor output of the board
#          layout. You have been warn.

#find out if you are printing the computer or user board
player = "Computer"
if s == "u":
player = "User"

print "The " + player + "'s board look like this: \n"

#print the horizontal numbers
print " ",
for i in range(10):
print "  " + str(i+1) + "  ",
print "\n"

for i in range(10):

#print the vertical line number
if i != 9:
print str(i+1) + "  ",
else:
print str(i+1) + " ",

#print the board values, and cell dividers
for j in range(10):
if board[i][j] == -1:
print ' ',
elif s == "u":
print board[i][j],
elif s == "c":
if board[i][j] == "*" or board[i][j] == "\$":
print board[i][j],
else:
print " ",

if j != 9:
print " | ",
print

#print a horizontal line
if i != 9:
print "   ----------------------------------------------------------"
else:
print
```

So to sum it all up, here is a link to the complete code Battleships in Python Command Line. Here are some screen shoots I took from the game play:

Have Fun!

If you enjoyed this post, please consider leaving a comment or subscribing to the RSS feed to have future articles delivered to your feed reader.

#### You Might Also Like

• ICe_COld

what python version is this?

2.7.6 Although it could easily be modified for Python 3

• Christian Clauss

An alternate formulation of print_board() that runs faster on low power machines (e.g. Pythonista on iPads):

def print_board(s, board):

# WARNING: This function was crafted with a lot of attention. Please be aware that any
# modifications to this function will result in a poor output of the board
# layout. You have been warned.

print("The {}'s board looks like this:n".format('User' if s == 'u' else 'Computer'))
# print horizontal numbers
print(' {}n'.format(''.join([' {} '.format(i+1) for i in xrange(10)])))
for i in range(10):
print('{:<2d} {}'.format(i+1, ' | '.join(
[' ' if board[i][j] == -1 else board[i][j] for j in xrange(10)])))
if i != 9:
print(' ' + '-' * 58) # print a horizontal line
print('')

• kjb

9
x = random.randint(1,10)-1
10 y = random.randint(1,10)-1

why do you use -1 in these lines