def safe_scan(this_ship, size): """ Scans the area to be covered defined by size with respect to the current ship and does not take into account the positions that are currently occupied and positions that are already assigned to previous ships as a dictionary with "(x, y)" cordinates and "halite amount" as key, value pairs. :param this_ship: current ship. :param size: covers 2 * size from current ship's position - size to current ship's position + size in both (x, y) directions. returns: tuple of co-ordinate of maximum halite available and halite dict. """ halite_dict = {(x, y): game_map[Position(x, y)].halite_amount for y in range(this_ship.position.y - size, this_ship.position.y + size) for x in range(this_ship.position.x - size, this_ship.position.x + size) if not game_map[Position(x, y)].is_occupied and ( x, y) not in owned_positions and (x, y) not in IGNORE} TARGET = max(halite_dict, key=halite_dict.get) MEDIAN = median(halite_dict.values()) ignore = rm_surrounding(Position(*TARGET), this_ship) return TARGET, ignore, MEDIAN
def find_best_cell(ship, marked_map, game_map): """ Finds best cell for a ship to mine, regarding the (energy/distance) ratio :param ship: the ship for which the cell is to be found :param marked_map: map filled with RESERVED or FREE :game_map: the actual game map :return: a Position of the best cell """ cell_rank = [] # lista cu rapoarte energie/distanta for i in range(constants.WIDTH): # generarea de distante for j in range(constants.HEIGHT): if Position(i, j) != ship.position: distan = game_map[Position( i, j)].halite_amount / game_map.calculate_distance( ship.position, Position(i, j)) cell_rank.insert(0, (Position(i, j), distan)) cell_rank.sort(reverse=True, key=(lambda a: a[1])) # sortare descrescatoare for i in cell_rank: if marked_map[i[0].x][i[0].y] == FREE: return i[ 0] # returneaza prima celula care nu este deja rezervata pentru alt ship
def find_destination(game_map): halite_map = [] for y in range(game_map.height): for x in range(game_map.width): pos = Position(x, y) halite_map.append(game_map[pos].halite_amount) halite_map = np.array(halite_map) num = np.array(game_map.height * game_map.width * 0.1).astype(int) indices = np.argpartition(halite_map, -num)[-num:] indices = indices[np.random.randint(0, len(indices), len(indices // 2))] positions = [] for idx in indices: positions.append(Position(idx % game_map.width, idx // game_map.height)) dist = 999 best_pos = positions[0] for pos in positions: d = game_map.calculate_distance(pos, ship.position) if d < dist: dist = d best_pos = pos return best_pos
def get_section_values(ship, game_map, turns): section_values = [] for i in range(0, 8): section_values.append([0, 0, 0, 0, 0, 0, 0, 0]) for x in range(-4, 4): for y in range(-4, 4): start_x = x * game_map.width / 8 + ship.position.x start_y = y * game_map.height / 8 + ship.position.y end_x = (x + 1) * game_map.width / 8 + ship.position.x end_y = (y + 1) * game_map.height / 8 + ship.position.y for section_x in range(int(start_x), int(end_x)): for section_y in range(int(start_y), int(end_y)): if x > 0 and y > 0 and ship.id % 4 == 0: section_values[x][y] += 10 elif x < 0 and y > 0 and ship.id % 4 == 1: section_values[x][y] += 10 elif x < 0 and y < 0 and ship.id % 4 == 2: section_values[x][y] += 10 elif x > 0 and y < 0 and ship.id % 4 == 3: section_values[x][y] += 10 section_values[x][y] += 1.0 * (game_map[Position( section_x, section_y)].halite_amount) / ( game_map.calculate_distance( ship.position, Position(section_x, section_y)) + 1)**(1.005**(500 - turns)) return section_values
def set_direction(map): pos = Position(random.randint(0, game_map.width - 1), random.randint(0, game_map.height - 1)) while (map[pos].halite_amount < 600): pos = Position(random.randint(0, game_map.width - 1), random.randint(0, game_map.height - 1)) return pos
def pathfinder(ship, end_position, collision_map): """ Lee Algorithm :param ship: the ship to move :param end_position: the position to find the path to :collision_map: map filled with FREE or OCCUPIED with future movements :return: a list with Directions to be followed """ dx = [-1, 0, 1, 0] # vectori de directie dy = [0, 1, 0, -1] if all(collision_map[ship.position.directional_offset(curr).x][ship.position.directional_offset(curr).y] == OCCUPIED \ for curr in [Direction.West, Direction.East, Direction.North, Direction.South]): # daca toate directiile invecinate sunt ocupate return [Direction.Still] if end_position == ship.position: # daca este deja pe pozitia indicata return [Direction.Still] q = Queue() path_matrix = np.full_like(collision_map, 0, dtype=int) # matrice Lee parents_matrix = np.full_like( collision_map, Position(0, 0), dtype=Position) # matrice reconstrcutie solutie q.put_nowait(ship.position) while not q.empty(): # Lee current = q.get_nowait() for direct in range(4): next_dir = Position( current.x + dx[direct], current.y + dy[direct], normalize=True) # normalize = sa faca wrap pe margini if next_dir == end_position and path_matrix[next_dir.x][ next_dir.y] == FREE: # sfarsit path_matrix[next_dir.x][next_dir.y] = path_matrix[current.x][ current.y] + 1 # marcheaza pozitia finala parents_matrix[next_dir.x][ next_dir. y] = current # marcheaza parintele pozitiei finale return traceback(end_position, path_matrix, parents_matrix, ship.position, ship) # reconstrcutie solutie if collision_map[next_dir.x][next_dir.y] == FREE and path_matrix[next_dir.x][next_dir.y] == 0\ and next_dir != ship.position: # daca nu are obstacol path_matrix[next_dir.x][next_dir.y] = path_matrix[current.x][ current.y] + 1 # incrementeaza distanta parents_matrix[next_dir.x][ next_dir.y] = current # parintele pt traceback q.put_nowait(next_dir) # adauga in coada
def get_best_dir(self, ship): directions = { Direction.North: 0, Direction.South: 0, Direction.East: 0, Direction.West: 0 } for row in range(-10, 10): for col in range(-10, 10): if col == 0 and row == 0: continue pos = Position(ship.position.x + col, ship.position.y + row) dist = self.map.calculate_distance(ship.position, pos) pull = self.map[pos].halite_amount / dist**2 if self.map[pos].inspired: pull *= (constants.INSPIRED_BONUS_MULTIPLIER + 1) for move in self.map.get_unsafe_moves(ship.position, pos): directions[move] += pull best_safe = None while len(directions): best_direction = max(directions.items(), key=operator.itemgetter(1))[0] directions.pop(best_direction) cell = self.map[ship.position.directional_offset(best_direction)] if cell.safe: best_safe = cell break if best_safe is None: raise ValueError('No safe adjacent positions!') return best_safe
def process_enemies(self): inspired = {} for player in self.game.players: if player is not self.game.my_id: for ship in self.game.players[player].get_ships(): # mark current enemy positions as unsafe self.map[ship].mark_unsafe() # find inspired positions for row in range(-constants.INSPIRATION_RADIUS, constants.INSPIRATION_RADIUS): for col in range(-constants.INSPIRATION_RADIUS, constants.INSPIRATION_RADIUS): x = ship.position.x + col y = ship.position.y + row if (x, y) in inspired: inspired[(x, y)] += 1 else: inspired[(x, y)] = 1 # marks these cells as being 'inspiring' to ships for x_y_tuple, ship_count in inspired.items(): if ship_count >= 2: position = Position(x_y_tuple[0], x_y_tuple[1]) self.map[position].mark_inspired()
def get_best_dir(self, ship): directions = { Direction.North: 0, Direction.South: 0, Direction.East: 0, Direction.West: 0 } for row in range(-10, 10): for col in range(-10, 10): pos = Position(ship.position.x + col, ship.position.y + row) dist = self.map.calculate_distance(ship.position, pos) for move in self.map.get_unsafe_moves(ship.position, pos): directions[move] += self.map[pos].halite_amount / dist**2 best_safe = None while len(directions): best_direction = max(directions.items(), key=operator.itemgetter(1))[0] directions.pop(best_direction) cell = self.map[ship.position.directional_offset(best_direction)] if cell.safe: best_safe = cell break return best_safe
def calculate_halite_remaining(self): total = 0 for row in range(self.map.height): for col in range(self.map.width): position = Position(row, col) total += self.map[position].halite_amount return total
def map_scan(): """ Scans the whole map for their halite amounts(resources) and returns the median of those values. """ halite_dict = {(x, y): game_map[Position(x, y)].halite_amount for y in range(0, game_map.height) for x in range(0, game_map.width)} return median(halite_dict.values())
def surrounding_halite(game, ship, size): cur_position = ship.position sur_positions = [ Position(cur_position.x + x, cur_position.y + y) for x in range(-size, size + 1) for y in range(-size, size + 1) ] sur_halites = [game.game_map[pos].halite_amount for pos in sur_positions] surrounding = [(sur_positions[i], sur_halites[i]) for i in range(len(sur_positions))] return surrounding
def add_target(self, ship: Ship, target: Optional[Union[Position, Tuple[int, int], List[Position]]]): self.targets[ship] = target if FLOG and target: p = Position(*(target[-1] if isinstance(target, list) else target)) self.f_log.append({ "t": self.game.turn_number, "x": p.x, "y": p.y, "color": "#FF0000" })
def set_direction(map): positions = [] halite = [] for i in range(10): pos = Position(random.randint(0, game_map.width - 1), random.randint(0, game_map.height - 1)) positions.append(pos) halite.append(map[pos].halite_amount) return positions[np.argmax(halite)]
def rm_surrounding(pos, ship): """ removes off positions surrounding the target and current ship's current position. :param pos: target position that should not be taken into consideration for the next ship. :param ship: ship position that should not be taken into consideration for the next ship. returns: returns list of target position, positions surrounding the target and ship's position """ temp = [unpack(pos)] + [unpack(game_map.normalize(pos + Position(*direction))) for direction in Direction.get_all_cardinals()] + \ [unpack(ship.position)] return temp
def get_map_info(game_map): width = game_map.width height = game_map.height halite_info = [[0 for j in range(height)] for i in range(width)] for i in range(width): for j in range(height): halite_info[i][j] = game_map[Position(i, j)].halite_amount total_halite = sum(sum(halite_info, [])) # max_halite = max(sum(halite_info,[])) # min_halite = min(sum(halite_info,[])) avg_halite = total_halite / (width * height) return halite_info, total_halite, avg_halite
def better_around(map, pos, dir): x = pos.x y = pos.y best_pos = dir best_val = 400 for x_i in range(-2, 2): for y_i in range(-2, 2): cur_pos = Position(x + x_i, y + y_i) cur_amount = map[cur_pos].halite_amount if (cur_amount > best_val): best_val = cur_amount best_pos = cur_pos return (best_val > 400), best_pos
def drop_custom_map(self, view): debug_map = self.bot.debug_maps.get(view, None) if debug_map is None: self.draw_halite() return max_value = debug_map.max() min_value = debug_map.min() it = np.nditer(debug_map, ['multi_index']) while not it.finished: halite = it[0] position = Position(*reversed(it.multi_index)) k = (max_value - min_value) or .1 pygame.draw.rect( self.screen, mul_tuple((255, 255, 255), (halite - min_value) / k, integer=True), pos2rect(position)) it.iternext()
def draw_selected(self): if isinstance(self.selected, Ship): debug_map = self.bot.debug_maps.get(self.selected.id, None) if debug_map is None: self.draw_halite() return max_value = debug_map.max() it = np.nditer(debug_map, ['multi_index']) while not it.finished: halite = it[0] position = Position(*reversed(it.multi_index)) pygame.draw.rect( self.screen, mul_tuple((255, 255, 255), halite / max_value, integer=True), pos2rect(position)) it.iternext()
def good_for_depo(game_map, me, ship): if not far_enough(game_map, me, ship): return False if me.halite_amount < 4100: return False x = ship.position.x y = ship.position.y halites = [] for x_i in range(-3, 3): for y_i in range(-3, 3): pos = Position(x + x_i, y + y_i) halites.append(game_map[pos].halite_amount) if np.mean(halites) > 250: return True return False
def max_dest(section_values, sections_exploring, width, height, shipyard_position): max = -1 max_x = 0 max_y = 0 for x in range(0, 8): for y in range(0, 8): if sections_exploring[x][y] == -1 and section_values[x][y] > max: max_x = x max_y = y max = section_values[x][y] ret_position = Position(max_x * width / 8 + random.randint(-4, 4), max_y * width / 8 + random.randint(-4, 4)) if ret_position.x == shipyard_position.x: ret_position.x += random.randint(1, 5) if ret_position.y == shipyard_position.y: ret_position.y += random.randint(1, 5) return ret_position, max_x, max_y
def free_ships_collided(ships, return_status, marked_map): """ Frees return_status for ships that got destroyed and frees map cells that were reserved for those destroyed ships :param ships: the list of ships :return_status: the dictionary formed of tuples ('Free'/'Collecting'/'Ending', Position()) :marked_map: map filled with cluster.FREE or cluster.RESERVED :return: the new return_status dictionary """ ship_exists = [False for _ in range(500)] for ship in ships: # marcheaza ship-urile care exista (dupa id unic global) ship_exists[ship.id] = True for i in range(500): if not ship_exists[i]: #daca nu exista if return_status[str(i)][0] != "Free": # o eliberam marked_map[return_status[str(i)][1].x][return_status[str( i)][1].y] = 0 # eliberam si harta return_status[str(i)] = ("Free", Position(0, 0)) return return_status
def dropoff_builder(self) -> Tuple[Optional[Position], Optional[Ship]]: GAUSS_SIGMA = 2. MIN_HALITE_PER_REGION = 16000 gmap = self.game.map bases = { self.game.me.shipyard.position, *(base.position for base in self.game.me.get_dropoffs()) } halite_ex = gmap.halite_extended.copy() h, w, d = gmap.height, gmap.width, 2 # Fill map border with zeros halite_ex[:d, 0:2 * w] = 0 halite_ex[-d:, 0:2 * w] = 0 halite_ex[0:2 * h, :d] = 0 halite_ex[0:2 * h, -d:] = 0 halite_ex_gb = gaussian_filter(halite_ex, sigma=GAUSS_SIGMA, truncate=2.0, mode='wrap') # Find contours of high halite deposits contours = msr.find_contours(halite_ex_gb, halite_ex_gb.max() * self.contour_k, fully_connected="high") masks = [ poly2mask(c[:, 0], c[:, 1], halite_ex.shape) for c in contours ] # Remove small halite chunks good_contours = [] for c, m in zip(contours, masks): if np.sum(np.multiply(gmap.halite_extended, m)) > MIN_HALITE_PER_REGION: good_contours.append(c) # Remove offset of contour points contours = [contour - h // 2 for contour in good_contours] # Set up possible dropoff points points = { tuple(float(round(x)) for x in point) for contour in contours for point in contour } points = [ list(point) for point in points if all(0 <= x < h for x in point) and Position(*map(int, point)) not in bases ] points = np.array(points, dtype=np.int) if points.shape[0] == 0: return None, None # Create potential field potential_field = np.zeros(gmap.halite.shape, dtype=np.float) for player in self.game.players.values(): me = player is self.game.me for ship in player.get_ships(): if me: potential_field[ship.position.y, ship.position.x] += self.dropoff_my_ship else: potential_field[ship.position.y, ship.position.x] += self.dropoff_enemy_ship for base in player.get_dropoffs(): if me: potential_field[base.position.y, base.position.x] += self.dropoff_my_base else: potential_field[base.position.y, base.position.x] += self.dropoff_enemy_base pos = player.shipyard.position potential_field[ pos.y, pos. x] += self.dropoff_my_base if me else self.dropoff_enemy_base potential_field = gaussian_filter(potential_field, sigma=self.potential_gauss_sigma, truncate=3.0, mode='wrap') if self.callbacks: self.debug_maps['dropoff'] = potential_field # Overlay points and potential_field potential_field_filtered = np.full(potential_field.shape, 0, dtype=np.float) x, y = points[:, 0], points[:, 1] potential_field_filtered[x, y] = potential_field[x, y] self.debug_maps['dropoff_2'] = potential_field_filtered y, x = np.unravel_index(potential_field_filtered.argmax(), potential_field_filtered.shape) value = potential_field_filtered[y, x] logging.debug(f"DROPOFF -> {value:.6f}") if value < self.dropoff_threshold: return None, None ships = self.game.me.get_ships() target = Position(x, y) distance = lambda ship: gmap.distance(ship.position, target) # ship_filter= lambda ship: ship.halite_amount / constants.MAX_HALITE < self.ship_fill_k * .75 ship = min(ships, key=distance) return target, ship
else: # exploring ships if (game_map[ship.position].halite_amount > constants.MAX_HALITE / greedy and not ship.is_full) \ or game_map[ship.position].halite_amount / 10 > ship.halite_amount: move = Direction.Still else: best_x = ship.position.x best_y = ship.position.y maxHLT = 0; search_len = 6 for i in range(-search_len, search_len + 1): for j in range(-search_len, search_len + 1): curr_x = ship.position.x + i curr_y = ship.position.y + j if game_map[Position(curr_x, curr_y)].halite_amount > maxHLT: maxHLT = game_map[Position(curr_x, curr_y)].halite_amount best_x = curr_x best_y = curr_y move = game_map.naive_navigate(ship, Position(best_x, best_y)) # if ship.position == me.shipyard.position: # move = random.choice(Direction.get_all_cardinals()) for current_direction in Direction.get_all_cardinals(): pos = ship.position.directional_offset(current_direction) if game_map[pos].halite_amount > constants.MAX_HALITE / greedy: move = current_direction if stuck_cnt[ship.id] > 5: move = random.choice(Direction.get_all_cardinals())
def loop(self): me = self.game.me my_ships = me.get_ships() gmap = self.game.map home = self.game.me.shipyard.position moves: Dict[Ship, Iterable[Tuple[int, int]]] = {} bases = {home, *(base.position for base in me.get_dropoffs())} # Check whether new dropoff can be created or not if self.building_dropoff is None: if 10 < self.game.turn_number < constants.MAX_TURNS * self.dropoff_spawn_stop_turn: dropoff_position, dropoff_ship = self.dropoff_builder() else: dropoff_position, dropoff_ship = None, None else: dropoff_position, dropoff_ship = self.building_dropoff if not dropoff_ship.exists: dropoff_position, dropoff_ship = None, None self.building_dropoff = None if dropoff_ship is not None and dropoff_position is not None: if me.halite_amount > constants.DROPOFF_COST: if dropoff_ship.position == dropoff_position: # If ship in place wait until we have enough halite to build dropoff if me.halite_amount + dropoff_ship.halite_amount > constants.DROPOFF_COST: yield dropoff_ship.make_dropoff() me.halite_amount -= constants.DROPOFF_COST - dropoff_ship.halite_amount logging.info( f"Building dropoff {dropoff_position} from Ship#{dropoff_ship.id}" ) self.building_dropoff = None else: yield dropoff_ship.stay_still() else: # Move to new dropoff position self.ship_manager.add_target(dropoff_ship, dropoff_position) self.building_dropoff = dropoff_position, dropoff_ship logging.info( f"Ship#{dropoff_ship.id} moving to dropoff position {dropoff_position}" ) path = gmap.a_star_path_search(dropoff_ship.position, dropoff_position) moves[dropoff_ship] = ( gmap.normalize_direction(path[0] - dropoff_ship.position), ) else: yield dropoff_ship.stay_still() # General mask # Contains: # * enemy ship penalty # * penalty for cells around enemy ships (4 directions) # * friendly dynamic penalty based on ship cargo / cell halite ratio self.mask.fill(1.) for player in self.game.players.values(): for ship in player.get_ships(): if player is not me: self.mask[ship.position.y, ship.position.x] *= self.enemy_ship_penalty for dir in Direction.All: pos = gmap.normalize(ship.position + dir) self.mask[pos.y, pos.x] *= self.enemy_ship_nearby_penalty else: self.mask[ ship.position.y, ship.position.x] *= ship_collecting_halite_coefficient( ship, gmap) if not self.fast_mode: self.blurred_halite = gaussian_filter(gmap.halite, sigma=2, truncate=2.0, mode='wrap') gbh_min, gbh_max = self.blurred_halite.min( ), self.blurred_halite.max() self.blurred_halite -= gbh_min self.blurred_halite /= gbh_max - gbh_min if self.callbacks: self.debug_maps["gbh_norm"] = self.blurred_halite self.filtered_halite *= self.blurred_halite / 2 + .5 max_halite: float = gmap.halite.max() self.filtered_halite[:, :] = gmap.halite[:, :] self.filtered_halite[self.filtered_halite < max_halite * self.halite_threshold] = 0 self.filtered_halite *= self.mask # Generate moves for ships from mask and halite field for ship in reversed(self.ship_manager.ships): if ship == dropoff_ship: continue # Ship unloading to_home = ship in self.ships_to_home if to_home and ship.position in bases: logging.info(f"Ship#{ship.id} unload") self.ships_to_home.remove(ship) to_home = False if to_home or ship.halite_amount >= constants.MAX_HALITE * self.ship_fill_k: logging.info(f"Ship#{ship.id} moving home") self.ships_to_home.add(ship) ship_target = min( bases, key=lambda base: gmap.distance(ship.position, base)) path = gmap.a_star_path_search(ship.position, ship_target) self.ship_manager.add_target(ship, path) moves[ship] = (gmap.normalize_direction(path[0] - ship.position), ) # Ship has too low halite to move elif gmap[ ship].halite_amount / constants.MOVE_COST_RATIO > ship.halite_amount: yield ship.stay_still() continue else: self.per_ship_mask.fill(1.0) for another_ship, target in self.ship_manager.targets.items(): if isinstance(target, list): target = target[0] if another_ship is ship or target is None or target == home: continue self.per_ship_mask[target[1], target[0]] *= self.same_target_penalty self.weighted_halite[:, :] = self.filtered_halite[:, :] # Apply masks distances = np.roll(self.distances, ship.position.y, axis=0) distances = np.roll(distances, ship.position.x, axis=1) self.weighted_halite /= distances self.weighted_halite *= self.per_ship_mask self.weighted_halite[ ship.position.y, ship.position.x] /= ship_collecting_halite_coefficient( ship, gmap) if self.callbacks: self.debug_maps[ship.id] = self.weighted_halite.copy() # Found max point y, x = np.unravel_index(self.weighted_halite.argmax(), self.weighted_halite.shape) max_halite = self.weighted_halite[y, x] if max_halite > 0.1: target = Position(x, y) if target is dropoff_position: continue if target == ship.position: logging.info(f"Ship#{ship.id} collecting halite") self.ship_manager.add_target(ship, None) yield ship.stay_still() else: logging.info( f"Ship#{ship.id} {ship.position} moving towards {target}" ) self.ship_manager.add_target(ship, target) moves[ship] = list( self.game.map.get_unsafe_moves( ship.position, target)) else: logging.info( f"Ship#{ship.id} does not found good halite deposit") self.ship_manager.add_target(ship, None) yield ship.stay_still() # Resolve moves into commands in 2 iterations moves: List[Tuple[Ship, Iterable[Tuple[int, int]]]] = list(moves.items()) for i in range(2): commands, moves = self.resolve_moves(moves) yield from commands # Remaining ship has not any safe moves for ship, d in moves: logging.debug(f"Unsafe move: {ship} -> {d}") yield ship.stay_still() # Max ships: base at 32 map size, 2*base at 64 map size if ((dropoff_position is None or me.halite_amount >= constants.SHIP_COST + constants.DROPOFF_COST) and self.game.turn_number <= constants.MAX_TURNS * self.ship_turns_stop and me.halite_amount >= constants.SHIP_COST and not gmap[me.shipyard].is_occupied and self.max_ships_reached <= 2): if len(my_ships) < self.ship_limit: yield me.shipyard.spawn() else: self.max_ships_reached += 1
def test_collect_halite(self): print(nav.collect_halite(game_map, Position(0, 0))) self.assertEqual('foo'.upper(), 'FOO')
def loop(self): me = self.game.me my_ships = me.get_ships() gmap = self.game.map total_halite = gmap.total_halite home = self.game.me.shipyard.position moves: Dict[Ship, Iterable[Tuple[int, int]]] = {} bases = {home, *(base.position for base in me.get_dropoffs())} # Check whether new dropoff can be created or not if self.building_dropoff is None: if 10 < self.game.turn_number < constants.MAX_TURNS * self.dropoff_spawn_stop_turn: dropoff_position, dropoff_ship = self.dropoff_builder() else: dropoff_position, dropoff_ship = None, None else: dropoff_position, dropoff_ship = self.building_dropoff if not dropoff_ship.exists: dropoff_position, dropoff_ship = None, None self.building_dropoff = None if dropoff_ship is not None and dropoff_position is not None: if dropoff_ship.position == dropoff_position: # If ship in place wait until we have enough halite to build dropoff if me.halite_amount + dropoff_ship.halite_amount > constants.DROPOFF_COST: yield dropoff_ship.make_dropoff() me.halite_amount -= constants.DROPOFF_COST - dropoff_ship.halite_amount logging.info( f"Building dropoff {dropoff_position} from Ship#{dropoff_ship.id}" ) self.building_dropoff = None else: yield dropoff_ship.stay_still() else: # Move to new dropoff position self.ship_manager.add_target(dropoff_ship, dropoff_position) self.building_dropoff = dropoff_position, dropoff_ship logging.info( f"Ship#{dropoff_ship.id} moving to dropoff position {dropoff_position}" ) path = gmap.a_star_path_search(dropoff_ship.position, dropoff_position) moves[dropoff_ship] = ( gmap.normalize_direction(path[0] - dropoff_ship.position), ) # Ships mask # Contains: # * enemy ship penalty # * penalty for cells around enemy ships (4 directions) # * inspiration bonus based on halite estimation of player self.ships_mask.fill(0.) players_halite: Dict[Player, int] = { p: p.halite_estimate for p in self.game.players.values() } players_sorted: List[Tuple[Player, int]] = sorted(players_halite.items(), key=operator.itemgetter(1), reverse=True) my_halite = players_halite[me] top_player_warning = players_sorted[0][ 0] is not me and players_sorted[0][1] - my_halite > 5000 enable_inspiration = len(players_sorted) > 2 for i, (player, _) in enumerate(players_sorted): if player is me: continue for ship in player.get_ships(): self.ships_mask += roll2d(self.inspiration_mask, *ship.position) # k := 1 | 2 if enable_inspiration: k = min(i + 1 if i else 1, 2) if top_player_warning else 2 else: k = 1 np.clip(self.ships_mask, -np.inf, k, out=self.ships_mask) # Replace mask pseudo value with real coefficients self.ships_mask[self.ships_mask < -300.0] = self.enemy_ship_penalty self.ships_mask[self.ships_mask < 0.0] = self.enemy_ship_penalty * 2 self.ships_mask[self.ships_mask == 0.0] = 1.0 self.ships_mask[self.ships_mask == 2.0] = self.inspiration_bonus np.clip(self.ships_mask, self.enemy_ship_penalty, self.inspiration_bonus, out=self.ships_mask) if self.callbacks: self.debug_maps["ships_mask"] = self.ships_mask # If we have time - apply blurred mask if not self.fast_mode: gaussian_filter(gmap.halite, sigma=2, truncate=2.0, mode='wrap', output=self.blurred_halite) gbh_min, gbh_max = self.blurred_halite.min( ), self.blurred_halite.max() self.blurred_halite -= gbh_min if gbh_max - gbh_min != 0.0: self.blurred_halite /= gbh_max - gbh_min if self.callbacks: self.debug_maps["gbh_norm"] = self.blurred_halite self.filtered_halite *= self.blurred_halite / 2 + .5 # Filter halite: # * reduce halite spikes after ships collide # * remove everything that is less than self.halite_threshold self.filtered_halite[:, :] = gmap.halite[:, :] self.filtered_halite[self.filtered_halite < self.halite_threshold] = 0 if len(players_sorted) > 2: ix = self.filtered_halite > (self.filtered_halite.mean()**2) self.filtered_halite[ix] = (self.filtered_halite[ix]**0.5) * 2 if len(self.game.players) == 2: enemy_2p = [p for p in self.game.players.values() if p is not me][0] ram_enabled = len(me.get_ships()) > len(enemy_2p.get_ships()) + 10 else: ram_enabled = False ram_enabled = ram_enabled or total_halite / gmap.initial_halite <= 0.13 # Generate moves for ships from masks and halite field for ship in reversed(self.ship_manager.ships): if ship == dropoff_ship: continue # Ship unloading to_home = ship in self.ships_to_home if to_home and ship.position in bases: logging.info(f"Ship#{ship.id} unload") self.ships_to_home.remove(ship) to_home = False if to_home or ship.halite_amount >= constants.MAX_HALITE * self.ship_fill_k: logging.info(f"Ship#{ship.id} moving home") self.ships_to_home.add(ship) ship_target = min( bases, key=lambda base: gmap.distance(ship.position, base)) path = gmap.a_star_path_search(ship.position, ship_target, my_id=me.id) self.ship_manager.add_target(ship, path) moves[ship] = (gmap.normalize_direction(path[0] - ship.position), ) # Ship has too low halite to move elif gmap[ ship].halite_amount / constants.MOVE_COST_RATIO > ship.halite_amount: yield ship.stay_still() continue else: if ram_enabled: # Ram enemy ship with a lot of halite for direction in Direction.All: other_ship = gmap[ship.position + direction].ship if not other_ship or other_ship.owner == me.id: continue if other_ship.halite_amount / (ship.halite_amount + 1) >= 3: target_found = True break else: target_found = False if target_found: # noinspection PyUnboundLocalVariable logging.info( f"Ship#{ship.id} ramming enemy ship #{other_ship.id}" f" ({ship.halite_amount}) vs ({other_ship.halite_amount})" ) # noinspection PyUnboundLocalVariable yield gmap.update_ship_position(ship, direction) continue self.per_ship_mask.fill(1.0) for another_ship, target in self.ship_manager.targets.items(): if isinstance(target, list): target = target[0] if another_ship is ship or target is None or target == home: continue self.per_ship_mask[target[1], target[0]] *= self.same_target_penalty self.weighted_halite[:, :] = self.filtered_halite[:, :] # Ship mask is filtered by distance distances = roll2d(self.distances, *ship.position) ships_mask = np.copy(self.ships_mask) ix = distances > self.inspiration_track_radius ships_mask[ix] = np.clip(ships_mask[ix], 0, 1) # Apply masks self.weighted_halite *= ships_mask self.weighted_halite *= self.per_ship_mask self.weighted_halite /= (distances + 1)**self.distance_penalty_k # self.weighted_halite[ship.position.y, ship.position.x] /= ship_collecting_halite_coefficient(ship, gmap) self.weighted_halite[ship.position.y, ship.position.x] *= self.stay_still_bonus if self.callbacks: self.debug_maps[ship.id] = self.weighted_halite.copy() # Found max point y, x = np.unravel_index(self.weighted_halite.argmax(), self.weighted_halite.shape) max_halite = self.weighted_halite[y, x] if max_halite > 0: target = Position(x, y) if target is dropoff_position: continue if target == ship.position: logging.info(f"Ship#{ship.id} collecting halite") self.ship_manager.add_target(ship, None) yield ship.stay_still() else: logging.info( f"Ship#{ship.id} {ship.position} moving towards {target}" ) self.ship_manager.add_target(ship, target) moves[ship] = list( self.game.map.get_unsafe_moves( ship.position, target)) else: logging.info( f"Ship#{ship.id} does not found good halite deposit") self.ship_manager.add_target(ship, None) yield ship.stay_still() moves: List[Tuple[Ship, Iterable[Tuple[int, int]]]] = list(moves.items()) yield from self.ship_manager.resolve_moves(moves) # Max ships: base at 32 map size, 2*base at 64 map size shipyard_cell = gmap[me.shipyard] if ((self.building_dropoff is None and me.halite_amount >= constants.SHIP_COST or self.building_dropoff is not None and all(self.building_dropoff) and me.halite_amount >= constants.SHIP_COST + constants.DROPOFF_COST) and self.max_ships_reached <= 3 and (shipyard_cell.ship is None or shipyard_cell.ship.owner != me.id)): if (total_halite / gmap.initial_halite >= .25 and self.game.turn_number <= constants.MAX_TURNS * self.ship_turns_stop or total_halite / gmap.initial_halite >= .57 and self.game.turn_number <= constants.MAX_TURNS * (1 - (1 - self.ship_turns_stop) / 1.5)): if len(my_ships) < self.ship_limit: yield me.shipyard.spawn() else: self.max_ships_reached += 1
command_queue.append(ship.stay_still()) placepicked.append(ship.position) else: command_queue.append(ship.move(game_map.naive_navigate(ship, Shipyard))) placepicked.append(placeVal) else: command_queue.append(ship.stay_still()) placepicked.append(ship.position) #game_map[ship.position].mark_unsafe(ship) Shipyard = Position(8, 16) """ <<<Game Loop>>> """ while True: # This loop handles each turn of the game. The game object changes every turn, and you refresh that state by # running update_frame(). game.update_frame() # You extract player metadata and the updated map metadata here for convenience. me = game.me game_map = game.game_map # 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 = [] placepicked = [] placepicked.clear()
def loop(self): me = self.game.me my_ships = me.get_ships() gmap = self.game.map home = self.game.me.shipyard.position # General mask # Contains: # * enemy ship penalty # * penalty for cells around enemy ships (4 directions) # * friendly dynamic penalty based on ship cargo / cell halite ratio self.mask.fill(1.) for player in self.game.players.values(): for ship in player.get_ships(): if player is not me: self.mask[ship.position.y][ ship.position.x] *= self.enemy_ship_penalty for dir in Direction.All: pos = gmap.normalize(ship.position + dir) self.mask[pos.y][ pos.x] *= self.enemy_ship_nearby_penalty else: self.mask[ship.position.y][ ship.position.x] *= ship_collecting_halite_coefficient( ship, gmap) if not self.fast_mode: for y, row in enumerate(gmap.cells): for x, cell in enumerate(row): self.blurred_halite[y][x] = cell.halite_amount self.blurred_halite = gaussian_filter(self.blurred_halite, sigma=2, truncate=2.0, mode='wrap') gbh_min, gbh_max = self.blurred_halite.min( ), self.blurred_halite.max() self.blurred_halite -= gbh_min self.blurred_halite /= gbh_max - gbh_min if self.callbacks: self.debug_maps["gbh_norm"] = self.blurred_halite # Generate moves for ships from mask and halite field moves: List[Tuple[Ship, Iterable[Tuple[int, int]]]] = [] for ship in my_ships: # Ship unloading to_home = ship in self.ships_to_home if to_home and ship.position == home: logging.info(f"Ship#{ship.id} unload") self.ships_to_home.remove(ship) to_home = False if to_home or ship.halite_amount >= constants.MAX_HALITE * self.ship_fill_k: logging.info(f"Ship#{ship.id} moving home") self.ships_to_home.add(ship) self.ships_targets[ship] = path = gmap.a_star_path_search( ship.position, home) moves.append( (ship, (gmap.normalize_direction(path[0] - ship.position), ))) else: self.per_ship_mask.fill(1.0) for another_ship, target in self.ships_targets.items(): if isinstance(target, list): target = target[0] if another_ship is ship or target is None or target == home: continue self.per_ship_mask[target[1]][ target[0]] *= self.same_target_penalty # Ship has too low halite to move if gmap[ship].halite_amount / constants.MOVE_COST_RATIO > ship.halite_amount: # self.ships_targets[ship] = None yield ship.stay_still() continue position = None # if gmap.width // 2 - 1 > self.lookup_radius: # field = ship.position.get_surrounding_cardinals(self.lookup_radius, center=True) # else: for coord in map(operator.attrgetter('position'), gmap): self.weights[coord.y][coord.x] = gmap[coord].halite_amount # Apply masks if not self.fast_mode: self.weights *= self.blurred_halite / 2 + .5 self.weights *= self.mask self.weights *= self.per_ship_mask it = np.nditer(self.weights, ['multi_index'], ['readwrite']) while not it.finished: y, x = it.multi_index distance = gmap.distance(ship.position, (x, y)) if distance: # Distance penalty # TODO: Experiment with A* algorithm it[0] /= (distance + 1)**self.distance_penalty_k else: # Revert "same point penalty" of current ship it[0] /= ship_collecting_halite_coefficient(ship, gmap) it.iternext() if self.callbacks: self.debug_maps[ship.id] = self.weights.copy() # Found max point y, x = np.unravel_index(self.weights.argmax(), self.weights.shape) max_halite: Tuple[Position, int] = (Position(x, y), self.weights[y][x]) if max_halite[1] > 0.1: position = max_halite[0] if position: if position == ship.position: logging.info(f"Ship#{ship.id} collecting halite") self.ships_targets[ship] = None yield ship.stay_still() else: logging.info( f"Ship#{ship.id} {ship.position} moving towards {position}" ) self.ships_targets[ship] = position moves.append((ship, list( self.game.map.get_unsafe_moves( ship.position, position)))) else: logging.info( f"Ship#{ship.id} does not found good halite deposit") self.ships_targets[ship] = None yield ship.stay_still() # Resolve moves into commands in 2 iterations for i in range(2): commands, moves = self.resolve_moves(moves) yield from commands # Remaining ship has not any safe moves for ship, d in moves: logging.debug(f"Unsafe move: {ship} -> {d}") yield ship.stay_still() # Max ships: base at 32 map size, 2*base at 64 map size if (self.game.turn_number <= constants.MAX_TURNS * self.ship_turns_stop and me.halite_amount >= constants.SHIP_COST and not gmap[me.shipyard].is_occupied and self.max_ships_reached <= 2): if len(my_ships) < self.ship_limit: yield me.shipyard.spawn() else: self.max_ships_reached += 1
if index % 10 == 0: MEDIAN = map_scan() if game.turn_number < rush: if ship_status[ ship.id][0] == explore and ship.id not in commanded_ships: max_halite_cord = target_positions[ship.id] if game_map[ship.position].halite_amount > MEDIAN * .7: move = Direction.Still commands.append(ship.move(move)) commanded_ships[ship.id] = index else: move = Navigator(ship, Position(*max_halite_cord), index) ship_status[ship.id][1] = ship.position.directional_offset( move) commands.append(ship.move(move)) commanded_ships[ship.id] = index if ship_status[ ship.id][0] == ret and ship.id not in commanded_ships: if ship.position == me.shipyard.position: ship_status[ship.id][0] = explore else: gain = calculate_gain(ship) if gain > threshold or ship.is_full: