Ejemplo n.º 1
0
    def populate_kamikaze(self, ship, potential_support_IDs,
                          explore_destination, enemy_position):
        support_ships = OrderedSet()
        for support_id in sorted(potential_support_IDs):
            if support_id in self.data.mySets.ships_to_move:
                support_ships.add(support_id)

        s = KamikazeShip(ship.halite_amount, ship.id, support_ships,
                         explore_destination, enemy_position)
        heapq.heappush(self.heap_kamikaze, s)
Ejemplo n.º 2
0
 def __init__(self, game):
     self.ships_all = OrderedSet(sorted(
         game.me._ships.keys()))  ## ALL SHIPS
     self.ships_to_move = OrderedSet(sorted(game.me._ships.keys(
     )))  ## SHIPS TO MOVE (SORTING TO MATCH ORDER ONLINE)
     self.ships_returning = OrderedSet()  ## SHIPS RETURNING HALITE
     self.ships_kicked = OrderedSet()
     self.ships_died = set()
     self.ships_ally_collision = set()
     self.ships_enemy_collision = set()
     self.ships_building = set()
     self.dock_coords = set()
     self.deposit_ships = OrderedSet()
Ejemplo n.º 3
0
    def get_neighbor_IDs(self, ship):
        """
        GET NEXT ALLY NEIGHBORS IN THE GIVEN SHIP

        :return: SET OF IDs
        """
        ally_IDs = OrderedSet()
        for direction in MyConstants.DIRECTIONS:
            destination = self.get_destination(ship, direction)
            id = self.data.myMatrix.locations.myShipsID[destination.y][
                destination.x]
            if id > Matrix_val.ZERO: ally_IDs.add(id)

        return ally_IDs
Ejemplo n.º 4
0
    def __init__(self, data, prev_data):
        Moves.__init__(self, data, prev_data)

        self.heap_set = set()
        self.heap_explore = []
        self.ships_kicked_temp = OrderedSet()

        self.halite_matrix = self.data.myMatrix.halite.updated_enemyCargo
        self.average_matrix = self.data.myMatrix.cell_average.enemyCargo

        if data.myVars.explore_disable_bonus:
            # self.harvest_matrix = copy.deepcopy(self.data.myMatrix.halite.enemyCargo_harvest)
            #self.harvest_matrix = self.data.myMatrix.halite.updated_enemyCargo_harvest
            self.harvest_matrix = self.data.myMatrix.halite.updated_enemyCargo_harvest * MyConstants.explore.score_harvest_ratio \
                                  + self.average_matrix * MyConstants.explore.score_average_ratio
        else:
            # self.harvest_matrix = self.data.myMatrix.halite.enemyCargo_harvest_with_bonus
            #self.harvest_matrix = self.data.myMatrix.halite.updated_enemyCargo_harvest_with_bonus
            self.harvest_matrix = self.data.myMatrix.halite.updated_enemyCargo_harvest_with_bonus * MyConstants.explore.score_harvest_ratio \
                                  + self.average_matrix * MyConstants.explore.score_average_ratio

        #self.harvest_matrix = copy.deepcopy(self.data.myMatrix.halite.enemyCargo_harvest)

        self.taken_matrix = np.zeros(
            (self.data.game.game_map.height, self.data.game.game_map.width),
            dtype=np.int16)
        self.taken_matrix.fill(1)  ## ZERO WILL BE FOR TAKEN CELL
        # r, c = np.where(self.data.myMatrix.locations.safe == Matrix_val.UNSAFE)
        # self.taken_matrix[r, c] = Matrix_val.ZERO
        #
        # self.taken_destinations = set()

        self.move_ships()
Ejemplo n.º 5
0
    def __init__(self, data, prev_data):
        Moves.__init__(self, data, prev_data)

        # self.halite_matrix = self.data.myMatrix.halite.updated_amount
        # self.average_matrix = self.data.myMatrix.cell_average.halite
        self.harvest_matrix = self.get_harvest_matrix()

        self.taken_matrix = np.zeros(
            (self.data.game.game_map.height, self.data.game.game_map.width),
            dtype=np.int16)
        self.taken_matrix.fill(1)  ## ZERO WILL BE FOR TAKEN CELL
        r, c = np.where(self.data.myMatrix.locations.safe == Matrix_val.UNSAFE)
        self.taken_matrix[r, c] = Matrix_val.ZERO
        self.taken_destinations = set()

        self.considered_already = OrderedSet()
        self.move_ships()
Ejemplo n.º 6
0
    def go_towards_building(self):
        """
        MOVE SHIPS TOWARD BUILDING DOCK
        """
        heap_halite = []

        if MyConstants.build.dock_manhattan > 2:
            for i in range(MyConstants.build.dock_manhattan - 2, 0,
                           -1):  ## MOVES SHIPS CLOSEST TO DOCK FIRST
                r, c = np.where(self.data.myMatrix.docks.manhattan == i)
                matrixIDs = OrderedSet(
                    self.data.myMatrix.locations.myShipsID[r, c])
                ships_going_dock = matrixIDs & self.data.mySets.ships_to_move

                logging.debug("ship going to dock: {} at i {}".format(
                    ships_going_dock, i))

                ## POPULATE HEAP
                for ship_id in sorted(ships_going_dock):
                    ship = self.data.game.me._ships.get(ship_id)
                    s = BuildShip(ship.halite_amount, ship_id)
                    heapq.heappush(heap_halite, s)

                while heap_halite:
                    s = heapq.heappop(heap_halite)
                    logging.debug(s)

                    ship = self.data.game.me._ships.get(s.ship_id)
                    dock_coord = self.get_closest_dock_coord(
                        ship)  ## DOCK COORD IS NONE IF ENEMY BUILT THERE
                    self.data.myDicts.ships_building_dock.setdefault(
                        dock_coord, set())

                    #if dock_coord and (ship.halite_amount >= 1000 or ship_id in self.prev_data.ships_building):
                    if dock_coord and ship.halite_amount >= MyConstants.build.min_halite_amount \
                        and len(self.data.myDicts.ships_building_dock[dock_coord]) < MyConstants.build.ships_per_dock \
                        and self.withinLimit_ships():

                        self.data.myVars.isBuilding = True

                        dock_position = Position(dock_coord[1], dock_coord[0])
                        directions = self.get_directions_target(
                            ship, dock_position)

                        if self.data.game.me.halite_amount < 5000:
                            self.data.myVars.isSaving = True  ## PREVENT SPAWNING SHIPS

                        ## OLD WAY
                        #direction = self.best_direction(ship, directions, mode=MoveMode.BUILDING, avoid_enemy=False, avoid_potential_enemy=False)
                        ## A STAR WAY
                        direction = self.get_Astar_direction(
                            ship, dock_position, directions)

                        ## RECORD INFO ALSO SHIP COUNTER PER DOCK
                        self.data.mySets.ships_building.add(ship_id)
                        self.data.myDicts.ships_building_dock[dock_coord].add(
                            ship.id)
                        self.move_mark_unsafe(ship, direction)
Ejemplo n.º 7
0
    def building_now(self):
        """
        MOVE SHIPS BUILDING NOW (CURRENTLY AT DOCK POSITION)
        """
        r, c = np.where(self.data.myMatrix.docks.manhattan ==
                        MyConstants.build.dock_manhattan)
        ships_on_docks = OrderedSet(self.data.myMatrix.locations.myShipsID[r,
                                                                           c])
        ships_building = ships_on_docks & self.data.mySets.ships_to_move

        for ship_id in sorted(ships_building):
            ship = self.data.game.me._ships.get(ship_id)
            cell_halite_amount = self.data.myMatrix.halite.amount[
                ship.position.y][ship.position.x]

            if self.withinLimit_ships():
                self.data.myVars.isBuilding = True
                if ship.halite_amount + self.data.game.me.halite_amount + cell_halite_amount >= 4000:
                    ## HAVE ENOUGH HALITE TO BUILD DOCK
                    self.data.game.me.halite_amount -= (4000 -
                                                        ship.halite_amount -
                                                        cell_halite_amount)
                    logging.debug(
                        "Shid id: {} building dock at position: {}".format(
                            ship.id, ship.position))
                    self.data.halite_stats.record_spent(BuildType.DOCK)
                    command = ship.make_dropoff()
                    self.data.command_queue.append(command)

                    ## CLEAR DOCK AREA, SO THAT OTHER SHIPS WILL NOT TRY TO BUILD ON IT
                    populate_manhattan(self.data.myMatrix.docks.manhattan,
                                       Matrix_val.ZERO, ship.position,
                                       MyConstants.build.dock_manhattan,
                                       Option.REPLACE)

                    self.data.init_data.myMatrix.docks.placement[
                        ship.position.y][ship.position.x] = Matrix_val.ZERO
                    self.data.init_data.myMatrix.docks.order[ship.position.y][
                        ship.position.x] = Matrix_val.NINETY
                else:
                    ## NOT ENOUGH HALITE YET, STAY STILL
                    self.data.myVars.isSaving = True  ## PREVENT SPAWNING SHIPS
                    direction = Direction.Still
                    command = ship.move(direction)
                    self.data.command_queue.append(command)

                ## RECORD INFO ALSO SHIP COUNTER PER DOCK
                dock_coord = (ship.position.y, ship.position.x)
                self.data.myDicts.ships_building_dock.setdefault(
                    dock_coord, set())
                self.data.myDicts.ships_building_dock[dock_coord].add(ship.id)
                self.mark_unsafe(ship, ship.position)
                self.data.mySets.ships_to_move.remove(ship.id)
Ejemplo n.º 8
0
    def build_on_high_halite(self):
        """
        BUILD A DOCK RIGHT AWAY ON A HIGH COLLISION CELL, TO PREVENT ENEMY FROM HARVESTING IT
        """
        ## BUILD ANYWHERE WHERE HALITE IS HIGH
        r, c = np.where(self.data.myMatrix.halite.amount >=
                        MyConstants.build.dock_anywhere_halite)
        ships_on_high_halite = OrderedSet(
            self.data.myMatrix.locations.myShipsID[r, c])
        ships_building = ships_on_high_halite & self.data.mySets.ships_to_move

        self.building_aggressively(ships_building, check_close_docks=False)

        ## BUILD WHERE HALITE IS MEDIUM AND NO CLOSE DOCKS
        r, c = np.where(self.data.myMatrix.halite.amount >=
                        MyConstants.build.dock_far_halite)
        ships_on_high_halite = OrderedSet(
            self.data.myMatrix.locations.myShipsID[r, c])
        ships_building = ships_on_high_halite & self.data.mySets.ships_to_move

        self.building_aggressively(ships_building, check_close_docks=True)
Ejemplo n.º 9
0
    def move_ships(self):
        print_heading("Moving attack ships......")

        allowAttack = (constants.MAX_TURNS * MyConstants.attack.allowed_turns_lower_limit <= self.data.game.turn_number <= constants.MAX_TURNS * MyConstants.attack.allowed_turns_upper_limit) \
                           and len(self.data.mySets.ships_all) > MyConstants.attack.min_ships_before_attacking

        ## MOVE SHIPS CLOSEST TO ENEMY FIRST (WITH ITS SUPPORT SHIP)
        if allowAttack:
            considered_prev_i = OrderedSet(
            )  ## USED TO NOT CONSIDER PREVIOUS i

            for i in range(
                    1, MyConstants.attack.engage_enemy_distance
            ):  ## DONT NEED TO MOVE FURTHEST ONES (WILL BE MOVED AS SUPPORT)
                matrixIDs = self.data.myMatrix.locations.engage_enemy[
                    i] * self.data.myMatrix.locations.myShipsID
                r, c = np.where(matrixIDs > Matrix_val.ZERO)
                ships_engaging = OrderedSet(
                    self.data.myMatrix.locations.myShipsID[
                        r, c]) - considered_prev_i
                ships_attacking = ships_engaging & self.data.mySets.ships_to_move
                considered_prev_i.update(ships_attacking)
                self.considered_already.update(ships_attacking)

                logging.debug("i {} ships_attacking {}".format(
                    i, ships_attacking))

                self.heap_kamikaze = []
                self.heap_support = []  ## RESET PER ITERATION

                for ship_id in sorted(ships_attacking):
                    self.populate_heap(ship_id, i)

                ## MOVE ATTACK/SUPPORT SHIPS
                self.move_attack_suppport()

                ## MOVE KAMIKAZE SHIPS
                self.move_kamikaze()
Ejemplo n.º 10
0
    def building_later(self):
        """
        MOVE SHIPS RIGHT NEXT TO DOCK POSITION
        """
        i = MyConstants.build.dock_manhattan - 1  ## CURRENTLY RIGHT NEXT TO THE DOCK

        r, c = np.where(self.data.myMatrix.docks.manhattan == i)
        matrixIDs = OrderedSet(self.data.myMatrix.locations.myShipsID[r, c])
        ships_going_dock = matrixIDs & self.data.mySets.ships_to_move

        logging.debug("ship next to dock: {}".format(ships_going_dock))

        for ship_id in sorted(ships_going_dock):
            ship = self.data.game.me._ships.get(ship_id)
            dock_coord = self.get_closest_dock_coord(
                ship)  ## DOCK COORD IS NONE IF ENEMY BUILT THERE
            self.data.myDicts.ships_building_dock.setdefault(dock_coord, set())

            if dock_coord \
                and len(self.data.myDicts.ships_building_dock[dock_coord]) < MyConstants.build.ships_per_dock \
                and self.withinLimit_ships():

                self.data.myVars.isBuilding = True

                if self.data.game.me.halite_amount < 5000:
                    self.data.myVars.isSaving = True  ## PREVENT SPAWNING SHIPS

                dock_position = Position(dock_coord[1], dock_coord[0])
                directions = self.get_directions_target(ship, dock_position)
                dock_halite_amount = self.data.myMatrix.halite.amount[
                    dock_position.y][dock_position.x]

                if ship.halite_amount + self.data.game.me.halite_amount + dock_halite_amount >= 4000 \
                        and self.data.myMatrix.locations.safe[dock_position.y][dock_position.x] != Matrix_val.UNSAFE:
                    ## ENOUGH HALITE TO BUILD
                    direction = self.best_direction(
                        ship,
                        directions,
                        mode=MoveMode.BUILDING,
                        avoid_enemy=True,
                        avoid_potential_enemy=False)
                    self.move_mark_unsafe(
                        ship, direction)  ## DIRECTION IS A LIST OF DIRECTIONS
                else:
                    ## POPULATE UNSAFE AROUND DOCK SO NO OTHER SHIPS WILL GO TOWARDS IT
                    # self.data.myMatrix.locations.safe[dock_position.y][dock_position.x] = Matrix_val.UNSAFE
                    self.move_mark_unsafe(ship, Direction.Still)

                ## SHIP COUNTER PER DOCK
                self.data.myDicts.ships_building_dock[dock_coord].add(ship.id)
Ejemplo n.º 11
0
    def __init__(self, data, prev_data):
        Moves.__init__(self, data, prev_data)

        self.heap_set = set()
        self.heap_explore = []
        self.ships_kicked_temp = OrderedSet()

        if self.data.game.turn_number <= constants.MAX_TURNS * MyConstants.explore.enable_bonus_turns_above:
            self.harvest_matrix = copy.deepcopy(
                self.data.myMatrix.halite.harvest)
        else:
            self.harvest_matrix = copy.deepcopy(
                self.data.myMatrix.halite.harvest_with_bonus)

        self.taken_matrix = np.zeros(
            (self.data.game.game_map.height, self.data.game.game_map.width),
            dtype=np.int16)
        self.taken_matrix.fill(1)  ## ZERO WILL BE FOR TAKEN CELL
        r, c = np.where(self.data.myMatrix.locations.safe == Matrix_val.UNSAFE)
        self.taken_matrix[r, c] = Matrix_val.ZERO

        self.taken_destinations = set()

        self.move_ships()
Ejemplo n.º 12
0
def get_move_points_collision(Moves, ship, directions, all_directions):
    """
    GET POINTS FOR IMMINENT COLLISION PREVENTION

    :param ship:
    :param directions: DIRECTIONS SHIP WAS ORIGINALLY GOING
    :return:
    """
    points = []

    ## SET FIRST PRIORITY DIRECTIONS
    try: first_priority = OrderedSet(directions)
    except: first_priority = OrderedSet()

    ## SET SECOND PRIORITY DIRECTIONS
    second_priority = OrderedSet()
    if directions:
        for direction in directions:
            directions = set(get_adjacent_directions(direction))
            second_priority.update(directions)

    if all_directions:
        DIRECTIONS = MyConstants.ALL_DIRECTIONS
    else:
        DIRECTIONS = MyConstants.DIRECTIONS

    for direction in DIRECTIONS:                                                                                        ## HAS NO STILL (KICKED, NEED TO MOVE)
        destination = Moves.get_destination(ship, direction)

        safe = Moves.data.myMatrix.locations.safe[destination.y][destination.x]
        occupied = Moves.data.myMatrix.locations.occupied[destination.y][destination.x]
        priority_direction = 2 if direction in first_priority else 1 if direction in second_priority else 0
        cost = Moves.data.myMatrix.halite.cost[ship.position.y][ship.position.x]
        harvest = Moves.data.myMatrix.halite.harvest_with_bonus[destination.y][destination.x]
        harvest_amnt = harvest - cost

        c = CollisionPoints(safe, occupied, priority_direction, harvest_amnt, direction)
        points.append(c)

    logging.debug(points)

    return points
Ejemplo n.º 13
0
    def populate_heap(self, ship_id, i):
        """
        POPULATE HEAP, SHIP WITH LEAST SUPPORT WILL MOVE FIRST
        SO SHIPS WITH 2 SUPPORT WILL NOT TAKE A SUPPORT FOR ANOTHER

        WILL NOT BE ADDED TO HEAP IF IT HAS TOO MUCH HALITE CARGO
        """
        ship = self.data.game.me._ships.get(ship_id)
        directions_to_enemy, enemy_position = self.get_closest_enemy_position(
            ship)
        ship_distance = calculate_distance(ship.position, enemy_position,
                                           self.data)
        enemy_ship, enemy_shipID = self.get_enemy_ship(enemy_position)
        potential_support = get_manhattan(
            self.data.myMatrix.locations.myShipsID, enemy_position,
            MyConstants.attack.engage_enemy_distance)
        potential_support_IDs = potential_support - {
            -1
        } - self.considered_already  ## myShipsID contains -1
        num_enemy_ships = count_manhattan(
            self.data.myMatrix.locations.enemyShips, Matrix_val.ONE,
            enemy_position, MyConstants.attack.enemy_backup_distance)
        enemy_halite = self.data.myMatrix.locations.shipsCargo[
            enemy_position.y][enemy_position.x]
        my_halite = ship.halite_amount

        logging.debug(
            "Populate heap for attack.  ship {} enemy_position {} potential_support_IDs {} num_enemy_ships {}"
            .format(ship, enemy_position, potential_support_IDs,
                    num_enemy_ships))

        canHarvest, harvest_direction = self.check_harvestNow(
            ship_id,
            moveNow=False,
            avoid_enemy=True,
            avoid_potential_enemy=True)
        # if not (canHarvest): canHarvest, harvest_direction = self.check_harvestLater(ship_id,
        #                                                                              MyConstants.DIRECTIONS,
        #                                                                              kicked=False,
        #                                                                              moveNow=False,
        #                                                                              avoid_enemy=True,
        # 																		 avoid_potential_enemy=True)

        matrix_highest_ratio, max_ratio, explore_destination, harvest_value = self.get_matrix_ratio(
            ship)

        # directions = self.get_directions_target(ship, explore_destination)
        # explore_direction, points = self.best_direction(ship, directions, mode=MoveMode.EXPLORE, avoid_enemy=False)

        harvest_destination = self.get_destination(ship, harvest_direction)
        harvest_ratio = matrix_highest_ratio[harvest_destination.y][
            harvest_destination.x]

        ## POPULATE KAMIKAZE
        if ((len(self.data.game.players) == 2) and (i == 1 or i == 2)) \
                or i == 2:
            ## FOR 2 PLAYERS, CHECK i = 1 or 2
            ## FOR 4 PLAYERS, JUST CHECK i = 2
            self.populate_kamikaze(ship, potential_support_IDs,
                                   explore_destination, enemy_position)

        ## POPULATE SUPPORT
        if max_ratio > harvest_ratio * self.data.myVars.harvest_ratio_to_explore \
                and len(potential_support_IDs) > num_enemy_ships:
            ## ATTACKING (NOT HARVESTING)
            support_ships = OrderedSet()
            for support_id in sorted(potential_support_IDs):
                support_ship = self.data.game.me._ships.get(support_id)
                support_distance = calculate_distance(support_ship.position,
                                                      enemy_position,
                                                      self.data)

                canHarvest, harvest_direction = self.check_harvestNow(
                    support_id,
                    moveNow=False,
                    avoid_enemy=True,
                    avoid_potential_enemy=True)
                # if not (canHarvest): canHarvest, harvest_direction = self.check_harvestLater(support_id,
                #                                                                              MyConstants.DIRECTIONS,
                #                                                                              kicked=False,
                #                                                                              moveNow=False,
                #                                                                              avoid_enemy=True,
                # 																		 avoid_potential_enemy=True)

                matrix_highest_ratio, max_ratio, explore_destination, harvest_value = self.get_matrix_ratio(
                    support_ship)

                # directions = self.get_directions_target(ship, explore_destination)
                # explore_direction, points = self.best_direction(support_ship, directions, mode=MoveMode.EXPLORE, avoid_enemy=False)

                harvest_destination = self.get_destination(
                    support_ship, harvest_direction)
                harvest_ratio = matrix_highest_ratio[harvest_destination.y][
                    harvest_destination.x]

                ## HAVE TO BE JUST 1 DISTANCE AWAY OR CLOSER
                if support_id in self.data.mySets.ships_to_move \
                        and support_distance <= ship_distance + 1:
                    #and harvest_ratio < max_ratio * MyConstants.attack.support_harvest_ratio:

                    potental_harvest = (
                        my_halite + enemy_halite) * 0.25  ## POTENTIAL HARVEST
                    total_halite = support_ship.halite_amount + potental_harvest
                    real_gain = potental_harvest if total_halite < 1000 else (
                        1000 -
                        support_ship.halite_amount)  ## CAN ONLY GET MAX 1000

                    if real_gain > my_halite * self.data.myVars.support_gain_ratio:  ## MORE THAN 20% GAIN THAN WHAT WE LOST
                        support_ships.add(support_id)

            num_support = len(support_ships)

            # if my_halite < enemy_halite * MyConstants.ATTACK_ENEMY_HALITE_RATIO and num_support >= 1:
            if num_support >= 1:  ## ATTACK EVEN WHEN HAS HIGH CARGO
                s = SupportShip(num_support, ship.id, support_ships,
                                directions_to_enemy, enemy_position)
                heapq.heappush(self.heap_support, s)
Ejemplo n.º 14
0
class Attack(Moves, Attacks, Harvests, Explores):
    def __init__(self, data, prev_data):
        Moves.__init__(self, data, prev_data)

        # self.halite_matrix = self.data.myMatrix.halite.updated_amount
        # self.average_matrix = self.data.myMatrix.cell_average.halite
        self.harvest_matrix = self.get_harvest_matrix()

        self.taken_matrix = np.zeros(
            (self.data.game.game_map.height, self.data.game.game_map.width),
            dtype=np.int16)
        self.taken_matrix.fill(1)  ## ZERO WILL BE FOR TAKEN CELL
        r, c = np.where(self.data.myMatrix.locations.safe == Matrix_val.UNSAFE)
        self.taken_matrix[r, c] = Matrix_val.ZERO
        self.taken_destinations = set()

        self.considered_already = OrderedSet()
        self.move_ships()

    def get_harvest_matrix(self):
        """
        POPULATE HARVEST MATRIX TO BE USED FOR ATTACK
        """
        matrixes = [
            self.data.myMatrix.halite.harvest_with_bonus,
            self.data.myMatrix.halite.enemyCargo_harvest_with_bonus
        ]
        #matrixes = [self.data.myMatrix.halite.updated_harvest_with_bonus, self.data.myMatrix.halite.enemyCargo_harvest_with_bonus]
        return myMaxValueMatrix(*matrixes)

    def move_ships(self):
        print_heading("Moving attack ships......")

        allowAttack = (constants.MAX_TURNS * MyConstants.attack.allowed_turns_lower_limit <= self.data.game.turn_number <= constants.MAX_TURNS * MyConstants.attack.allowed_turns_upper_limit) \
                           and len(self.data.mySets.ships_all) > MyConstants.attack.min_ships_before_attacking

        ## MOVE SHIPS CLOSEST TO ENEMY FIRST (WITH ITS SUPPORT SHIP)
        if allowAttack:
            considered_prev_i = OrderedSet(
            )  ## USED TO NOT CONSIDER PREVIOUS i

            for i in range(
                    1, MyConstants.attack.engage_enemy_distance
            ):  ## DONT NEED TO MOVE FURTHEST ONES (WILL BE MOVED AS SUPPORT)
                matrixIDs = self.data.myMatrix.locations.engage_enemy[
                    i] * self.data.myMatrix.locations.myShipsID
                r, c = np.where(matrixIDs > Matrix_val.ZERO)
                ships_engaging = OrderedSet(
                    self.data.myMatrix.locations.myShipsID[
                        r, c]) - considered_prev_i
                ships_attacking = ships_engaging & self.data.mySets.ships_to_move
                considered_prev_i.update(ships_attacking)
                self.considered_already.update(ships_attacking)

                logging.debug("i {} ships_attacking {}".format(
                    i, ships_attacking))

                self.heap_kamikaze = []
                self.heap_support = []  ## RESET PER ITERATION

                for ship_id in sorted(ships_attacking):
                    self.populate_heap(ship_id, i)

                ## MOVE ATTACK/SUPPORT SHIPS
                self.move_attack_suppport()

                ## MOVE KAMIKAZE SHIPS
                self.move_kamikaze()

    def move_attack_suppport(self):
        while self.heap_support:
            s = heapq.heappop(self.heap_support)
            logging.debug(s)

            first_ship = self.data.game.me._ships.get(s.ship_id)
            direction = self.best_direction(first_ship,
                                            s.directions,
                                            mode=MoveMode.ATTACKING)

            if direction != Direction.Still and s.ship_id in self.data.mySets.ships_to_move:
                ## IF STAYING STILL, NO NEED TO MOVE
                ## FIRST SHIP WILL JUST HARVEST/EXPLORE
                ## SUPPORT SHIP MOVE WILL BE DETERMINED LATER
                destination = self.get_destination(first_ship, direction)

                ## OLD WAY (MARK TAKEN)
                #self.mark_taken_udpate_top_halite(destination)
                ## NEW WAY (DEDUCT HALITE TO BE HARVESTED)
                #self.update_harvest_matrix(s.ship_id, destination)

                self.move_mark_unsafe(first_ship, direction)

                logging.debug("Attacking ship id: {} support ships: {}".format(
                    first_ship.id, s.support_ships))

                for support_id in s.support_ships:
                    if support_id in self.data.mySets.ships_to_move:
                        support_ship = self.data.game.me._ships.get(support_id)
                        #support_directions = self.get_directions_target(support_ship, first_ship.position)
                        support_directions = self.get_directions_target(
                            support_ship, s.enemy_position)
                        direction = self.best_direction(
                            support_ship,
                            support_directions,
                            mode=MoveMode.SUPPORTING)
                        destination = self.get_destination(
                            support_ship, direction)

                        ## OLD WAY (MARK TAKEN)
                        #self.mark_taken_udpate_top_halite(destination)
                        ## NEW WAY (DEDUCT HALITE TO BE HARVESTED)
                        #self.update_harvest_matrix(support_id, destination)

                        self.move_mark_unsafe(support_ship, direction)

    def move_kamikaze(self):
        while self.heap_kamikaze:
            s_kamikaze = heapq.heappop(self.heap_kamikaze)
            logging.debug(s_kamikaze)

            s_exploreTarget = self.data.myDicts.explore_ship[
                s_kamikaze.ship_id]

            logging.debug("s_kamikaze.ship_id {}".format(s_kamikaze.ship_id))

            ship = self.data.game.me._ships.get(s_kamikaze.ship_id)

            if s_kamikaze.ship_id in self.data.mySets.ships_to_move:
                canHarvest, harvest_direction = self.check_harvestLater(
                    s_kamikaze.ship_id,
                    MyConstants.DIRECTIONS,
                    kicked=False,
                    moveNow=False,
                    avoid_enemy=False,
                    avoid_potential_enemy=False)
                harvest_destination = self.get_destination(
                    ship, harvest_direction)

                harvest_halite = self.data.myMatrix.halite.harvest_with_bonus[
                    harvest_destination.y][harvest_destination.x]

                logging.debug(
                    "harvest_destination {} s_kamikaze.explore_destination {} ship.halite_amount {} harvest_halite {}"
                    .format(harvest_destination, s_kamikaze.destination,
                            ship.halite_amount, harvest_halite))

                if ship.halite_amount <= MyConstants.attack.kamikaze_halite_max \
                    and harvest_halite >= ship.halite_amount * self.data.myVars.kamikaze_halite_ratio \
                    and ( ((len(self.data.game.players) == 2) and (harvest_destination == s_kamikaze.destination or harvest_destination == s_exploreTarget.destination)) \
                         or harvest_destination == s_kamikaze.destination ):
                    ## IF 2 PLAYERS, CHECK EITHER KAMIKAZE OR EXPLORE DESTINATION

                    self.move_mark_unsafe(ship, harvest_direction)

                    for support_id in s_kamikaze.support_ships:
                        if support_id in self.data.mySets.ships_to_move:
                            support_ship = self.data.game.me._ships.get(
                                support_id)
                            #support_directions = self.get_directions_target(support_ship, ship.position)
                            support_directions = self.get_directions_target(
                                support_ship, s_kamikaze.enemy_position)
                            direction = self.best_direction(
                                support_ship,
                                support_directions,
                                mode=MoveMode.SUPPORTING)
                            destination = self.get_destination(
                                support_ship, direction)

                            ## OLD WAY (MARK TAKEN)
                            #self.mark_taken_udpate_top_halite(destination)
                            ## NEW WAY (DEDUCT HALITE TO BE HARVESTED)
                            #self.update_harvest_matrix(support_id, destination)

                            self.move_mark_unsafe(support_ship, direction)

    def populate_heap(self, ship_id, i):
        """
        POPULATE HEAP, SHIP WITH LEAST SUPPORT WILL MOVE FIRST
        SO SHIPS WITH 2 SUPPORT WILL NOT TAKE A SUPPORT FOR ANOTHER

        WILL NOT BE ADDED TO HEAP IF IT HAS TOO MUCH HALITE CARGO
        """
        ship = self.data.game.me._ships.get(ship_id)
        directions_to_enemy, enemy_position = self.get_closest_enemy_position(
            ship)
        ship_distance = calculate_distance(ship.position, enemy_position,
                                           self.data)
        enemy_ship, enemy_shipID = self.get_enemy_ship(enemy_position)
        potential_support = get_manhattan(
            self.data.myMatrix.locations.myShipsID, enemy_position,
            MyConstants.attack.engage_enemy_distance)
        potential_support_IDs = potential_support - {
            -1
        } - self.considered_already  ## myShipsID contains -1
        num_enemy_ships = count_manhattan(
            self.data.myMatrix.locations.enemyShips, Matrix_val.ONE,
            enemy_position, MyConstants.attack.enemy_backup_distance)
        enemy_halite = self.data.myMatrix.locations.shipsCargo[
            enemy_position.y][enemy_position.x]
        my_halite = ship.halite_amount

        logging.debug(
            "Populate heap for attack.  ship {} enemy_position {} potential_support_IDs {} num_enemy_ships {}"
            .format(ship, enemy_position, potential_support_IDs,
                    num_enemy_ships))

        canHarvest, harvest_direction = self.check_harvestNow(
            ship_id,
            moveNow=False,
            avoid_enemy=True,
            avoid_potential_enemy=True)
        # if not (canHarvest): canHarvest, harvest_direction = self.check_harvestLater(ship_id,
        #                                                                              MyConstants.DIRECTIONS,
        #                                                                              kicked=False,
        #                                                                              moveNow=False,
        #                                                                              avoid_enemy=True,
        # 																		 avoid_potential_enemy=True)

        matrix_highest_ratio, max_ratio, explore_destination, harvest_value = self.get_matrix_ratio(
            ship)

        # directions = self.get_directions_target(ship, explore_destination)
        # explore_direction, points = self.best_direction(ship, directions, mode=MoveMode.EXPLORE, avoid_enemy=False)

        harvest_destination = self.get_destination(ship, harvest_direction)
        harvest_ratio = matrix_highest_ratio[harvest_destination.y][
            harvest_destination.x]

        ## POPULATE KAMIKAZE
        if ((len(self.data.game.players) == 2) and (i == 1 or i == 2)) \
                or i == 2:
            ## FOR 2 PLAYERS, CHECK i = 1 or 2
            ## FOR 4 PLAYERS, JUST CHECK i = 2
            self.populate_kamikaze(ship, potential_support_IDs,
                                   explore_destination, enemy_position)

        ## POPULATE SUPPORT
        if max_ratio > harvest_ratio * self.data.myVars.harvest_ratio_to_explore \
                and len(potential_support_IDs) > num_enemy_ships:
            ## ATTACKING (NOT HARVESTING)
            support_ships = OrderedSet()
            for support_id in sorted(potential_support_IDs):
                support_ship = self.data.game.me._ships.get(support_id)
                support_distance = calculate_distance(support_ship.position,
                                                      enemy_position,
                                                      self.data)

                canHarvest, harvest_direction = self.check_harvestNow(
                    support_id,
                    moveNow=False,
                    avoid_enemy=True,
                    avoid_potential_enemy=True)
                # if not (canHarvest): canHarvest, harvest_direction = self.check_harvestLater(support_id,
                #                                                                              MyConstants.DIRECTIONS,
                #                                                                              kicked=False,
                #                                                                              moveNow=False,
                #                                                                              avoid_enemy=True,
                # 																		 avoid_potential_enemy=True)

                matrix_highest_ratio, max_ratio, explore_destination, harvest_value = self.get_matrix_ratio(
                    support_ship)

                # directions = self.get_directions_target(ship, explore_destination)
                # explore_direction, points = self.best_direction(support_ship, directions, mode=MoveMode.EXPLORE, avoid_enemy=False)

                harvest_destination = self.get_destination(
                    support_ship, harvest_direction)
                harvest_ratio = matrix_highest_ratio[harvest_destination.y][
                    harvest_destination.x]

                ## HAVE TO BE JUST 1 DISTANCE AWAY OR CLOSER
                if support_id in self.data.mySets.ships_to_move \
                        and support_distance <= ship_distance + 1:
                    #and harvest_ratio < max_ratio * MyConstants.attack.support_harvest_ratio:

                    potental_harvest = (
                        my_halite + enemy_halite) * 0.25  ## POTENTIAL HARVEST
                    total_halite = support_ship.halite_amount + potental_harvest
                    real_gain = potental_harvest if total_halite < 1000 else (
                        1000 -
                        support_ship.halite_amount)  ## CAN ONLY GET MAX 1000

                    if real_gain > my_halite * self.data.myVars.support_gain_ratio:  ## MORE THAN 20% GAIN THAN WHAT WE LOST
                        support_ships.add(support_id)

            num_support = len(support_ships)

            # if my_halite < enemy_halite * MyConstants.ATTACK_ENEMY_HALITE_RATIO and num_support >= 1:
            if num_support >= 1:  ## ATTACK EVEN WHEN HAS HIGH CARGO
                s = SupportShip(num_support, ship.id, support_ships,
                                directions_to_enemy, enemy_position)
                heapq.heappush(self.heap_support, s)

    def populate_kamikaze(self, ship, potential_support_IDs,
                          explore_destination, enemy_position):
        support_ships = OrderedSet()
        for support_id in sorted(potential_support_IDs):
            if support_id in self.data.mySets.ships_to_move:
                support_ships.add(support_id)

        s = KamikazeShip(ship.halite_amount, ship.id, support_ships,
                         explore_destination, enemy_position)
        heapq.heappush(self.heap_kamikaze, s)