-
Notifications
You must be signed in to change notification settings - Fork 69
Bot Development Tutorial
Hello and welcome to the advanced tutorial. If you haven't already, we recommend that you start with the basic tutorial.
We will be designing a bot that plays the standard deck (minions, archers, arrows, giant, minipekka, fireball, knight and musketeer). This is the deck that all players can play after creating a new account.
Our plan is as follows:
- Design a score function for each of our units
- At each time step
- Score all possible actions
- Play the action with the highest score
To do this we need to create 2 files:
- A subclass of
Bot(clashroyalebuildabot/bot/standard/standard_bot.py) - A subclass of
Action(clashroyalebuildabot/bot/standard/standard_action.py)
1.) Create subclasses of Bot (clashroyalebuildabot/bot/bot.py) and Action (clashroyalebuildabot/bot/action.py).
class StandardBot(Bot):
passclass StandardAction(Action):
pass2.) In the StandardAction class, design a score function for each unit. I.e.
def _calculate_minipekka_score(self, state):
"""
Place minipekka on the bridge as high up as possible
Try to target the lowest hp tower
"""
left_hp, right_hp = [state['numbers'][f'{direction}_enemy_princess_hp']['number']
for direction in ['left', 'right']]
score = [0]
if self.tile_x == 3:
score = [1, self.tile_y, left_hp != -1, left_hp <= right_hp]
elif self.tile_x == 14:
score = [1, self.tile_y, right_hp != -1, right_hp <= left_hp]
return scoreIt is useful to use the state to score an action. In the example above, we use the HP of the enemy towers to decide which side to put minipekka on. You might want to print the state a few times to learn what metadata is available, and how reliable it is.
We use the tile map to work out that 3 and 14 are the x-coordinates of the bridges.

See clashroyalebuildabot/bot/standard/standard_action.py for how I choose to score actions for each unit.
3.) In the StandardAction class, apply the score function to each action.
def calculate_score(self, state):
name_to_score = {'knight': self._calculate_knight_score,
'minions': self._calculate_minions_score,
'fireball': self._calculate_fireball_score,
'giant': self._calculate_giant_score,
'minipekka': self._calculate_minipekka_score,
'musketeer': self._calculate_musketeer_score,
'arrows': self._calculate_arrows_score,
'archers': self._calculate_archers_score
}
score_function = name_to_score[self.name]
score = score_function(state)
self.score = score
return score4.) In the StandardBot class, play the action with the highest score at each timestep.
def run(self):
while True:
# Set the state of the game
self.set_state()
# Obtain a list of playable actions
actions = self.get_actions()
if actions:
# Get the best action
action = max(actions, key=lambda x: x.calculate_score(self.state))
# Play the best action
self.play_action(action)This is almost identical to RandomBot (clashroyalebuildabot/bot/random/random_bot.py),
except that RandomBot uses action = random.choice(actions)
5.) Load the emulator, start a game, and watch your bot play!
from clashroyalebuildabot.bot.standard.standard_bot import StandardBot
card_names = ['minions', 'archers', 'arrows', 'giant',
'minipekka', 'fireball', 'knight', 'musketeer']
bot = StandardBot(card_names, debug=True)
bot.run()