Getting Started

Let's get you up and running!

Welcome to the exciting world of the CeneBotBattle! In this tutorial, we will embark on a playful journey where you will learn the art of bot creation and get your system up and running. Get ready to unleash your coding prowess and dive into the thrilling realm of competition. So, buckle up and let's embark on this exhilarating adventure together!

The Boring Stuff

To be able to develop your very own bot that plays Pyro Party, you'll need to prepare your system with the necessary dependencies. We recommend you use a recent Ubuntu distribution as your OS with Python 3.8 or later. Other setups should probably work too, but you might have some more trouble setting everything up.

Start off by downloading the game code of Pyro Party. You'll need this to run Pyro Party locally and test out your bots before submitting them. You can download the most recent version by cloning the Git repository from UGent GitHub using the button below. This way, when we make updates to the game or release bugfixes, you can simply do a git pull to update your local version.

  Clone Git Repo

After unzipping or cloning the repo to a convenient location, it's time to install the required dependencies for Pyro Party. Open a terminal, move into the project directory and execute this command:

pip install -r requirements.txt
  You might need to install the pip package manager first using sudo apt update and sudo apt install python3-pip.

Now that you've got everything installed, let's check if you can actually run the game. In the same terminal, now run:

python3 main.py

If you see a window with some colorful circular thingies moving around on the screen, you're all set up! Move on to the next section to learn how you can write your own bot.

If you get an error or don't see any window, don't worry. Send us a message on our Discord server or contact us via email and we'll help you out!

Getting Comfortable

The code that you've just downloaded may seem quite complicated and intimidating. Don't worry, you don't need to understand it to be able to develop your bots. That said, some background knowledge can help you feel more at ease. So let's dive in for a quick tour!

The root project folder consists of some subfolders to give it all a bit more structure. Let's take a look:

  • bots/: this is the directory where your bots are stored. It's like a warehouse for all your creations. Every bot you create should be stored in a subfolder with the Python file for your bot named bot.py and a copy of the client.py file (more about that later). We've already provided some sample bots for you so you can quickly get started testing your own bot!
  • client/: this is where you'll find the client.py file with the code to connect to the game server and thus interface with your bot. You'll need to copy this client.py file and put it in the bots/ directory such that you can breathe life into your bot. But again, more about that in a moment :)
  • game_engine/: here resides all the code that has to do with the rules of Pyro Party. It is used internally to play Pyro Party offline, so you probably don't need to sneak around in here.
  • game_server/: a bit like the previous directory, but then for all code that has to do with connection stuff.
  • config.json: the main configuration file for your local setup of Pyro Party. Here you can define which bots exist and can play games against eachother. We go into more detail about this file on the README of the Git repo.
  • main.py: the entry point of Pyro Party. Execute this script using python3 main.py to run the game locally.

And that's about it actually. Not so daunting anymore, is it? And now that we've covered the boring stuff, let's actually get coding!

Hello, world!

During this super duper tutorial, we'll create a bot that runs towards the nearest tree on the game field. We'll cover the whole process step by step so you'll be able to follow along quite easily and get the hang of it pretty quickly. By the end, you'll be able to write your very own Pyro Party bot. So buckle up and come along for the ride!

To create a new bot, we'll need to create a new subdirectory under bots/. This will group the Python file of the bot and the client.py file to connect to the game server. Luckily, we don't have to do this manually. We've provided you with a little handy script to setup your bot from the configuration file config.json.

Edit the config.json file in the project root directory and add the following under the clients section:

"tutorial-bot": {
	"elo": 1000,
	"color_body": "green",
	"color_ring": "blue",
	"username": "tutorial-bot"
},
As you can probably guess, we chose the name tutorial-bot for our little baby bot.

  You might want to delete other bots under the clients section of the config.json file if you don't want to test those.

If you now run the following command, your bot and all required files will be configured automagically:

python3 update.py
As you can see in the bots/ directory, there will now be a new directory for your bot with some Python files inside it. The bot.py file is the main file for your bot. This is where you'll be writing your code.

If you take a look at the bot.py file inside the bots/tutorial-bot/ directory using your favorite editor, you'll see that there's already some code in it:

from client import Client

def main():
    # Connect to the game server
    client = Client()

    # Sleep forever
    while True:
        client.sleep(1)

if __name__ == "__main__":
    main()
This fundamental code is the minimum you need to let your bot connect to the game server. The Client class that we import from client.py will automatically create a connection to the game server and handle all the nitty gritty details. Later on, we'll need the client object to send our moves to the game server. But for now, we just let the bot sleep forever using the infinite loop. So this bot will just connect to the game server and then do nothing. Pretty stupid, but it's a good starting point.

Alright, we've created the structure and code for our bot. Now it's time to actually run the game and see the bot battle (well, sleep...) for the first time in its life. Run this to start the game:

python3 main.py
If all is right, you'll see a green-blue dot sitting in one of the corners. Congrats, you're now the happy parent of your very own baby bot!

If you get an error or don't see your creation, don't worry. Send us a message on our Discord server or contact us via email and we'll help you out!

One Small Step for Bot, One Giant Leap for Botkind

Next up, time to get your baby bot making its first steps! Go back to the bot.py file and change it to:

from client import Client

def main():
    # Connect to the game server
    client = Client()

    # Move to the right
    client.move(right = True)

    # Sleep forever
    while True:
        client.sleep(1)

if __name__ == "__main__":
    main()
The one line we added will make the bot move to the right. It's as if the bot presses the right arrow on its virtual keyboard and holds it down. After sending the command to move right, we'll let our bot go to sleep again. But remember, since we don't call the client.move() anymore, the game server still assumes that the bot is pressing the right arrow.

  Any call to client.move() is latching. The game server will keep moving your bot in the direction that you last sent. To stop moving the bot, call client.move() without any arguments.
  The client.move() is equivalent to two seperate calls: client.set_movement(right = True) and client.send(). This first function just stores the movement internally, while the second actually sends it to the game server. You might find this more useful in more advanced bots.

As you can see, moving around and controlling your character is actually quite easy. The arguments for the client.move() are right, left, up, down and bomb (but more about that last one in the next chapter...). You can even set more of them at the same time! So when your bot gets frustrated, it can rage quit by smashing its virtual keyboard and pressing all keys at once :)

Run the code using python3 main.py to check if your bot actually sets its first steps. Be proud and shed a little tear to your little bot's accomplishment before moving on to the next section: giving your bot some eyes.

Let There Be Light

Alright, your bot crawls around but doesn't actually know where it's going. Let's change that, shall we? Go back to the bot.py file and add some code so it looks like this:

from client import Client

def main():
    # Connect to the game server
    client = Client()

    # Wait until the game starts
    client.recv()

    # Move to the right
    client.move(right = True)

    # Periodically retrieve and print out game field
    for i in range(10):
        client.recv()
        print(client.field)
        print(client.print_field())  # Fancy printing
        client.sleep(1)

    # Sleep forever
    while True:
        client.sleep(1)

if __name__ == "__main__":
    main()
Notice that we've introduced a new function: client.recv(). This function will retrieve information from the game server and update the local game field in client.field and player information in client.players. You should call this function before accessing those variables so you're getting a fresh reading.

If you run the code, you'll see that nothing gets printed in the terminal. This is because the logs of every bot are written to a seperate file: bot.log under the bots/tutorial-bot/ directory in this case. If you open that file, you'll see that the game field gets printed as follows:

UUUUUUUUUUUUU
UEEETETEEEEEU
UEUTUTUEUEUEU
UTEEEEEEEEEEU
UEUEUEUEUEUTU
UEEETEEETEEEU
UEUEUEUTUEUTU
UEEETEEEEETTU
UEUTUEUTUEUEU
UEETEEETEETEU
UEUEUTUTUEUEU
UEEETETEEEEEU
UUUUUUUUUUUUU
To make sense of what it actually means, take a look at this. The field is a 2D python list where the tiles can be accessed with client.field[y][x], with x the horizontal coordinate and y the vertical. Each tile has a type that is represented by a string character:

  • E: Empty tile, this tile can be walked on
  • U: Wall that cannot be destroyed
  • T: Tree, a tree is a removable object
  • B: Bomb, a bomb will explode after 2 seconds
  • X: Explosion, a deadly pool of fire that will burn the skin of your bones, this lasts for 2 seconds

We also want to put a time limit on the game. That's why after a minute of gameplay, lava will start flowing from the corners:

  • L: Lava tile, very deadly

In the near future, we'll also add powerups to the gameplay. These are the letters that will be used:

  • A: Bomb powerup, this will increase the range of the bomb that a player places by one tile
  • S: Speed powerup, this will increase the player's speed

And because the range of a bomb will be dependant upon the powerups, we'll have different types of bombs. A few more letters are introduced to keep track of how powerful the bomb is:

  • B: Bomb that has a range of 2 tiles
  • C: Bomb that has a range of 3 tiles
  • D: Bomb that has a range of 4 tiles
  • F: Bomb that has a range of 5 tiles
  • H: Bomb that has the max range of 6 tiles
  Note that both the bomb and the explosion have a timeout timer, you have to keep track of this timer yourself!
  Now also try to print out client.players and make sense of what it means. Hint hint: take a look at the Player class in client.py! You should be able to get your bot's position from there somehow.
Let's Blow Up A Tree

As the title of this chapter says, we're finally gonna blow stuff up woohoo! Our battleplan is:

  1. Find a tree
  2. Move to the tree
  3. Blow up the tree

If that doesn't sound like a solid plan, what is? Combining everything we've learned so far, let's write this code for our bot:

from client import Client

def main():
    # start up client
    client = Client()
    print(f"{client.username} ready")

    # wait for the game to start
    client.recv()

    # analyse field, search for a tree
    # it is advised to use client.username rather than hardcoding this
    print(client.players)
    print(client.print_field())
    x, y = client.players[client.username].pos
    x, y = round(x), round(y)
    if x == 1:
        dx = +1
    else:
        dx = -1

    if y == 1:
        dy = +1
    else:
        dy = -1

    looking = True
    # look in every column
    for i in range(11):
        # look in every row < column
        for j in range(i + 1):
            # Tree is represented by T
            if client.field[y + dy*(i-j)][x + dx*j] == "T":
                # we have a tree, yay
                tree_x, tree_y = x + dx*j, y + dy*(i-j)
                looking = False

            if not looking:
                break

        if not looking:
            break

    if looking:
        raise ValueError("No Trees in field")

    # we have a tree, yes
    print(f"Tree: {tree_x, tree_y}")

    # now lets find an empty tile next to the tree
    # note that if both x and y are even, the tile is a unmovable object
    if tree_x%2:
        valid_x, valid_y = tree_x, tree_y - dy
        if client.field[valid_y][valid_x] != "E":
            valid_x -= dx
            valid_y += dy
            if client.field[valid_y][valid_x] != "E":
                raise ValueError("Cannot find a valid tile next to the tree")
    else:
        valid_x, valid_y = tree_x - dx, tree_y
        if client.field[valid_y][valid_x] != "E":
            valid_x += dx
            valid_y -= dy
            if client.field[valid_y][valid_x] != "E":
                raise ValueError("Cannot find a valid tile next to the tree")

    print(f"Valid spot: {valid_x, valid_y}")
    # now lets move
    if valid_x%2 and x != valid_x:
        # we can move trough the entire row of the x coord, so go there first
        if dx > 0:
            client.move(right = True)
            print("moving right")
        else:
            client.move(left = True)
            print("moving left")

        while not abs(valid_x - client.players[client.username].x) <= 0.03:
            client.recv()

        # stop moving
        client.move()

    client.sleep(0.2)

    if y != valid_y:
        if dy > 0:
            client.move(down = True)
            print("moving down")
        else:
            client.move(up = True)
            print("moving up")

        while not abs(valid_y - client.players[client.username].y) <= 0.03:
            client.recv()

        # stop moving
        client.move()

    client.sleep(0.2)

    # if you are not yet on the correct location, move again
    if not valid_x%2:
        if dx > 0:
            client.move(right = True)
            print("moving right 2")
        else:
            client.move(left = True)
            print("moving left 2")

        while not abs(valid_x - client.players[client.username].x) <= 0.03:
            client.recv()

        # stop moving
        client.move()

    print("Client is at location")
    x, y = client.players[client.username].pos
    assert valid_x == round(client.players[client.username].x)
    assert valid_y == round(client.players[client.username].y)

    # Place a bomb
    client.move(bomb = True)

    # stay active
    while True:
        client.sleep(1)

if __name__ == "__main__":
    main()
Alright, our bot is beginning to look more and more competent to tackle the challenge of defeating its opponents. It should be straightforward to extend these bots with your own strategy. So without any further ado, go write some code!!

  When you're happy with your bot, you can upload your bot.py file on this page. More technical information can be found in the README of the GitHub repo. Happy coding!