Skip to content

Tutorial on writing a Bot API for Tank Royale

Davide Cappellini edited this page Oct 6, 2024 · 9 revisions

Introduction

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.

Common Considerations for Writing a Bot API

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.

Suggestions on where to start

  1. 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.

  2. Find how to allow someone to import your API, initialize and start a bot.

  3. 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.

test 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.

Automated test

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.

State Diagram

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
Loading

Diagram created with Mermaid