def getClosestSpot(ship): possible = [] cleanPlaces(targetArea) for i in targetArea: if i != ship.position: pickDir = game_map.get_unsafe_moves(ship.position, i)[0] if ((pickDir).__ne__(Direction.Still)) and (ship.position.__ne__( i)) and game_map[i].halite_amount > 100: possible.append(i) if len(possible) > 0: closest = possible[0] for i in possible: if game_map.calculate_distance(ship.position, i) < game_map.calculate_distance( ship.position, closest): if game_map.get_unsafe_moves(ship.position, i)[0] != Direction.Still: closest = i return Direction.convert( game_map.get_unsafe_moves(ship.position, closest)[0]) else: for i in Direction.get_all_cardinals(): if (game_map[ship.position.directional_offset(i)].is_occupied == False): return Direction.convert( game_map.get_unsafe_moves( ship.position, ship.position.directional_offset(i))[0])
def get_random_move(ship): moveChoice = random.choice(["n", "s", "e", "w"]) #logging.info("Ship - get_random_move() - ship {} moveChoice2: {}".format(ship.id, moveChoice)) moveOffset = ship.position.directional_offset(DIRECTIONS[moveChoice]) #logging.info("Ship - get_random_move() - ship {} moveOffset2: {}".format(ship.id, moveOffset)) move = Direction.convert(game_map.naive_navigate(ship, moveOffset)) #logging.info("Ship - get_random_move() - ship {} final move2: {}".format(ship.id, move)) if move == "o": original_move = move for i in range(4): # get char direction vs alt code below so we can log consistent msg # alt code: ship.move(random.choice([Direction.North, Direction.South, Direction.East, Direction.West])) moveChoice = random.choice(["n", "s", "e", "w"]) moveOffset = ship.position.directional_offset( DIRECTIONS[moveChoice]) move = Direction.convert(game_map.naive_navigate(ship, moveOffset)) if move != "o": break if move == "o": logging.info( "Ship - get_random_move() - ship {} Collision, original {}, correct failed" .format(ship.id, original_move)) else: logging.info( "Ship - get_random_move() - ship {} Collision, original {}, corrected {}" .format(ship.id, original_move, move)) return move
def get_dense_move(ship): moves = [] for d in ship.position.get_surrounding_cardinals(): if game_map[ d].halite_amount > constants.MAX_HALITE / 10 and not ship.is_full: moves.append((d.x, d.y, game_map[d].halite_amount)) sorted_moves = sorted(moves, key=lambda item: item[2], reverse=True) logging.info( "Ship - get_dense_move() - sorted_moves {}".format(sorted_moves)) if len(sorted_moves) == 0: move = get_random_move(ship) else: pos = Position(sorted_moves[0][0] - ship.position.x, sorted_moves[0][1] - ship.position.y) logging.info("Ship - ship {} get_dense_move() - pos {}".format( ship.id, pos)) moveOffset = game_map.normalize(pos) logging.info("Ship - ship {} get_dense_move() - moveOffset {}".format( ship.id, moveOffset)) move = Direction.convert( game_map.naive_navigate(ship, ship.position + moveOffset)) logging.info("Ship - ship {} get_dense_move() - move {}".format( ship.id, move)) if move == "o": for i in range(1, len(sorted_moves)): moveOffset = game_map.normalize( Position(sorted_moves[i][0] - ship.position.x, sorted_moves[i][1] - ship.position.y)) logging.info( "Ship - ship {} get_dense_move() - moveOffset {}".format( ship.id, moveOffset)) move = Direction.convert( game_map.naive_navigate(ship, moveOffset)) if move != "o": break if move == "o": logging.info("Ship - ship {} get_dense_move() - noop".format(ship.id)) return move
def resolve_halite_move(game, collision): """ :param collision [0:ship1 1:ship2 2:move 3:dest1 4:res_func] :returns Returns the 'o' or the lateral move based on the one with the most halite. 'o' gets a 10% preference. """ ship = collision[0] move = collision[2] position = collision[3] if DEBUG & (DEBUG_NAV): logging.info("Nav - ship {} - Resolving density move".format(ship.id)) move_offsets = Direction.laterals(DIRECTIONS[move]) move_offsets.append(Direction.Still) move_offsets.append(Direction.invert(DIRECTIONS[move])) best_moves = [] for o in move_offsets: pos = ship.position.directional_offset(o) cell = game.game_map[pos] if o == Direction.Still: val = cell.halite_amount * 1.1 elif o == Direction.invert(DIRECTIONS[move]): val = cell.halite_amount * .5 else: val = cell.halite_amount best_moves.append((cell, val, o)) best_moves.sort(key=lambda item: item[1], reverse=True) new_offset = None new_cell = None # try the cells to the left/right of the direction orginally attempted in order of # desirability. Additionally check staying still. for m in best_moves: if not m[0].is_occupied: new_offset = m[2] new_cell = m[0] break; # if no cell available, we know ship lost it's original cell, and the two lateral moves are # blocked, try the only remaining option, the inverse of the original, otherwise rewind if new_offset is None: cnt = unwind(game, ship) if DEBUG & (DEBUG_NAV): logging.info("Nav - ship {} - Resolve failed, Unwinding {} ships and using 'o'".format(ship.id, cnt)) #game_map[ship].mark_unsafe(ship) # unwind will mark the cell unsafe move = 'o' else: if DEBUG & (DEBUG_NAV): logging.info("Nav - ship {} - Resolved by best cell {}".format(ship.id, new_cell.position)) new_cell.mark_unsafe(ship) move = Direction.convert(new_offset) if DEBUG & (DEBUG_NAV): logging.info("Nav - ship {} - Successfully resolved density move collision. Move: {}".format(ship.id, move)) return move
def get_best_move(self, move_dict, description, crash_base=False): for ship in move_dict: if ship.id in self.can_not_move: #Can't move self.commands.append(f'm {ship.id} o') self.moves.append(str(ship.position)) continue for i in range(len(move_dict[ship])): if self.valid_move(move_dict[ship][i][2]): # logging.info(f"VALID MOVE {description} Ship ID: {ship.id} H/M/P: {move_dict[ship][i]}") self.moves.append(str(move_dict[ship][i][2])) direction = Direction.convert(move_dict[ship][i][1]) self.commands.append(f'm {ship.id} {direction}') break else: if crash_base == True and move_dict[ship][i][0] == 0: self.moves.append(str(move_dict[ship][i][2])) direction = Direction.convert(move_dict[ship][i][1]) self.commands.append(f'm {ship.id} {direction}') break
def _get_homing_next_move(self, shippos, gmap): drops = self.game.me.get_dropoffs() drops.append(self.game.me.shipyard) # trying to add shipyard near_drop = drops[0] for drop in drops: if gmap.calculate_distance( shippos, drop.position) < gmap.calculate_distance( shippos, near_drop.position): near_drop = drop return Direction.convert( gmap.naive_navigate(self.ship, near_drop.position))
def get_command(game_map, ship, shipyard): best_neighbor_position = None best_neighbor_direction = None max_halite = 0 current_halite = game_map[ship.position].halite_amount # Get the nearest neighbor positions and check their halite amounts for neighboring_position in ship.position.get_surrounding_cardinals(): halite_amt = game_map[neighboring_position].halite_amount is_occupied = game_map[neighboring_position].is_occupied if (best_neighbor_position is None or halite_amt > max_halite) and not is_occupied: max_halite = halite_amt best_neighbor_position = neighboring_position best_neighbor_direction = game_map.naive_navigate(ship, best_neighbor_position) if max_halite == 0: # We're surrounded by zero. Pick a random direction for now. We should be smarter about picking targets. best_neighbor_direction = random.choice([Direction.North, Direction.South]) if best_neighbor_position is None: # We've been trapped? There is no best position after checking all four # For now we'll crash to the right to see if we can free up the situation best_neighbor_direction = Direction.East logging.info('Max halite nearby: {} direction: {}'.format( max_halite, Direction.convert(best_neighbor_direction))) logging.info('Halite storage is at {}'.format(ship.halite_amount)) logging.info('Current halite at this position: {}'.format(current_halite)) two_turn_stay_ev, two_turn_move_ev = get_future_ev(current_halite, max_halite) if two_turn_stay_ev > two_turn_move_ev: # Better to stay logging.info("I should stay here. EV={} vs {}".format( current_halite * .25 + current_halite * (.75 * .25), max_halite * .25 - current_halite * .1 )) return ship.stay_still() else: logging.info("I should move. EV={} vs {}".format( current_halite * .25 + current_halite * (.75 * .25), max_halite * .25 - current_halite * .1 )) return ship.move(best_neighbor_direction)
def endGame(ship): Dir = game_map.get_unsafe_moves(ship.position, game_map[me.shipyard].position) #print(Dir) moveDir = Direction.Still if len(Dir) > 0: if (ship.position.directional_offset(Dir[0])).__eq__( game_map[me.shipyard].position): moveDir = game_map.get_unsafe_moves( ship.position, game_map[me.shipyard].position)[0] #print(moveDir) else: moveDir = game_map.naive_navigate(ship, game_map[me.shipyard].position) command_queue.append(ship.move(Direction.convert(moveDir))) return 0
def get_closest_dropoff_move(game_map, ship, dropoff_positions): distances = [] for dropoff in dropoff_positions: distance = game_map.calculate_distance(ship.position, dropoff) distances.append((distance, dropoff)) distances = sorted(distances, key=lambda x: x[0]) logging.debug(f"Distances: {distances}") closest_dropoff = distances[0] move = game_map.naive_navigate(ship, closest_dropoff[1]) logging.info("Closest Dropoff: {} -> {}, distance {}, move {}".format( ship.position, closest_dropoff[1], closest_dropoff[0], Direction.convert(move) )) if move == Direction.Still: logging.warning("This shouldn't happen, the nearest dropoff is current position?") return move
ship.status = "exploring" else: dropoffs = me.get_dropoffs() destinations = list(dropoffs) + [me.shipyard.position] minDistance = False movePosition = False move = False for dest in destinations: distance = game_map.calculate_distance(ship.position, dest) if minDistance == False or distance < minDistance: minDistance = distance movePosition = dest move = Direction.convert( game_map.naive_navigate(ship, movePosition)) logging.info("Ship - Ship {} initial move1: {}".format( ship.id, move)) if move == "o": logging.info("Ship - Ship {} Collision returning".format( ship.id)) ship.status = "backingoff" ship.path.append(getBackoffPoint(game, ship, dest)) else: fuelCost = round( game_map[ship.position].halite_amount * .1, 2) if fuelCost > ship.halite_amount: logging.info( "Ship - Ship {} has insuffient fuel. Have {}, need {}"
def get_halite_move(game, ship, args=None): """ Get a move based on the surrounding cell with the most halite :param args None ... for now. :returns Returns a move letter on success, None if there is a collision. """ if args is None: args = {} move = "o" if DEBUG & (DEBUG_NAV_VERBOSE): logging.info("Nav - ship {} is getting a density based move".format( ship.id)) sorted_blocks = get_best_blocks(game, ship, 3, 3) if not sorted_blocks: old_threshold = ship.mining_threshold ship.mining_threshold = 25 if DEBUG & (DEBUG_NAV): logging.info( "Nav - ship {} Best block search failed 1. All surrounding cells have halite < threshold({}). Setting mining_threshold to {} and retrying. t{}" .format(ship.id, old_threshold, ship.mining_threshold, game.turn_number)) sorted_blocks = get_best_blocks(game, ship, 3, 3) if not sorted_blocks: move = get_random_move( game, ship) # ToDo: would be better to try a large search radius? if DEBUG & (DEBUG_NAV): logging.info( "Nav - ship {} Best block search failed 2. All surrounding cells have halite < threshold({}) . Returning random move: {}. t{}" .format(ship.id, ship.mining_threshold, move, game.turn_number)) return move best_bloc_data = sorted_blocks[ 0] # (directional_offset, block, block mean value) max_cell_amount = best_bloc_data[1].get_max() if DEBUG & (DEBUG_NAV_VERBOSE): logging.info( "Nav - ship {} found {} valid halite cells with a the max cell containing {} halite" .format(ship.id, len(sorted_blocks), max_cell_amount)) for best_cell in best_bloc_data[1].get_cells(): if best_cell.halite_amount == max_cell_amount: break move_offset = best_bloc_data[0] new_position = game.game_map.normalize( ship.position.directional_offset(move_offset)) normalized_position = game.game_map.normalize(new_position) cell = game.game_map[normalized_position] if DEBUG & (DEBUG_NAV_VERBOSE): logging.info( "Nav - Ship {} next cell: {}, offset: {}, value: {}".format( ship.id, normalized_position, move_offset, cell.halite_amount)) # # collision resolution # if cell.is_occupied: game.collisions.append( (ship, cell.ship, Direction.convert(move_offset), normalized_position, resolve_halite_move)) # args = alt moves? if DEBUG & (DEBUG_NAV): logging.info( "Nav - ship {} collided with ship {} at {} while moving {}". format(ship.id, cell.ship.id, normalized_position, Direction.convert(move_offset))) return None # # success # cell.mark_unsafe(ship) move = Direction.convert(move_offset) # blocks are guaranteed to have at least 1 cell that is minable. This is critical to avoid getting stuck # between two blocks each of which is never modified. For a 3x3 block, add 'plus_one' assures that we move # far enough to reach the modifiable cell, thus preventing an endless movement between blocks move_plus_one = Position( best_cell.position.x, best_cell.position.y) # go one more move in the same direction if cell.position != move_plus_one: if DEBUG & (DEBUG_NAV): logging.info( "Nav - Ship {} has a plus_one move of {}, halite: {}".format( ship.id, move_plus_one, max_cell_amount)) ship.path.append(move_plus_one) return move
def get_nav_move(game, ship, args=None): """ Get a move based on the exist ship.path :param game :param ship :param args :returns Returns a move letter on success, None if there is a collision. """ if args is None: args = {} waypoint_resolution = args[ "waypoint_resolution"] if "waypoint_resolution" in args else "astar" move_cost = args["move_cost"] if "move_cost" in args else "turns" game_map = game.game_map if DEBUG & (DEBUG_NAV_VERBOSE): logging.info( "Nav - ship {} Getting nav move, path: {}, waypoint resolution: {}, move_cost: {}" .format(ship.id, list_to_short_string(ship.path, 4), waypoint_resolution, move_cost)) ship_cell = game_map[ship] if not ship.path: if DEBUG & (DEBUG_NAV): logging.warn( "Nav - ship {} Getting nav path. Empty path. Returning 'o'. t{}" .format(ship.id, game.turn_number)) if ship_cell.is_occupied: game.collisions.append((ship, ship_cell.ship, 'o', ship.position, resolve_nav_move)) # args = ? if DEBUG & (DEBUG_NAV): logging.info( "Nav - Ship {} collided with ship {} at {} while moving {}" .format(ship.id, ship_cell.ship.id, ship.position, 'o')) return None else: ship_cell.mark_unsafe(ship) return 'o' next_position = ship.path[-1] # check to see if we have a waypoint, not a continous path if game_map.calculate_distance(ship.position, next_position) > 1: normalized_next_position = game_map.normalize(next_position) if DEBUG & (DEBUG_NAV): logging.info( "Nav - ship {} Getting nav path. Found waypoint {}, calulating complete path" .format(ship.id, next_position)) # calc a continous path path, cost = game_map.navigate(ship.position, normalized_next_position, waypoint_resolution, {"move_costs": move_cost}) if path is None or len(path) == 0: if DEBUG & (DEBUG_NAV): logging.warn( "Nav - ship {} Nav failed, can't reach waypoint {} from {}" .format(ship.id, normalized_next_position, ship.position)) if ship_cell.is_occupied: game.collisions.append( (ship, ship_cell.ship, 'o', ship.position, resolve_nav_move)) # args = ? if DEBUG & (DEBUG_NAV): logging.info( "Nav - Ship {} collided with ship {} at {} while moving {}" .format(ship.id, ship_cell.ship.id, ship.position, 'o')) return None else: ship_cell.mark_unsafe(ship) return 'o' else: if DEBUG & (DEBUG_NAV_VERBOSE): logging.info( "Nav - ship {} path to waypoint {} found. Length: {}, cost: {}" .format(ship.id, next_position, len(path), round(cost))) ship.path.pop() ship.path = ship.path + path new_position = ship.path[-1] normalized_new_position = game_map.normalize(new_position) if DEBUG & (DEBUG_NAV_VERBOSE): logging.info("Nav - ship {} new_position: {}".format( ship.id, normalized_new_position)) # why? if normalized_new_position == ship.position: if DEBUG & (DEBUG_NAV): logging.warn( "Nav - ship {} popped move {}. Returning 'o'. Why did this happen?" .format(ship.id, ship.path[-1])) ship.path.pop() game_map[ship].mark_unsafe(ship) return 'o' cell = game_map[normalized_new_position] # use get_unsafe_moves() to get a normalized directional offset. We should always get one soln. offset = game_map.get_unsafe_moves(ship.position, normalized_new_position)[0] move = Direction.convert(offset) if DEBUG & (DEBUG_NAV_VERBOSE): logging.info("Nav - Ship {} has potential nav move: {}".format( ship.id, move)) # # collision resolution # if cell.is_occupied: game.collisions.append((ship, cell.ship, move, normalized_new_position, resolve_nav_move)) # args = ? if DEBUG & (DEBUG_NAV): logging.info( "Nav - Ship {} collided with ship {} at {} while moving {}". format(ship.id, cell.ship.id, normalized_new_position, move)) return None # # success # cell.mark_unsafe(ship) if DEBUG & (DEBUG_NAV_VERBOSE): logging.info("Nav - ship {} popped nav path {}".format( ship.id, ship.path[-1])) ship.path.pop() return move
else: # For each of your ships, move randomly if the ship is on a low halite location or the ship is full. # Else, collect halite. ship_x, ship_y = ship.position.x, ship.position.y ship_utility = deepcopy( game_utility_grid[ship_x][ship_y].utilities) ship_utility.sort(key=by_halite, reverse=True) for i in range(5): if game_map[ship.position.directional_offset( ship_utility[i] [0])].is_occupied and ship.position.directional_offset( ship_utility[i][0]) != ship.position: logging.info("continues") continue else: if Direction.convert(ship_utility[i][0]) == "o": logging.info("still at %s", ship.stay_still()) command_queue.append(ship.stay_still()) else: logging.info("logging %s", ship_utility[i][0]) command_queue.append(ship.move(ship_utility[i][0])) break # if game_map[ship.position].halite_amount < constants.MAX_HALITE / 10 or ship.is_full: # command_queue.append( # ship.move( # random.choice([ Direction.North, Direction.South, Direction.East, Direction.West ]))) # else: # command_queue.append(ship.stay_still()) # If the game is in the first 200 turns and you have enough halite, spawn a ship.
def resolve_nav_move(game, collision): """ Resolve a nav move. Note: The current position of the ship is 'given up'/marked safe for others in get_move(), there is no guarantee the ship can remain in it's current position - be sure to check is_occupied for the current cell if returning 'o' :param game :param collision Collision tuple: (0:ship1 1:ship2 2:move 3:dest1 4:res_func) :returns Returns move based on a number of special cases, if none of these exists, returns a random move excluding the original collision move. Returns 'o' if no move exists. """ ship1 = collision[0] ship2 = collision[1] move = collision[2] position = collision[3] collision_cell = game.game_map[position] ship_cell = game.game_map[ship1] if DEBUG & (DEBUG_NAV): logging.info("Nav - ship {} - Resolving nav move".format(ship1.id)) if DEBUG & (DEBUG_NAV): logging.info( "Nav - Ship {} collided with ship {} at {} while moving {}".format( ship1.id, ship2.id, position, move)) #base = get_base_positions(game, ship1.position) # don't let enemy ships on a dropoff/shipyard block arrivals or spawns. Halite from both ships will be deposited if collision_cell.structure_type is Shipyard and collision_cell.ship.owner != game.me.id: if DEBUG & (DEBUG_NAV): logging.info( "Nav - Ship {} collided with enemy ship {} at shipyard. Crashing" .format(ship1.id, ship2.id)) collision_cell.mark_unsafe(ship1) ship1.path.pop() new_move = move elif ship2.position == collision_cell.position and ship2.owner != game.me.id and collision_cell.position in get_base_surrounding_cardinals( game, ship1.position): game.base_clear_request.insert(0, { "position": collision_cell.position, "ship": ship2 }) # , "base": base if DEBUG & (DEBUG_NAV): logging.info( "Nav - Ship {} sent a dropoff clear request for {}. Blocked by {}" .format(ship1.id, collision_cell.position, ship2.id)) if game.game_map[ship1].is_occupied: new_move = None # None == unwind else: ship_cell.mark_unsafe(ship1) new_move = "o" # when arriving at a droppoff, wait from entry rather than making a move # this probably will not work as well without entry/exit lanes elif ship1.path and ship1.path[ 0] == game.me.shipyard.position and game.game_map.calculate_distance( ship1.position, game.me.shipyard.position) <= 2: if game.game_map[ship1].is_occupied: new_move = None # None == unwind else: ship_cell.mark_unsafe(ship1) new_move = "o" # when departing ... elif ship1.position == game.me.shipyard.position: # wait zero for enemy if collision_cell.ship.owner != game.me.id: collision_cell.mark_unsafe(ship1) ship1.path.pop() new_move = move # wait to leave for friendly, ... but make sure our old cell is available. prob should never happen # since we don't spawn with a ship already in shipyard, but rapid departure could cause this elif game.game_map[ship1].is_occupied: new_move = None # None == unwind else: ship_cell.mark_unsafe(ship1) new_move = 'o' else: if DEBUG & (DEBUG_NAV): logging.info( "Nav - ship {} collision at {} with ship {}. Resolving to random move {}" .format(ship1.id, position, ship2.id, move)) new_move = resolve_random_move(game, collision, { "moves": [Direction.convert(m) for m in Direction.laterals(move)] }) # popping the path point will allow nav around the blocking ship, buy this can # cause conjestion/screw up arrival/departure lanes if close to the dropoff if game.game_map.calculate_distance(ship2.position, game.me.shipyard.position) > 4: if len(ship1.path) > 1: ship1.path.pop() # # if we were not able to resolve above, unwind ... # if new_move is None: cnt = unwind(game, ship1) if DEBUG & (DEBUG_NAV): logging.info( "Nav - ship {} - Resolved by unwinding {} ships".format( ship1.id, cnt)) #ship_cell.mark_unsafe(ship1) this is handled by unwind new_move = 'o' if DEBUG & (DEBUG_NAV): logging.info( "Nav - ship {} - Successfully resolved nav move collision. Move: {}" .format(ship1.id, new_move)) return new_move
# Debug metric, can't use position because will be for diff ship/position every time #DebugMetrics["loiterOffsets"].append((loiterOffset.x, loiterOffset.y)) #DebugMetrics["loiterDistances"].append((game.turn_number, round(math.sqrt(loiterOffset.x ** 2 + loiterOffset.y ** 2), 2))) loiterPoint = ship.position + loiterOffset #logging.info("Ship - backoff/loiter point: {}".format(loiterPoint)) ship.path.append(loiterPoint) ship.status = "exploring" else: dropoff_position = get_dropoff_position(ship) move = Direction.convert( game_map.naive_navigate(ship, dropoff_position)) logging.info("Ship - Ship {} initial move1: {}".format( ship.id, move)) if move == "o": logging.info("Ship - Ship {} Collision returning".format( ship.id)) ship.status = "backingoff" ship.path.append( get_backoff_point(game, ship, dropoff_position)) else: fuelCost = round( game_map[ship.position].halite_amount * .1, 2) if fuelCost > ship.halite_amount: logging.info(
for ship in me.get_ships(): logging.info("Ship {} has {} halite with status {} and number of dropoffs {}.".format( ship.id, ship.halite_amount, ship_status[ship.id], len(me.get_dropoffs()))) # make the ship a drop off myDist = game_map.calculate_distance( ship.position, me.shipyard.position) # suicide bots run into the shipyard in the last to deposit halite ## This is the fastest way to get all ships to empty cargo at once if game.turn_number > 470: if ship.position == me.shipyard.position: command_queue.append(ship.stay_still()) elif myDist <2: myUnSafe = game_map.get_unsafe_moves(ship.position, me.shipyard.position) move = Direction.convert(myUnSafe[0]) command_queue.append(ship.move(move)) logging.info("unSafe moves: {}".format(move)) else: move = game_map.naive_navigate(ship, me.shipyard.position) command_queue.append(ship.move(move)) # turn a ship into a dropoff when game is not too far in # AND the ship is far enough away from the shipyard # AND the play can afford to spend 4000 on a dropoff elif (game.turn_number <= 330 and count == 0 and me.halite_amount > 6000 and myDist > 15 and game_map[ship.position].halite_amount > 500): count = count + 1 logging.info("ship {} is creating drop off @ {} with distance {}".format( ship.id, ship.position, myDist))
def pathfind(ship, m, unpassable: list): ''' Determines the best route to target using astar. Start and target are Position objects ''' size = m.shape[0] # Get the ship position start = ship.position # Get the target target = ship.target # Start position coord tupple sx, sy = start.x, start.y # Target position coord tupple tx, ty = target.x, target.y # Get adjacent squares (list with Position objects) adj = start.get_surrounding_cardinals() # Remove adjacent squares that are unpassable (occupied) # logging.info("Ship {}: Unpassable {}".format(ship.id, unpassable)) adj = [a for a in adj if a not in unpassable.values()] # See if ship can get out of the square # If not, return the same position # If no squares are available, stay still # And add current position to the unpassable list if (ship.halite_amount < round(0.1 * m[sy, sx]) or not adj): logging.info( "Ship {} on square {} with hal: {} needs {} to move and has {}". format(ship.id, (sx, sy), m[sy, sx], 0.1 * m[sy, sx], ship.halite_amount)) unpassable[ship.id] = start return 'o', unpassable # All the tiles with hal < htresh have value 0 m[m < htresh] = 0 # Sort Positions by value # Get position as tupple adj = [(pos.y, pos.x) for pos in adj] # Sort the positions based on val adj = sorted(adj, key=lambda c: val(c, (ty, tx), m))[0] # Replace with the navigate function # Get the best direction d_tuple = (adj[1] - sx, adj[0] - sy) # logging.info("{}".format(d_tuple)) # need to normalize the tuple in case of something like (0, -31) # Probably not the most efficient way of doing this if max([abs(a) for a in d_tuple]) == size - 1: d_tuple = tuple([int(a / -(size - 1)) for a in d_tuple]) direction = Direction.convert(d_tuple) # Append new position to unpassable unpassable[ship.id] = (start.directional_offset(d_tuple)) return direction, unpassable
def get_nav_move(game, ship, waypoint_algorithm="astar", args={"move_cost": "turns"}): game_map = game.game_map if DEBUG & (DEBUG_NAV): logging.info("NAV - ship {} getting nav move for path {}".format( ship.id, ship.path)) if not check_fuel_cost(game, ship): return 'o' if len(ship.path) == 0: if DEBUG & (DEBUG_NAV): logging.info("NAV - ship {} empty path".format(ship.id)) return 'o' next_position = ship.path[len(ship.path) - 1] # check to see if we have a waypoint, not a continous path if game_map.calculate_distance(ship.position, next_position) > 1: normalized_next_position = game_map.normalize(next_position) if DEBUG & (DEBUG_NAV): logging.info( "NAV - ship {} found waypoint {}, calulating complete path". format(ship.id, next_position)) # calc a continous path path, cost = game_map.navigate(ship.position, normalized_next_position, waypoint_algorithm, args) if path is None: if DEBUG & (DEBUG_NAV): logging.info("NAV - ship {} Nav failed, can't reach {}".format( ship.id, normalized_next_position)) return 'o' else: if DEBUG & (DEBUG_NAV): logging.info( "NAV - ship {} path to waypoint found with a cost of {} ({} turns)" .format(ship.id, round(cost), len(path))) ship.path.pop() ship.path = ship.path + path new_position = ship.path[len(ship.path) - 1] if DEBUG & (DEBUG_NAV): logging.info("NAV - ship {} new_position: {}".format( ship.id, new_position)) normalized_new_position = game_map.normalize(new_position) if DEBUG & (DEBUG_NAV): logging.info("NAV - ship {} normalized_new_position: {}".format( ship.id, normalized_new_position)) # why? if normalized_new_position == ship.position: ship.path.pop() return 'o' cell = game_map[normalized_new_position] # use get_unsafe_moves() to get a normalized directional offset. We should always get one soln. offset = game_map.get_unsafe_moves(ship.position, normalized_new_position)[0] move = Direction.convert(offset) if DEBUG & (DEBUG_NAV): logging.info("NAV - Ship {} has potential move: {}".format( ship.id, move)) # once we have the move, handle collisions if cell.is_occupied: if DEBUG & (DEBUG_NAV): logging.info( "Nav - Ship {} has a collision at {} while moving {}".format( ship.id, normalized_new_position, move)) # don't let enemy ships block the dropoff if cell.structure_type is Shipyard and cell.ship.owner != game.me.id: cell.mark_unsafe(ship) ship.path.pop() # when arriving at a droppoff, wait from entry rather than making a random # this probably will not work as well if not using entry/exit lanes elif normalized_new_position == game.me.shipyard.position: move = "o" # when departing a shipyard, try not to head the wrong direction elif ship.position == game.me.shipyard.position: alternate_moves = Direction.laterals(move) move = "o" for alternate_move_offset in alternate_moves: alternate_pos = ship.position.directional_offset( alternate_move_offset) alternate_cell = game_map[alternate_pos] if not alternate_cell.is_occupied: alternate_cell.mark_unsafe(ship) move = Direction.convert(alternate_move_offset) else: move = get_random_move(game, ship) if move == "o": if DEBUG & (DEBUG_NAV): logging.info( "NAV - ship {} collision at {} with ship {}, using {}". format(ship.id, normalized_new_position, cell.ship.id, move)) else: cell.mark_unsafe(ship) ship.path.pop() return move
def get_density_move(game, ship): move = "o" if DEBUG & (DEBUG_NAV): logging.info("Nav - ship {} is getting a density based move".format( ship.id)) if not check_fuel_cost(game, ship): return move moves = [] for quadrant in get_surrounding_cell_blocks(game, ship, 3, 3): directional_offset = quadrant[0] block = quadrant[1] if block.get_max() > constants.MAX_HALITE * MINING_THRESHOLD_MULT: moves.append((directional_offset, block, block.get_mean())) sorted_blocks = sorted(moves, key=lambda item: item[2], reverse=True) if len(sorted_blocks) == 0: return get_random_move( game, ship ) # FIX ME FIX ME FIX ME FIX ME FIX ME FIX ME would be better to try a large search radius ??? best_bloc_data = sorted_blocks[0] max_cell = best_bloc_data[1].get_max() bc = best_bloc_data[1].get_cells() for best_cell in bc: if best_cell.halite_amount == max_cell: break move_offset = best_bloc_data[0] if DEBUG & (DEBUG_NAV): logging.info("Nav - Ship {} moveOffset: {}".format( ship.id, move_offset)) new_position = game.game_map.normalize( ship.position.directional_offset(move_offset)) if DEBUG & (DEBUG_NAV): logging.info("Nav - Ship {} new_position: {}".format( ship.id, new_position)) normalized_position = game.game_map.normalize(new_position) if DEBUG & (DEBUG_NAV): logging.info("Nav - Ship {} normalized_position: {}".format( ship.id, normalized_position)) cell = game.game_map[normalized_position] if not cell.is_occupied: move = Direction.convert(move_offset) cell.mark_unsafe(ship) #game.game_map[ship.position].mark_safe() # if we were not able to find a usable dense cell, try to find a random one if move == "o": if DEBUG & (DEBUG_NAV): logging.info( "Nav - Ship {} Collision, trying to find a random move".format( ship.id)) lateral_offsets = Direction.laterals(move_offset) lateral_moves = list( map(lambda direction_offset: Direction.convert(direction_offset), lateral_offsets)) move = get_random_move(game, ship, lateral_moves) if move == "o": if DEBUG & (DEBUG_NAV): logging.info( "Nav - Ship {} Collision, unable to find a move".format( ship.id)) move_plus_one = Position( best_cell.position.x, best_cell.position.y) # go one more move in the same direction if DEBUG & (DEBUG_NAV): logging.info("Nav - Ship {} has a move_plus_one of {}".format( ship.id, move_plus_one)) ship.path.append(move_plus_one) return move
def getBack(ship, drop): possPos = [] returning = 1 mustGetBack = False same = False for dir in game_map.get_unsafe_moves(ship.position, drop): possPos.append(ship.position.directional_offset(dir)) if ship.position.x == drop.x and ship.position.y == drop.y: possPos.append(ship.position) same = True if ship.id not in shipsToReturn: returning = -1 if returning == 1 and remainingTurns - game_map.calculate_distance( ship.position, locateClosestDropoff(ship)) <= 10: mustGetBack = True min = possPos[0] last = shipsbeen[ship.id] # Min for returning, make sure that it doesn't go into the enemy territory if possible # Max not returning, make sure that it doesn't go into the enemy territory if possible for pos in possPos: if (returning == 1 and game_map[pos].halite_amount < game_map[min].halite_amount and pos not in nogozone and last != pos) or (min in nogozone and pos not in nogozone) or (min == last): min = pos elif (returning == -1 and game_map[pos].halite_amount > game_map[min].halite_amount and pos not in nogozone and last != pos) or (min in nogozone and pos not in nogozone) or (min == last): min = pos moves = game_map.get_unsafe_moves(ship.position, min) min = game_map.normalize(min) if not same: # Move if my ship isnt in the way if nextmap[min.x][min.y] == -1 or (nextmap[min.x][min.y] == -2 and min == drop): command_queue.append(ship.move(Direction.convert(moves[0]))) shipsOrdered.append(ship.id) nextmap[min.x][min.y] = ship.id nextmap[ship.position.x][ship.position.y] = -1 shipsbeen[ship.id] = Position(-1, -1) elif nextmap[min.x][min.y] == -2: badpos = list( map( lambda x: game_map.normalize( ship.position.directional_offset(Direction.invert(x))), game_map.get_unsafe_moves(ship.position, drop))) if returning == 1: max = 22000 maxpos = Position(-1, -1) else: max = -5 maxpos = Position(-1, -1) for pos in ship.position.get_surrounding_cardinals(): temp = game_map.normalize(pos) if temp not in badpos and nextmap[temp.x][temp.y] == -1: if returning == 1 and game_map[ temp].halite_amount < max and temp != last: max = game_map[temp].halite_amount maxpos = temp elif returning == -1 and game_map[ temp].halite_amount > max and temp != last: max = game_map[temp].halite_amount maxpos = temp if (returning == 1 and max < 22000) or (returning == -1 and max > -5): command_queue.append( ship.move( Direction.convert( game_map.get_unsafe_moves(ship.position, maxpos)[0]))) shipsOrdered.append(ship.id) nextmap[ship.position.x][ship.position.y] = -1 nextmap[maxpos.x][maxpos.y] = ship.id shipsbeen[ship.id] = ship.position else: command_queue.append(ship.stay_still()) shipsOrdered.append(ship.id) shipsToStay.append(ship.id) shipsbeen[ship.id] = Position(-1, -1) elif mustGetBack and (min == me.shipyard.position or min in list( map(lambda x: x.position, me.get_dropoffs()))): command_queue.append(ship.move(Direction.convert(moves[0]))) shipsOrdered.append(ship.id) nextmap[min.x][min.y] = ship.id nextmap[ship.position.x][ship.position.y] = -1 shipsbeen[ship.id] = Position(-1, -1) # When you don't want to swap since it would make the other ship further from goal elif ship.id not in shipsToReturn and nextmap[min.x][ min.y] >= 0 and game_map.calculate_distance( ship.position, shiptargets[nextmap[min.x][ min.y]]) > game_map.calculate_distance( min, shiptargets[nextmap[min.x][min.y]]): if game_map[ship.position].halite_amount < halitethreshold: #something I put in for testing purposes, get rid of it if it's bad # Don't just sit there if there's someone ahead and the cell is almost empty, move to nearest highest halite location max = game_map[ship.position].halite_amount maxpos = ship.position for pos in ship.position.get_surrounding_cardinals(): pos = game_map.normalize(pos) if nextmap[pos.x][ pos.y] == -1 and game_map[pos].halite_amount > max: max = game_map[pos].halite_amount maxpos = pos if maxpos != ship.position and (max >= halitethreshold or ship.position == me.shipyard.position): command_queue.append( ship.move( game_map.get_unsafe_moves(ship.position, maxpos)[0])) shipsOrdered.append(ship.id) nextmap[maxpos.x][maxpos.y] = ship.id nextmap[ship.position.x][ship.position.y] = -1 elif ship.position != me.shipyard.position: command_queue.append(ship.stay_still()) shipsOrdered.append(ship.id) shipsToStay.append(ship.id) shipsbeen[ship.id] = Position(-1, -1) # Swap positions of ships if first one is higher in priority elif nextmap[min.x][min.y] >= 0 and nextmap[min.x][ min.y] not in shipsToStay and nextmap[min.x][ min.y] not in shipsToReturn and nextmap[min.x][ min.y] not in shipsOrdered: command_queue.append(ship.move(Direction.convert(moves[0]))) command_queue.append(game.game_map[min].ship.move( Direction.invert(moves[0]))) shipsOrdered.append(ship.id) shipsOrdered.append(game.game_map[min].ship.id) nextmap[ship.position.x][ ship.position.y] = game.game_map[min].ship.id nextmap[min.x][min.y] = ship.id shipsbeen[ship.id] = Position(-1, -1) else: # if the destination is reached and it has less than 10 halite, go to the nearest highest halite square if game_map[drop].halite_amount < halitethreshold and ( drop.x, drop.y) not in forecast: max = game_map[drop].halite_amount maxpos = ship.position for pos in ship.position.get_surrounding_cardinals(): pos = game_map.normalize(pos) if nextmap[pos.x][pos.y] == -1 and game_map[ pos].halite_amount > game_map[ ship.position].halite_amount and game_map[ pos].halite_amount > max: max = game_map[pos].halite_amount maxpos = pos if max > game_map[drop].halite_amount and max >= halitethreshold: command_queue.append( ship.move( game_map.get_unsafe_moves(ship.position, maxpos)[0])) shipsOrdered.append(ship.id) nextmap[maxpos.x][maxpos.y] = ship.id nextmap[ship.position.x][ship.position.y] = -1 else: command_queue.append(ship.stay_still()) shipsOrdered.append(ship.id) shipsToStay.append(ship.id) else: command_queue.append(ship.stay_still()) shipsOrdered.append(ship.id) shipsToStay.append(ship.id) shipsbeen[ship.id] = Position(-1, -1)
def get_halite_move(game, ship, args=None): """ Get a move based on the surrounding cell with the most halite :param args None ... for now. :returns Returns a move letter on success, None if there is a collision. """ if args is None: args = {} move = "o" if DEBUG & (DEBUG_NAV): logging.info("Nav - ship {} is getting a density based move".format( ship.id)) moves = [] for blocks in game.game_map.get_cell_blocks( ship.position, 3, 3): # returns array of tuples [(direction), CellBlock] directional_offset = blocks[0] block = blocks[1] if block.get_max() > constants.MAX_HALITE * MINING_THRESHOLD_MULT: moves.append((directional_offset, block, block.get_mean())) sorted_blocks = sorted(moves, key=lambda item: item[2], reverse=True) if not sorted_blocks: move = get_random_move( game, ship) # ToDo: would be better to try a large search radius? if DEBUG & (DEBUG_NAV): logging.info( "Nav - ship {} All surrounding cells have halite < {} . Returning random move: {}" .format(ship.id, constants.MAX_HALITE * MINING_THRESHOLD_MULT, move)) return move best_bloc_data = sorted_blocks[ 0] # (directional_offset, block, block mean value) max_cell_amount = best_bloc_data[1].get_max() if DEBUG & (DEBUG_NAV): logging.info( "Nav - ship {} found {} valid halite cells with a the max cell containing {} halite" .format(ship.id, len(sorted_blocks), max_cell_amount)) for best_cell in best_bloc_data[1].get_cells(): if best_cell.halite_amount == max_cell_amount: break move_offset = best_bloc_data[0] new_position = game.game_map.normalize( ship.position.directional_offset(move_offset)) if DEBUG & (DEBUG_NAV): logging.info( "Nav - Ship {} best cell new_position: {}, offset: {}, value: {}". format(ship.id, new_position, move_offset, max_cell_amount)) normalized_position = game.game_map.normalize(new_position) if DEBUG & (DEBUG_NAV): logging.info("Nav - Ship {} best cell normalized_position: {}".format( ship.id, normalized_position)) cell = game.game_map[normalized_position] # # collision resolution # if cell.is_occupied: game.collisions.append( (ship, cell.ship, Direction.convert(move_offset), normalized_position, resolve_halite_move)) # args = alt moves? if DEBUG & (DEBUG_NAV): logging.info( "Nav - ship {} collided with ship {} at {} while moving {}". format(ship.id, cell.ship.id, normalized_position, Direction.convert(move_offset))) return None # # success # cell.mark_unsafe(ship) move = Direction.convert(move_offset) # blocks are guaranteed to have at least 1 cell that is minable. This is critical to avoid getting stuck # between two blocks each of which never modified. For a 3x3 block, add 'plus_one' assures that we move far # to reach the modifiable cell, thus prevent an endless movement between blocks move_plus_one = Position( best_cell.position.x, best_cell.position.y) # go one more move in the same direction if DEBUG & (DEBUG_NAV): logging.info("Nav - Ship {} has a move_plus_one of {}".format( ship.id, move_plus_one)) ship.path.append(move_plus_one) return move