## Editing the Code

Once you are ready to start improving your bot you should draw your attention to the calculateMove() function, this is where all the action happens. This function is called every time the server wants you to make a move. It receives a dictionary of information containing the state of the game, namely the gamestate. For greater detail on the contents of the gamestate check out the Understanding The Gamestate section of our Battleships Quick Reference Guide.

From this information it is your task to determine two things:

- Which round you are in.
- What your move for this round is going to be.

You can specify your move by returning a valid dictionary of the form specified in the Making A Valid Move section of our Quick Reference Guide from the calculateMove() function.

For example:

def calculateMove(gamestate): if gamestate["Round"] == 0: # If we are in the ship placement round # The Placement list adds ships in the order that the ships are # listed in the game style e.g. 5,4,3,3,2 places the ship of length # 5 first, the ship of length 4 second, the ship of length 3 third. # # This function does not check for any land and, so, should be used # with a gamestyle that does not include land. move = { "Placement": [ { "Row": "A", "Column": 1, "Orientation": "H" }, { "Row": "B", "Column": 6, "Orientation": "V" }, { "Row": "C", "Column": 1, "Orientation": "H" }, { "Row": "D", "Column": 1, "Orientation": "H" }, { "Row": "E", "Column": 1, "Orientation": "V" } ] } else: # If we are in the ship hunting round move = { "Row": "B", "Column": 4 } return move

Here is an example of a function that takes into account land to ensure that the move is valid:

def shipPlacement(gameState): move = [] # Initialise our move list # Start in the top left corner start = (0,0) row = 0 column = 0 for i in gameState["Ships"]: # For every ship we need to place space = 0 start = (row,column) # Start trying to find room at the current row and column while space < i: # Until we find enough room to fit the ship keep on looking if column >= len(gameState["MyBoard"][0]): # If we have reached the end of a row without finding enough room row += 1 # Move to the next row down column = 0 # Start at the beginning of the row space = 0 # Reset the amount of space we have found on the row since the last obstacle start = (row, column) # Initialise the potential start location for our ship if gameState["MyBoard"][row][column] == "": # If the current cell is empty space += 1 # Increment the amount of space we have found on the row since the last obstacle column += 1 # Move to the next column along else: # If there is something blocking a ship being placed space = 0 # Reset the amount of space we have found on the row since the last obstacle column += 1 # Move to the next column along start = (row, column) # Reset the potential start location for our ship # If we have found enough room to place our ship shipPlace = BATHelperFunctions.translateMove(start[0], start[1]) # Call the translateMove hepler function with our coordinates to convert it to a valid move format shipPlace["Orientation"] = "H" # Set the orientation we wish to place our ship as horizontal move.append(shipPlace) # Add this ship placement to our move list # Once we have added all of our ships return {"Placement": move} # Return the valid placement move

## Case Study

Let’s say we would like to implement the following strategy:

- In the first round we will just randomly place our ships.
- In the second round if we haven't hit any ships we will randomly guess a location in the sea.
- If we have hit a ship we will randomly choose one of the adjacent tiles.
- If we are playing the particular bot “Barney01”, we will guess around the edges of the board.

We will make use of the built in helper functions from the battleships_helper_function module.

There is a helper function called deployRandomly() that we can use to randomly place our ships.

There is also a helper function called chooseRandomValidTarget() that we can use to randomly guess a location in the sea.

You can read more about the helper functions in the Helper Functions section of our Quick Reference Guide

The code looks like the following:

import BATHelperFunctions # Allows us to use the helper functions from random import choice # So we can randomly choose an element from a list from random import randint # So we can randomly pick a number def calculateMove(gamestate): if gamestate["Round"] == 0: # If we are in the ship placement round move = BATHelperFunctions.deployRandomly(gamestate) # Randomly place your ships else: # If we are in the ship hunting round for i in range(len(gamestate["OppBoard"])): for j in range(len(gamestate["OppBoard"][0])): # Look through every cell on the board if gamestate["OppBoard"][i][j] == "H": # If it has been hit if i > 0 and gamestate["OppBoard"][i-1][j] == "": # Try and guess above return {"Row": chr(i + 64), "Column": j + 1} elif i < len(gamestate["OppBoard"])-1 and gamestate["OppBoard"][i+1][j] == "": # Otherwise try and guess below return {"Row": chr(i + 66), "Column": j + 1} elif j > 0 and gamestate["OppBoard"][i][j-1] == "": # Otherwise try and guess left return {"Row": chr(i + 65), "Column": j} elif j < len(gamestate["OppBoard"][0])-1 and gamestate["OppBoard"][i][j+1] == "": # Otherwise try and guess right return {"Row": chr(i + 65), "Column": j + 2} # If there are no hit ships if gamestate["OpponentId"] == "Barney01": # If we are playing against Barney01 move = guess_edges(gamestate) # Call our guess_edges function to choose a target on the edge of the board if available else: move = BATHelperFunctions.chooseRandomValidTarget(gamestate) # Randomly fire at valid sea targets return move def guess_edges(gamestate): count = 0 while count < 50: # While we haven't given up trying to find a valid edge (row, column) = choice( # Randomly choose one of the following: [ (0, randint(0, len(gamestate["OppBoard"][0])-1)), # A random cell along the top edge (len(gamestate["OppBoard"])-1, randint(0, len(gamestate["OppBoard"][0])-1)), # A random cell along the bottom edge (randint(0, len(gamestate["OppBoard"])-1), 0), # A random cell along the left edge (randint(0, len(gamestate["OppBoard"])-1), len(gamestate["OppBoard"][0])-1) # A random cell along the right edge ] ) if gamestate["OppBoard"][row][column] == "": # If the move is valid return {"Row": chr(row + 65), "Column": column + 1} # Set values to a valid move else: # Otherwise increase number of attempts by one count += 1 # If we have given up trying to find a valid edge return BATHelperFunctions.chooseRandomValidTarget(gamestate) # Randomly fire at valid sea targets

This code is inefficient, see if you can improve its performance and its strategy!

## Advanced

Also you may like to edit the method play_game() from the battleships_layout.py module. The method is called during both player's turns. To maximise your efficiency, you may like to do some processing here while your opponent calculates their move. In particular you may want to add code to the following section:

while True: if self.game_cancelled: break if game_state['IsMover']: move = battleships_move.calculateMove(game_state) move_results = self.make_move(move) if move_results['Result'] != 'SUCCESS': break game_state = move_results['GameState'] else: # ---- Code here will be called on your opponent's turn ---- # ---------------------------------------------------------- poll_results = self.poll_for_game_state() if poll_results['Result'] != 'SUCCESS': break game_state = poll_results['GameState'] if game_state['GameStatus'] != 'RUNNING': break