Welcome to part 2 of the Halite tutorials. Up to this point, we've just simply used the pre-made bot that everyone has access to. In this part, let's look into making some slight tweaks to improve it. Right now, my rank on Halite with this bot is 1546. I suspect even a slight tweak here will grant us a massive improvement in our rank, let's find out.
To program some improvements to our AI, we need to first identify some logical issues. I am no Halite expert, but, I think the largest problem I notice is that we send all of our ships to the same not-currently-docked planet. The following image sums it up decently:
Let's assume we're the purple team above. All of our available ships are currently heading towards the same planet. We can mine the same planet with multiple ships, but, it's unlikely that this is a good idea to do at the beginning of the game.
Another big problem we're going to encounter here is that, clearly, the blue ship is closer, and is going to be able to dock before any of our ships. Once one team has docked a ship on a planet, that planet is "owned" by that team. As we're running towards a planet, we'd probably want to check to see if we're going to be beat to it, and if we should change course to a planet that we can definitely get to in time before the other team.
Make a copy of MyBot.py
. The *new* one we're going to work on will be MyBot.py
, and let's rename the other copy MyBot-v1.py
Within the MyBot.py
, let's make our first edit, renaming our bot:
game = hlt.Game("Settler V2")
Okay, let's begin trying to tackle these issues, first with not sending all of our ships to the same planets. In fact, let's see what might happen if we just make one change: send each ship to a unique planet.
How might we do this? Well, at least at a very basic level, we need to have some sort of log of where we're already heading. We need to begin saving a bit of a "state." Right now all of our ships buzz around blindly, not really communicating with each other. Now, let's at least share our plans across all ships. Above the while True:
, let's create a new variable called planned_planets
:
... logging.info("Starting my Settler bot!") planned_planets = [] while True: # TURN START # Update the map for the new turn and get the latest version game_map = game.update_map() ...
Now, we can use this list in the loop, after we ask the if ship.can_dock(planet):
else: ''' adding this:''' if planet in planned_planets: continue else: # If we can't dock, we move towards the closest empty point near this planet (by using closest_point_to) # with constant speed. Don't worry about pathfinding for now, as the command will do it for you. # We run this navigate command each turn until we arrive to get the latest move. # Here we move at half our maximum speed to better control the ships # In order to execute faster we also choose to ignore ship collision calculations during navigation. # This will mean that you have a higher probability of crashing into ships, but it also means you will # make move decisions much quicker. As your skill progresses and your moves turn more optimal you may # wish to turn that option off. navigate_command = ship.navigate( ship.closest_point_to(planet), game_map, speed=int(hlt.constants.MAX_SPEED/2), ignore_ships=False) # If the move is possible, add it to the command_queue (if there are too many obstacles on the way # or we are trapped (or we reached our destination!), navigate_command will return null; # don't fret though, we can run the command again the next turn) if navigate_command: command_queue.append(navigate_command) '''adding this''' planned_planets.append(planet)
My changes are noted in the triple quotes. Not PEP 8, but, in this highly-commented code, it'd be hard to see mine. Okay, quite a simple change. We never actually find out if the planned_planet ever actually gets docked. Because of this, ships *might* change their mind too.
Just in case, here's the full code up to this point:
import hlt # Then let's import the logging module so we can print out information import logging # GAME START # Here we define the bot's name as Settler and initialize the game, including communication with the Halite engine. game = hlt.Game("Settler V2") # Then we print our start message to the logs logging.info("Starting my Settler bot!") # Begin tracking planets we already have ships heading towards planned_planets = [] while True: # TURN START # Update the map for the new turn and get the latest version game_map = game.update_map() # Here we define the set of commands to be sent to the Halite engine at the end of the turn command_queue = [] # For every ship that I control for ship in game_map.get_me().all_ships(): # If the ship is docked if ship.docking_status != ship.DockingStatus.UNDOCKED: # Skip this ship continue # For each planet in the game (only non-destroyed planets are included) for planet in game_map.all_planets(): # If the planet is owned if planet.is_owned(): # Skip this planet continue # If we can dock, let's (try to) dock. If two ships try to dock at once, neither will be able to. if ship.can_dock(planet): # We add the command by appending it to the command_queue command_queue.append(ship.dock(planet)) else: ''' adding this:''' if planet in planned_planets: continue else: # If we can't dock, we move towards the closest empty point near this planet (by using closest_point_to) # with constant speed. Don't worry about pathfinding for now, as the command will do it for you. # We run this navigate command each turn until we arrive to get the latest move. # Here we move at half our maximum speed to better control the ships # In order to execute faster we also choose to ignore ship collision calculations during navigation. # This will mean that you have a higher probability of crashing into ships, but it also means you will # make move decisions much quicker. As your skill progresses and your moves turn more optimal you may # wish to turn that option off. navigate_command = ship.navigate( ship.closest_point_to(planet), game_map, speed=int(hlt.constants.MAX_SPEED/2), ignore_ships=False) # If the move is possible, add it to the command_queue (if there are too many obstacles on the way # or we are trapped (or we reached our destination!), navigate_command will return null; # don't fret though, we can run the command again the next turn) if navigate_command: command_queue.append(navigate_command) planned_planets.append(planet) break # Send our set of commands to the Halite engine for this turn game.send_command_queue(command_queue) # TURN END # GAME END
It's a good idea to go ahead and visualize a game or two. Let's edit run_game.bat to be: halite.exe -d "240 160" "python MyBot-v1.py" "python MyBot.py"
... or just run that line in your command prompt, or ./halite -d "240 160" "python3 MyBot-v1.py" "python3 MyBot.py"
if you're on mac/linux.
This will tell you who the victor is at the end, but it can also help to go ahead and visualize the replays too, so drag and drop the replay files to the submit a bot page on the right. We still have some problems, but it looked as expected to me. Ship it!
As usual, we want to highlight our game files and send them to a zip file. For us, this means highlighting the hlt
directory, and the MyBot.py
file, and zipping these up to a submission.zip
. Now we submit these, and let's see how we do! Before submitting, my best rank was 1546
, and my typical scoring looked like:
After submitting this, and about 30 minutes later, my highest rank is 1078
and my recent games more like:
Alright, that's about what I expected to be honest. This has made a huge improvement for us, and we probably wont see such a massive performance improvement next time. Regardless, now what? From the game videos feed (go to your avatar picture at the top right, click it, choose profile), here you can see how you're doing. Oh look, I am rank 1015
now! It's cool to see our winning games, but, chances are, we're going to learn the most from our losing games. So, I'll click on one of my losing games to see a replay. Immediately, I see I crashed into myself and lost all but one ship immediately. Okay, that's not good. Next, I see my ship keeps changing its mind about where it's heading to! This isn't good either, we're wasting time! From viewing other games, I can see we often wind up with idle ships, once we've conquered many planets. I can see that the people I am competiting against are doing the same thing. I wonder what might happen if we were to use our ships to attack them! That's what we'll be doing in the next tutorial.