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!
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.
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
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!
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!
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.
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!
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.
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.
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.
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:
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:
In the near future, we'll also add powerups to the gameplay. These are the letters that will be used:
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:
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.
As the title of this chapter says, we're finally gonna blow stuff up woohoo! Our battleplan is:
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!!
bot.py
file on this page.
More technical information can be found in the README of the GitHub repo.
Happy coding!