Here's all the code you need to get started with making a bot for Mechmania in Python. Just do these steps:
- Pre-Setup -- install Docker, Node, and the
mm
command line tools - Setup -- Clone this repository and start running your bot!
-
First, install Node. To do this, go here and download the Windows Installer.
-
Next, install Docker Toolbox for Windows.
- Go to this site and click the button that says "Get Docker Toolbox for Windows".
- Open the installer and follow the instructions to install Docker
- If you don't already have Oracle VM VirtualBox, you will also go through a series of prompts to install VirtualBox.
- Once Docker is installed, open the "Docker Quickstart Terminal" application (It will take a few minutes to get set up the first time you start it. This process may involve more Windows prompts for permissions.)
-
Within Docker, run
npm install -g mechmania
. This gets themm
command line tools, which are used to test and submit bots for the tournament.
-
First, install Node. To do this, go here and download the macOS Installer.
-
Next, install Docker for Mac.
- You can find the installer here.
- Open the installer and follow the instructions to install Docker
-
Run
npm install -g mechmania
. This gets themm
command line tools, which are used to test and submit bots for the tournament.
- Clone this repo (or fork it or download it somewhere as a ZIP)
- Modify the script at
MyBot.py
.- Write your code after the
YOUR CODE BELOW
comment, making sure to callgame.submit_decision()
once per turn. - You may also add other files or dependencies, but if you do, make sure to update the
.dockerignore
andDockerfile
s accordingly. If you have any questions about this, we're here to help!
- Write your code after the
- Run
mm play .
- This will build the bot in the given directory (
.
) and then starts a game in which your bot fights against itself.
- This will build the bot in the given directory (
- To run two different bots against each other, run
mm play bot1_directory bot2_directory
.- You must have valid
.dockerignore
andDockerfile
files in each directory. We reccomend cloning this repository multiple times to achieve this.
- You must have valid
Use mm help
for more information!
Within the file Game_API.py
, there are a variety of helpful functions for writing your bot. You can use the game
object in the MyBot.py
script to run any of these functions (e.g. game.get_turn_num()
).
IMPORTANT: Do NOT use print()
for debugging, as this will make your bot fail when the game runs. Instead, use the game.log()
function in Game_API.py
.
Within Game_API
, three other classes are defined -- Player
, Monster
, and DeathEffects
. We recommend only accessing the fields in these classes, since changing them might cause the game
object to not properly represent the current state of the game.
player_num
:int
(either 1 or 2)name
:str
(either "Player1" or "Player2")stance
:str
-- string representation of the player's stance -- "Rock", "Paper", or "Scissors" (Note: on the first turn, each player's stance will be "Invalid Stance", since neither player has yet chosen a stance)health
:int
-- the player's health. If a player's health reaches 0, they die.speed
:int
-- the player's speed.movement_counter
:int
-- the movement counter will go down by 1 each turn. Once a player's movement counter is equal to their speed, they will move to their destination.location
:int
destination
:int
(Note: on the first turn, the player's destination will be -1, since the player hasn't yet set a destination)dead
:bool
(This should always beFalse
, since once a player dies, the game is over)rock
:int
-- the player's Rock attack statpaper
:int
-- the player's Paper attack statscissors
:int
-- the player's Scissors attack stat
name
:str
-- represents the class of this monster. All monsters with the same name should have the same base stats (health, attack, respawn_rate, death_effects, and attack)stance
:str
health
:int
respawn_rate
:int
-- number of turns for the monster to respawn after dyingrespawn_counter
:int
-- turns until this monster will respawn (Note: ifdead=False
, then you should ignore the value of this field.)location
:int
dead
:bool
death_effects
:DeathEffects
-- Gives information on the buffs given to the player when this monster dies on the same node as them.attack
:int
-- the amount of damage the monster deals per turnbase_health
:int
-- the health that the monster will have after respawning
Each field corresponds to the buff provided to a player's stat from defeating a monster:
rock
:int
paper
:int
scissors
:int
health
:int
speed
:int
(Note: For any function that returns Player, Monster, or DeathEffects structs, the data in the structs may change between one turn and another, so you should make sure to only use structs returned during the current turn)
log(strn)
Logs the string str
to stderr
for debugging.
get_duel_turn_num()
Returns the turn number when the duel will occur
get_adjacent_nodes(node)
Takes an int node
and returns a list of int
s representing the nodes adjacent to node
.
get_all_monsters()
Returns a list of all the monsters in the game.
get_self()
Returns a Player
object representing the player you are controlling.
get_opponent()
Returns a Player
object representing your opponent. (Note: the opponent's destination will always appear to be -1. This is by design -- neither player can see the other's destination)
submit_decision(destination, stance)
Takes an int
destination
and a string stance
and sends this decision to the game engine. You should call this method exactly once per turn.
shortest_paths(start, end)
Returns a list of all shortest paths between start
and end
nodes (passed in as int
s).
Each element of the returned list will be a list of integers, representing the steps required to follow the path to end
. These lists will include end
, but will not include start
unless start==end
.
So, as a hypothetical example (that doesn't necessarily match the game map), shortest_paths(1,5)
could return the following:
[[2, 3, 4, 5]
[2, 6, 7, 5]
[9, 8, 7, 5]]
In this example, there are 3 paths, each of length 4, that can get from node 1
to node 5
.
has_monster(node)
Returns a bool
indicating whether there is a monster at node node
.
get_monster(node)
Returns a Monster
struct for the monster located at node
. It is reccomended to check if there is a monster at node
first, using has_monster(node)
. If you call get_monster
for a node without a monster, an invalid monster will be returned.
nearest_monsters(node, search_type)
Returns a list of Monster
structs for all monsters nearest to node
(including on node
itself), only considering monsters according to the search_mode
parameter:
search_mode = 0
: Searches all monsterssearch_mode = 1
: Only searches for live monsterssearch_mode = 2
: Only searches for dead monsters
nearest_monsters_with_name(node, name, search_type)
Same as nearest_monsters
, but only considers monsters with name name
.
(Note: since, on our map, there is only one monster with any given name, the list returned by this method will contain a maximum of 1 monster)