An environment to host friendly bot writing competitions.
I wrote this in a way to allow for sandboxing later on. When a bot is run, it is spawned in a new process which communicates with the parent via stdin/stdout. Currently, the bot is called using python3
but this can be changed to an interpreter that allows for sandboxing such as PyPy or the spawning of a container.
To create a new bot, create a .py file to contain your bot's code. Next, create a class that inherits colusseum.games.bot.Bot
. In the __init__
method, you must call super().__init__(<GameTracker type>)
with the type of the GameTracker associated with your game. When you instantiate your bot, your bot will begin running and it will not return from the constructor. Any code placed below the constructor call will not be run.
The bot will have a self.game
GameTracker property which keeps track of the current game state as it updates.
Implement the take_turn(self)
and new_game(self)
methods. The former will be called when it is your bot's turn to play. The latter will be called when a new game is starting. When a new game is started the bot's state is not necessarily reset. You can take advantage of this to implement a 'memory' where your bot learns about an opponent over successive games.
To submit your bot's action at the end of take_turn
, wrap your action in self.game.make_move(*args, **kwargs)
. The specifics of the make_move
call will depend on the game you are playing. In the case of GuessThatNumber, make_move
takes your bot's guess as the one parameter. The make_move
method will return the packaged move which can be returned from take_turn
.
Since the bots will be run either sandboxed or in a container (or both), io is extremely limited. As such, only stdin, stdout, and stderr should be assumed to be available. However, stdin and stdout are used to communicate with the parent process.
In lieu of print
and input
, the functions colosseum.games.bot.log
and colosseum.games.bot.get_input
are provided. Both take the same parameters as print
and input
except for log
will overwrite the file
argument to the parent process' stdout. Both functions will use the parent process' stdin and stdout. The parent process will service the log
or get_input
request only when they are expecting a message. This could be during the bot's turn.
Every game has a GameTracker
and a GameHoster
. Like the names suggest, the GameTracker
tracks the progress of the game and the GameHoster
hosts the game and manages the bots. To define a new game, create two classes. One class inherits from GameTracker
and the other from GameHoster
. The GameTracker
must implement make_move
, update
, and is_done
.
As was mentioned in a previous section, make_move
will take the move generated by the bot and package it for transport to the parent process. The packaged move must be a dict
which will be converted to json. The update
method is called whenever the state of the GameTracker
should be updated. This should not be called by make_move
. Finally, is_done
returns True
if the game is over, False
otherwise.
The only method that must be implemented in the GameHoster
is play(*args, **kwargs)
. This method is called in start_game
to start a new game. The method should:
- Create a new
GameTracker
to track the game as well as perform any additional setup required by the game host. - Call
p.new_game(...)
with any needed parameters for everyp
inself._players
. - Start issuing turns to the players whichever order is appropriate for the game.
- Call
p.update
for everyp
inself._players
with the relevant parameters whenever the game state changes. - Return the
GameTracker
at the end of the game.