Moving towards most halite - Halite III (2018) AI Coding Competition p.3




Halite III part 3

Now, let's learn some things about our choices. We can only move once in the north, south, east or west direction, so we can have a pretty basic bot that just checks these. Code for doing this has already been made for us, and we can do something like:

So one option we have is, on a per-ship basis, check all surrounding squares, and then move towards the square with the most halite, remembering to also check our current position as well (holding there if it's the most ideal spot). To do this, let's first recognize that we must differentiate between a direction and a position. A position is a set of coordinates. We can use this position to learn information about things at those coordinates or around them, but, in order to actually move to a new position, we have to specify a direction. What are our directional choices?

    # specify the order we know this all to be
    direction_order = [Direction.North, Direction.South, Direction.East, Direction.West, Direction.Still]

This is the same for all ships, so we should define this right before our for loop for the ships. Next, on a per-ship basis, we want to collect all of the possible future positions that we could take. There's already a method for this, which can be applied to any Postion object: .get_surrounding_cardinals(). Keep this in mind. What we're going to do is very rudimentary, but you could...and should... probably get a LOT more fancy with this. I would likely instead weight, probably, all of the possible paths, at least for the next few steps, per ship, at every single step. Right now, our pathing is still going to be very stupid, but it will at least begin to resemble something slightly intelligent!

Let's begin doing this with:

    for ship in me.get_ships():
        # cardinals get surrounding cardinals using get_all_cardinals in positionals.py.
        # reading this file, I can see they will be in order of:
        # [Direction.North, Direction.South, Direction.East, Direction.West]
        # maps with position orders, but also gives us all the surrounding possitions
        position_options = ship.position.get_surrounding_cardinals() + [ship.position]

As the comments note, I went and read the positionals.py file to determine the order here. I did this to make sure that direction_order and position_options would map correctly to eachother. Next, let's make a couple of dictionaries:

        # position_dict = {(0, -1): Position(8, 15), (0, 1): Position(8, 17), (1, 0): Position(9, 16), (-1, 0): Position(7, 16), (0, 0): Position(8, 16)}
        position_dict = {}

        # maps the direction choice with halite
        # halite_dict = {(0, -1): 708, (0, 1): 492, (1, 0): 727, (-1, 0): 472, (0, 0): 0}
        halite_dict = {}

The idea here is that we want to map directions to both a position, and directions to halite amounts. This is by no means the most efficient method of doing this, because now we're going to use two loops to populate them:

        for n, direction in enumerate(direction_order):
            position_dict[direction] = position_options[n]

        for direction in position_dict:
            position = position_dict[direction]
            halite_amount = game_map[position].halite_amount
            halite_dict[direction] = halite_amount

Sorry mom.

No matter though, we have the information we need now.

...so why do we actually care about the position dict? Why not just use the halite dict? All we need to do is find the max square and move to it, so really we just need to do the simple task of finding the key with the highest value. That's pretty easy: max(halite_dict, key=halite_dict.get)

Well, let's try that, here's the full code:

# Python 3.6
import hlt  #main halite stuff
from hlt import constants  # halite constants
from hlt.positionals import Direction  # helper for moving
import random  # randomly picking a choice for now.
import logging  # logging stuff to console

game = hlt.Game()  # game object
# Initializes the game
game.ready("Sentdebot")

while True:
    # This loop handles each turn of the game. The game object changes every turn, and you refresh that state by
    game.update_frame()
    # You extract player metadata and the updated map metadata here for convenience.
    me = game.me

    '''comes from game, game comes from before the loop, hlt.Game points to networking, which is where you will
    find the actual Game class (hlt/networking.py). From here, GameMap is imported from hlt/game_map.py.

    open that file to seee all the things we do with game map.'''
    game_map = game.game_map  # game map data. Recall game is

    # A command queue holds all the commands you will run this turn. You build this list up and submit it at the
    #   end of the turn.
    command_queue = []

    # specify the order we know this all to be
    direction_order = [Direction.North, Direction.South, Direction.East, Direction.West, Direction.Still]
    for ship in me.get_ships():
        # cardinals get surrounding cardinals using get_all_cardinals in positionals.py.
        # reading this file, I can see they will be in order of:
        # [Direction.North, Direction.South, Direction.East, Direction.West]

        # maps with position orders, but also gives us all the surrounding possitions
        position_options = ship.position.get_surrounding_cardinals() + [ship.position]

        # we will be mapping the "direction" to the actual position

        # position_dict = {(0, -1): Position(8, 15), (0, 1): Position(8, 17), (1, 0): Position(9, 16), (-1, 0): Position(7, 16), (0, 0): Position(8, 16)}
        position_dict = {}

        # maps the direction choice with halite
        # halite_dict = {(0, -1): 708, (0, 1): 492, (1, 0): 727, (-1, 0): 472, (0, 0): 0}
        halite_dict = {}

        for n, direction in enumerate(direction_order):
            position_dict[direction] = position_options[n]

        for direction in position_dict:
            position = position_dict[direction]
            halite_amount = game_map[position].halite_amount
            halite_dict[direction] = halite_amount

        if game.turn_number == 15:
            logging.info(position_dict)
            logging.info(halite_dict)
            logging.info(max(halite_dict, key=halite_dict.get))

        command_queue.append(
            ship.move(max(halite_dict, key=halite_dict.get)))

    # ship costs 1000, dont make a ship on a ship or they both sink
    if me.halite_amount >= 1000 and not game_map[me.shipyard].is_occupied:
        command_queue.append(me.shipyard.spawn())

    # Send your moves back to the game environment, ending this turn.
    game.end_turn(command_queue)

Well, very quickly all of our ships head towards the same spots! Oops. That's not really ideal!

... thus we need a way to make sure we dont have two ships that go to the same spot, which is what we're going to focus on starting in the next tutorial!

The next tutorial:





  • Introduction - Halite III (2018) AI Coding Competition p.1
  • Running Locally and getting surrounding halite - Halite III (2018) AI Coding Competition p.2
  • Moving towards most halite - Halite III (2018) AI Coding Competition p.3
  • Trying to not run into ourselves - Halite III (2018) AI Coding Competition p.4
  • Moving to drop off halite - Halite III (2018) AI Coding Competition p.5
  • Cleaning up a few things - Halite III (2018) AI Coding Competition p.6