Contents
All blogs / How to build an AI Bomberman bot (Part 2/3)
July 19, 2021 • Joy Zhang • Tutorial • 5 minutes
How to build an AI Bomberman bot (Part 1)
How to build an AI Bomberman bot (Part 2)
How to build an AI Bomberman bot (Part 3)
So far we've covered:
In part 2, we'll show you how to build a simple agent called the 'Wanderer Agent' that implements some scripted logic to explore and navigate the world. More specifically, it:
If you're more interested in the topic of reinforcement learning, check out my other introductory tutorial to Reinforcement Learning With OpenAI Gym’s ‘Taxi’.
In this section, we'll walk through creating 3 helper methods that will be useful for our agent:
get_surrounding_tiles()
: Returns a list of our surrounding tilesget_empty_tiles()
: Returns tiles that are valid for our agent to move intomove_to_tile()
: Returns the corresponding action the agent should take to move to a tileYou should be familiar with the game_state
and player_state
objects from part 1 of the tutorial.
get_surrounding_tiles()
Our first helper method get_surrounding_tiles()
will return us a list of tiles surrounding our agent's current location as an (x,y) tuple of the game map.
We'll take advantage of the coordinate-representation of the map:
Below is the skeleton code for our get_surrounding_tiles()
method**.** We've left you some gaps to fill out. If you get stuck, check out the solution. (💡 Hint: check the game state documentation for useful methods).
# given our current location as an (x,y) tuple, return the surrounding tiles as a list
# (i.e. [(x1,y1), (x2,y2),...])
def get_surrounding_tiles(self, location):
# location[0] = x-index; location[1] = y-index
tile_north = (location[0], location[1]+1)
tile_south = None ################ FILL THIS ###################
tile_west = None ################ FILL THIS ###################
tile_east = (location[0]+1, location[1])
surrounding_tiles = [tile_north, tile_south, tile_west, tile_east]
for tile in surrounding_tiles:
# check if the tile is within the boundaries of the game
if None: ################ CHANGE 'NONE' ###################
# remove invalid tiles from our list
surrounding_tiles.remove(tile)
return surrounding_tiles
Next, add your get_surrounding_tiles()
method to your Agent
class in my_agent.py
.
class Agent:
def __init__(self):
pass
def next_move(self, game_state, player_state):
'''
This method is called each time your Agent is required to choose an action
'''
pass
########################
### HELPERS ###
########################
def get_surrounding_tiles(self, location):
'''
Your code here
'''
return surrounding_tiles
get_empty_tiles()
In order for our agent to move effectively, it will also need to know which of its surrounding tiles are actually empty (i.e. not containing a block or other player). Here's a get_empty_tiles
method with some blanks for you to fill out:
# given a list of tiles, return only those that are empty/free
def get_empty_tiles(self, tiles):
empty_tiles = []
for tile in tiles:
if None: ################ CHANGE 'NONE' ###################
# add empty tiles to list
empty_tiles.append(tile)
return empty_tiles
move_to_tile()
Given an adjacent surrounding tile and our current location, move_to_tile()
will return the action (i.e. u
, d
, l
, r
) that will get us there. E.g. if the tile we want to move to is directly north of us, this method will return u
.
# given an adjacent tile location, move us there
def move_to_tile(self, location, tile):
# see where the tile is relative to our current location
diff = tuple(x-y for x, y in zip(tile, self.location))
# return the action that moves in the direction of the tile
if diff == (0,1):
action = 'u'
elif diff == (0,-1):
action = None ################ FILL THIS ###################
elif diff == (1,0):
action = None ################ FILL THIS ###################
elif diff == (-1,0):
action = 'l'
else:
action = ''
return action
With our helper methods in place, we'll be able to implement some simple logic to control our agent to navigate the game world.
Here's some sample skeleton code to help you piece together your agent. Here's also a link to our own version of Wanderer Agent.
import random
class Agent:
def __init__(self):
'''
Place any initialization code for your agent here (if any)
'''
pass
def next_move(self, game_state, player_state):
'''
This method is called each time your Agent is required to choose an action
'''
########################
### VARIABLES ###
########################
# game map is represented in the form (x,y)
self.cols = game_state.size[0]
self.rows = None ################ FILL THIS ###################
# useful for later
self.game_state = game_state
self.location = player_state.location
########################
### AGENT ###
########################
# get our surrounding tiles
surrounding_tiles = self.get_surrounding_tiles(self.location)
# get list of empty tiles around us
empty_tiles = None ################ FILL THIS ###################
if empty_tiles:
# choose an empty tile to walk to
random_tile = random.choice(empty_tiles)
action = None ################ FILL THIS ###################
else:
# we're trapped
action = ''
return action
########################
### HELPERS ###
########################
# given our current location as an (x,y) tuple, return the surrounding tiles as a list
# (i.e. [(x1,y1), (x2,y2),...])
def get_surrounding_tiles(self, location):
# location[0] = x-index; location[1] = y-index
tile_north = (location[0], location[1]+1)
tile_south = None ################ FILL THIS ###################
tile_west = None ################ FILL THIS ###################
tile_east = (location[0]+1, location[1])
surrounding_tiles = [tile_north, tile_south, tile_west, tile_east]
for tile in surrounding_tiles:
# check if the tile is within the boundaries of the game
if None: ################ CHANGE 'NONE' ###################
# remove invalid tiles from our list
surrounding_tiles.remove(tile)
return surrounding_tiles
# given a list of tiles, return only those that are empty/free
def get_empty_tiles(self, tiles):
empty_tiles = []
for tile in tiles:
if None: ################ CHANGE 'NONE' ###################
# add empty tiles to list
empty_tiles.append(tile)
return empty_tiles
# given an adjacent tile location, move us there
def move_to_tile(self, location, tile):
# see where the tile is relative to our current location
diff = tuple(x-y for x, y in zip(tile, self.location))
# return the action that moves in the direction of the tile
if diff == (0,1):
action = 'u'
elif diff == (0,-1):
action = None ################ FILL THIS ###################
elif diff == (1,0):
action = None ################ FILL THIS ###################
elif diff == (-1,0):
action = 'l'
else:
action = ''
return action
Save your agent (my_agent.py
) then run the following command in your terminal to watch your new bot go up against itself:
coderone-dungeon --watch my_agent my_agent
To win at Dungeons and Data Structures, your agent will need to do more than roam around the map. It will need to know how to place bombs strategically in order to blow up crates for points or take down your opponent.
Now that you've got a better grasp of the environment, have a go at implementing some logic around bomb placements.
If you're interested, check out our implementation of a very simple 'Flee Agent'. It uses the Manhattan Distance formula to decide whether to focus on running away from nearby bombs or placing more of them.
In this part, we went from a random agent that selects its moves at random, to one that can interpret its surrounding environment and explore it.
In the next part of the series, we'll implement a pathfinding algorithm to help it navigate to useful objects around the map: