Trying to not run into ourselves - Halite III (2018) AI Coding Competition p.4




Halite III Part 4

Welcome to part 3 of the Halite III competition tutorials. Leading up to this point, we've written some basic code to help our ships navigate towards halite, but we're finding that our ships are colliding with eachother! This is obviously no good, so let's work on that next.

Our code up to this point:

# 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)

What we'd like to do now is keep track of the positions we're planning to move to next, and stop two ships from choosing to move to the same coordinate position. To do this, first we need to track them. We want to track them not ship-specifically, but by each turn. So, right above the ship for-loop, let's add position_choices = [], like so:

    position_choices = []
    for ship in me.get_ships():
        ...

Next, we want to add our chosen positions to this list. To do this, let's assign the direction we're going to choose to a variable, then grab the actual position from the position_dict, then append this position to the position_choices list, then we can make our move:

        directional_choice = max(halite_dict, key=halite_dict.get)
        position_choices.append(position_dict[directional_choice])
        command_queue.append(
            ship.move(directional_choice))

Alright, now we know what positions we're going to take, but we're doing nothing yet to stop collisions. We still want to check to make sure we're not going to collide. What we could do is, before we calculate the directional_choice, we can remove any direction choices from the halite_dict that will result in a position that is already going to be occupied. We can do it by modifying this loop:

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

...by adding an if statement before we add the entry into the halite_dict. We only want to add if the actual positional coordinates won't conflict, so we can use the following if-statement: if position_dict[direction] not in position_choices: so the loop becomes:

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

Full code up to this point:

# 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]

    position_choices = []
    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
            if position_dict[direction] not in position_choices:
                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))

        directional_choice = max(halite_dict, key=halite_dict.get)
        position_choices.append(position_dict[directional_choice])
        command_queue.append(
            ship.move(directional_choice))

    # 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)

Okay, so this is further improving, but we've quickly created a backup at the depot, and are ending up completely stuck often! Let's solve this and work more on our logic 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