-
Notifications
You must be signed in to change notification settings - Fork 29
Tutorial on writing a Bot API for Tank Royale
Hello, my name is Davide and I'm working my way through the creation of a Bot API library written in NIM. I'm writing this post to share my experience and give some insights. This is my current repository: Tank Royale Bot API in NIM
This document is intended to help you create a Bot API for Tank Royale, a game where you can create your own bots and make them fight against each other. My guidelines are based on my experience and I can't guarantee that they are the best way to do it, but I hope they can help anybody who wants to start this journey.
When writing a Bot API, you will need to address several common considerations:
- WebSocket: These are unavoidable as the RTR (Robocode Tank Royale) engine relies on them.
- Multi-threading/async: The library must use multi-threading or asynchronous programming to manage incoming messages from the server and execute custom bot code triggered by events.
- Overriding/overloading: Bot creators will need to override or overload methods to make the bot programmable.
- Environmental variables: To ensure bots created with your API can be booted by the original Booter, your bot must read from environment variables.
- Reading from disk: Bots must ingest at least one file from the disk, specifically the JSON file containing the bot's "profile."
- JSON: JSON is the format for all messages between the bot and the game server, so you need to program the bot to handle these.
- Efficient handling: The bot API engine must be highly efficient. In a standard game, the bot has 30ms or less each turn to send its intent. It needs to handle everything quickly to provide more "CPU space" for user calculations.
-
I recommend reviewing the basic schemas available in the Game schemas README. This will help you understand how the game and bots communicate to make the game function.
-
Find how to allow someone to import your API, initialize and start a bot.
-
Start with creating the connection to the server with WebSockets; this is the first thing you need to do to start receiving and sending messages to the server. As soon as you connect to a server, you will receive its handshake json message, if you do you have successfully connected to the server, answer it with your botHandshake and the programming fun will follow.
With these tasks completed, you should now have a solid understanding of the basics and a clear direction for further developing your API.
As you progress, you will eventually want to test your API. But how should you do it?
I found it useful to test my library by re-implementing the Sample Bots. Each bot will challenge you in different ways.
There are no automated tests available for your API/Bots. Depending on your chosen language, you will need to create all tests yourself. One approach is to create a test that starts a server, initializes a Controller to handle messages from the server, and launches two or more bots. The Controller can then command the server to start the game. The Controller will receive updates, or the bot can test its own values.
All these steps are possible without using the GUI
How you test your API/Bots is entirely up to you.
This is the state diagram I've created to show the logic I think is the correct one to implement the Bot API for Tank Royale.
It's a simple diagram that shows the main states of the bot and how they interact with each other.
The diagram does not show all the details of the implementation, but it gives a good idea of the logic behind the bot and how implement it is up to you.
stateDiagram-v2
state check_connection <<choice>>
[*] --> CONNECT
CONNECT --> check_connection: connection success?
check_connection --> [*]: no
state running_fork <<fork>>
state running_join <<join>>
check_connection --> running_fork: yes
running_fork --> MAIN
state MAIN {
state IO_fork <<fork>>
state IO_join <<join>>
CALL_FOR<br><i>DISCONNECTION</i> --> [*]
[*] --> IO_fork
IO_fork --> LISTEN_MESSAGES
IO_fork --> CHECK<br><i>OUT_QUEUE</i>
CHECK<br><i>OUT_QUEUE</i> --> SEND_MESSAGE: message
SEND_MESSAGE --> CHECK<br><i>OUT_QUEUE</i>
CHECK<br><i>OUT_QUEUE</i> --> IO_join: disconnected
LISTEN_MESSAGES --> IO_join: disconnected
LISTEN_MESSAGES --> HANDLE_MESSAGE<br><br>RUNNIG_and<br>nextTurn<br>handled_here: message
HANDLE_MESSAGE<br><br>RUNNIG_and<br>nextTurn<br>handled_here --> CHECK_TYPE
state check_type <<choice>>
CHECK_TYPE --> check_type: message
check_type --> ADD_MESSAGE_TO<br><i>EVENT_QUEUE</i>: type of<br><i>bot event</i>
check_type --> LISTEN_MESSAGES: else
ADD_MESSAGE_TO<br><i>EVENT_QUEUE</i> --> LISTEN_MESSAGES
IO_join --> CALL_FOR<br><i>DISCONNECTION</i>
}
running_fork --> BOT
state BOT {
[*] --> WAIT_FOR_START
WAIT_FOR_START --> RUN<br><i>custom_code</i> : @START
RUN<br><i>custom_code</i> --> REMOVE_OLD_EVENTS<br><i>EVENT_QUEUE</i>: GO<br>from RUN()
REMOVE_OLD_EVENTS<br><i>EVENT_QUEUE</i> --> SORT_BY_PRIORITY<br><i>EVENT_QUEUE</i>
SORT_BY_PRIORITY<br><i>EVENT_QUEUE</i> --> HANDLE_QUEUE<br><i>EVENT_QUEUE</i>
HANDLE_QUEUE<br><i>EVENT_QUEUE</i> --> STILL_VALID?:pop()
state check_validity <<choice>>
STILL_VALID? --> check_validity
check_validity --> HANDLE_QUEUE<br><i>EVENT_QUEUE</i>: no<br>drop event
check_validity --> RUN_BOT_EVENT_CODE<br><i>custom_code</i>: yes
RUN_BOT_EVENT_CODE<br><i>custom_code</i> --> HANDLE_QUEUE<br><i>EVENT_QUEUE</i>: no GO
RUN_BOT_EVENT_CODE<br><i>custom_code</i> --> REMOVE_OLD_EVENTS<br><i>EVENT_QUEUE</i>: GO<br>from custom event
HANDLE_QUEUE<br><i>EVENT_QUEUE</i> --> SEND_INTENT: @queue empty
SEND_INTENT --> WAIT_NEXT_TURN
WAIT_NEXT_TURN --> RUN<br><i>custom_code</i>: @nextTurn<br>if from<br>RUN
WAIT_NEXT_TURN --> AUTOMATIC_GO: @nextTurn<br>if from<br>AUTOMATIC_GO
RUN<br><i>custom_code</i> --> AUTOMATIC_GO : @exit from RUN<br><i>custom_code</i>
AUTOMATIC_GO --> REMOVE_OLD_EVENTS<br><i>EVENT_QUEUE</i>: GO<br>from AUTOMATIC_GO
AUTOMATIC_GO --> WAIT_FOR_START: @RUNNING = false
}
running_join --> [*]
MAIN --> running_join
BOT --> running_join : exit @DISCONNECTION
Diagram created with Mermaid