This repository has been moved to gitlab.com/paul-nechifor/battlebot-client-py.
Old readme:
This is a Python client for AI battles! This is a boilerplate repository for quickly getting a bot set up that can connect and authenticate with the server. How the bot implements and plays the game is up to you.
The server is currently being hosted on Heroku: https://blunderdome-server.herokuapp.com/
Server code here: https://github.com/dprgarner/battlebot-server
Before playing a game, each bot needs to register with the server. This consists of making a POST request to the endpoint /games/noughtsandcrosses
, consisting of the name of the bot bot_id
and optionally your name owner
. The server will return a JSON object consisting of the bot_id
, the name of the game, and pass_hash
, which is used to authenticate the bot when connecting via websocket.
The provided helper file 'register.py' will make this request and save the JSON file to 'auth.json'.
> $ python register.py --hostname=blunderdome-server.herokuapp.com --game=noughtsandcrosses --owner=David MyAwesomeBot
('noughtsandcrosses', 'blunderdome-server.herokuapp.com')
Bot MyAwesomeBot registered successfully
Bot data saved to /data/home/london/dgarner/idiotbot-client/auth.json
> $ cat auth.json
{
"game": "noughtsandcrosses",
"pass_hash": "8ad86f2934f347abf60ee7c192c96fbc8383de273c4c092de7ae97151b84d934",
"hostname": "blunderdome-server.herokuapp.com",
"bot_id": "MyAwesomeBot"
}
When a websocket connection is made, after the initial WebSocket handshake, the server will send a JSON object with the key salt
. The client should reply with a JSON object containing bot_id
, the name of the game game
, and login_hash
. The login_hash should be generated by appending the pass_hash to the salt, and encryping this with sha256 and returning it as a hex digest. The server will then return JSON { "authentication": "ok" }
if successful, or disconnect if not. If the authentication is successful, the server will pair the bot off with any other connected bot to a start a game, or will maintain the connection if no other connected bots are available.
The provided helper file client.py
will handle the connection to the server, using the saved auth.json
file, and wait for a game to start.
> $ python client.py
Bot connected - waiting to start game...
Traceback (most recent call last):
File "client.py", line 79, in <module>
Client()
File "client.py", line 15, in __init__
self.play_game()
File "client.py", line 75, in play_game
raise NotImplementedError('Write your bot\'s logic here.')
NotImplementedError: Write your bot's logic here.
The provided client.py file should be modified or extended with your custom logic for running the game. All communication between the client and server is via JSON, so the client provides the two helper methods recv
, which pauses until the server sends some data and then decodes it to an object, and send
, which encodes a JSON-serialisable object and sends the data to the server. If the server closes the connection, the method recv
will return None.
To play in a contest, add the name of the contest as an argument, e.g.
> $ python client.py --contest=round-robin
The client will add an extra key to the login hash and will repeatedly attempt to reconnect after each game is played. The server will match up bots which are playing in the contest with other bots in the contest, provided that they have played each other less than five times in the contest. The API endpoint /games/<game_name>/contest/<contest_name>
gives a summary of the games played in the contest and the rankings of the bots. A bot scores three points for a win, one point for a draw, and no points for a loss. Ties are broken on most wins, then fewest losses.
As soon as a game starts, the server will send an update of the following form to the client bot:
{
"state": {
"players": [
"IdiotBot2",
"IdiotBot"
],
"complete": false,
"board": [
[
"",
"",
""
],
[
"",
"",
""
],
[
"",
"",
""
]
],
"nextPlayer": "IdiotBot2",
"marks": {
"X": "IdiotBot2",
"O": "IdiotBot"
}
}
}
The client sends turns to the server, and the server validates the turn and responds with the new state and the played turn. A turn dispatched from the client to the server should look like this:
{
"mark": "X",
"space": [2, 2]
}
A space is specified as a two-entry array, with each entry an integer from 0 to 2, specifying the row and column to place the mark in respectively. The mark should be "O" or "X", depending on whether the client is playing "X"es or "O"s.
The following is an example of a server response, at the end of the game:
{
"turn": {
"player": "IdiotBot2",
"valid": true,
"space": [1, 2],
"time": 1498036964996,
"mark": "X"
},
"state": {
"complete": true,
"players": [
"IdiotBot2",
"IdiotBot"
],
"reason": "complete",
"board": [
[
"O",
"O",
"X"
],
[
"X",
"O",
"X"
],
[
"O",
"X",
"X"
]
],
"marks": {
"X": "IdiotBot2",
"O": "IdiotBot"
},
"nextPlayer": "IdiotBot",
"victor": "IdiotBot2"
}
}
If a bot disconnects the websocket during the course of the game, then the bot is disqualified. If the bot makes three invalid moves, or takes longer than three seconds to play a move, then the bot is disqualified.
If you want to test your bot out, I'd suggest registering a second bot and having them play each other. I've also written a bot which randomly plays valid noughts and crosses turns, but doesn't really have any strategy - I can spin this up to repeatedly connect to the server when someone wants to try their own bot out.
Happy bot-writing!