def _compute_min_evade_step(obs, parent_pos_list, pos, bomb_real_life): flag_cover, min_cover_value, max_cover_value = _position_covered_by_bomb(obs, pos, bomb_real_life) if not flag_cover: return 0 elif len(parent_pos_list) >= max_cover_value: if len(parent_pos_list) > max_cover_value + FLAME_LIFE: return 0 else: return INT_MAX elif len(parent_pos_list) >= min_cover_value: if len(parent_pos_list) > min_cover_value + FLAME_LIFE: return 0 else: return INT_MAX else: board = obs['board'] dirs = _all_directions(exclude_stop=True) min_step = INT_MAX for d in dirs: next_pos = utility.get_next_position(pos, d) if not utility.position_on_board(board, next_pos): continue if not (utility.position_is_passage(board, next_pos) or \ utility.position_is_powerup(board, next_pos)): continue if next_pos in parent_pos_list: continue x = _compute_min_evade_step(obs, parent_pos_list + [next_pos], next_pos, bomb_real_life) min_step = min(min_step, x + 1) return min_step
def position_can_be_bomb_through(board, position): if utility.position_is_flames(board, position): return True if utility.position_is_passage(board, position): return True if utility.position_is_powerup(board, position): return True return False
def position_is_bombable(board, position, bombs): return any([ utility.position_is_agent(board, position), utility.position_is_powerup(board, position), utility.position_is_passage(board, position), position_is_flame(board, position), position_is_bomb(bombs, position) ])
def get_power_up(obs): res = [0] * 6 my_position = obs['position'] board = obs['board'] for act in dirs: next_pos = util.get_next_position(my_position, act) if util.position_on_board( board, next_pos) and util.position_is_powerup(board, next_pos): res[act.value] = 1 return res
def position_is_passable(board, position, enemies): '''Determins if a possible can be passed''' return all([ any([ utility.position_is_agent(board, position), utility.position_is_powerup(board, position), utility.position_is_passage(board, position), utility.position_is_fog(board, position), ]), not utility.position_is_enemy(board, position, enemies) ])
def moving_bomb_check(moving_bomb_pos, p_dir, time_elapsed): pos2 = utility.get_next_position(moving_bomb_pos, p_dir) dist = 0 for i in range(10): dist += 1 if not utility.position_on_board(board, pos2): break if not (utility.position_is_powerup(board, pos2) or utility.position_is_passage(board, pos2)): break life_now = bomb_life[pos2] - time_elapsed if bomb_life[pos2] > 0 and life_now >= -2 and life_now <= 0 and dist < blast_st[pos2]: return False pos2 = utility.get_next_position(pos2, p_dir) return True
def _all_bomb_real_life(board, bomb_life, bomb_blast_st): def get_bomb_real_life(bomb_position, bomb_real_life): """One bomb's real life is the minimum life of its adjacent bomb. Not that this could be chained, so please call it on each bomb mulitple times until converge """ dirs = _all_directions(exclude_stop=True) min_life = bomb_real_life[bomb_position] for d in dirs: pos = bomb_position last_pos = bomb_position while True: pos = utility.get_next_position(pos, d) if _stop_condition(board, pos): break if bomb_real_life[pos] > 0: if bomb_real_life[pos] < min_life and \ _manhattan_distance(pos, last_pos) <= bomb_blast_st[pos] - 1: min_life = bomb_real_life[pos] last_pos = pos else: break return min_life bomb_real_life_map = np.copy(bomb_life) sz = len(board) while True: no_change = [] for i in range(sz): for j in range(sz): if utility.position_is_wall(board, (i, j)) or utility.position_is_powerup(board, (i, j)) \ or utility.position_is_fog(board, (i, j)): continue if bomb_life[i, j] < 0 + EPSILON: continue real_life = get_bomb_real_life((i, j), bomb_real_life_map) no_change.append(bomb_real_life_map[i, j] == real_life) bomb_real_life_map[i, j] = real_life if all(no_change): break return bomb_real_life_map
def _compute_safe_actions(obs, exclude_kicking=False, prev_two_obs=(None, None)): dirs = _all_directions(exclude_stop=True) ret = set() my_position, board, blast_st, bomb_life, can_kick = obs['position'], obs['board'], obs['bomb_blast_strength'], obs[ 'bomb_life'], obs['can_kick'] kick_dir = None bomb_real_life_map = _all_bomb_real_life(board, bomb_life, blast_st) flag_cover_passages = [] for direction in dirs: position = utility.get_next_position(my_position, direction) if not utility.position_on_board(board, position): continue if (not exclude_kicking) and utility.position_in_items(board, position, [constants.Item.Bomb]) and can_kick: # filter kick if kick is unsafe if _kick_test(board, blast_st, bomb_real_life_map, my_position, direction): ret.add(direction.value) kick_dir = direction.value gone_flame_pos = None if (prev_two_obs[0] != None and prev_two_obs[1] != None) and _check_if_flame_will_gone(obs, prev_two_obs, position): # three consecutive flames means next step this position must be good # make this a candidate obs['board'][position] = constants.Item.Passage.value gone_flame_pos = position if utility.position_is_passage(board, position) or utility.position_is_powerup(board, position): my_id = obs['board'][my_position] obs['board'][my_position] = constants.Item.Bomb.value if bomb_life[ my_position] > 0 else constants.Item.Passage.value flag_cover, min_cover_value, max_cover_value = _position_covered_by_bomb(obs, position, bomb_real_life_map) flag_cover_passages.append(flag_cover) if not flag_cover: ret.add(direction.value) else: min_escape_step = _compute_min_evade_step(obs, [position], position, bomb_real_life_map) assert (min_escape_step > 0) if min_escape_step < min_cover_value: ret.add(direction.value) obs['board'][my_position] = my_id if gone_flame_pos is not None: obs['board'][gone_flame_pos] = constants.Item.Flames.value # Test Stop action only when agent is covered by bomb, # otherwise Stop is always an viable option my_id = obs['board'][my_position] obs['board'][my_position] = constants.Item.Bomb.value if bomb_life[ my_position] > 0 else constants.Item.Passage.value # REMEMBER: before compute min evade step, modify board accordingly first.. flag_cover, min_cover_value, max_cover_value = _position_covered_by_bomb(obs, my_position, bomb_real_life_map) if flag_cover: min_escape_step = _compute_min_evade_step(obs, [None, my_position], my_position, bomb_real_life_map) if min_escape_step < min_cover_value: ret.add(constants.Action.Stop.value) else: ret.add(constants.Action.Stop.value) obs['board'][my_position] = my_id # REMEBER: change the board back # Now test bomb action if not (obs['ammo'] <= 0 or obs['bomb_life'][obs['position']] > 0): # place bomb might be possible assert (BOMBING_TEST in ['simple', 'simple_adjacent', 'lookahead']) if BOMBING_TEST == 'simple': if not flag_cover: ret.add(constants.Action.Bomb.value) elif BOMBING_TEST == 'simple_adjacent': if (not flag_cover) and (not any(flag_cover_passages)): ret.add(constants.Action.Bomb.value) else: # lookahead if (constants.Action.Stop.value in ret) and len(ret) > 1 and (kick_dir is None): obs2 = copy.deepcopy(obs) my_pos = obs2['position'] obs2['board'][my_pos] = constants.Item.Bomb.value obs2['bomb_life'][my_pos] = min_cover_value if flag_cover else 10 obs2['bomb_blast_strength'][my_pos] = obs2['blast_strength'] bomb_life2, bomb_blast_st2, board2 = obs2['bomb_life'], obs2['bomb_blast_strength'], obs2['board'] bomb_real_life_map = _all_bomb_real_life(board2, bomb_life2, bomb_blast_st2) min_evade_step = _compute_min_evade_step(obs2, [None, my_position], my_pos, bomb_real_life_map) current_cover_value = obs2['bomb_life'][my_pos] if min_evade_step < current_cover_value: ret.add(constants.Action.Bomb.value) return ret
def _board_sequence(self, board, bombs, flames, length, my_position, my_action=None, can_kick=False, enemy_mobility=3): """ Simulate the sequence of boards, assuming agents stay unmoved Parameters ---------- board : array initial board bombs : list list of initial bombs flames : list list of initial flames length : int length of the board sequence to simulate my_position : tuple position of my agent my_action : Action, optional my action at the first step can_kick : boolean, optional whether I can kick enemy_mobility : int, optional number of steps where enemies move nondeterministically Return ------ list_boards : list list of boards """ # Forward model to simulate model = ForwardModel() # Prepare initial state _board = board.copy() _bombs = deepcopy(bombs) _flames = deepcopy(flames) _items = dict() # we never know hidden items _actions = [constants.Action.Stop.value] * 4 if my_action is not None: agent = characters.Bomber() agent.agent_id = board[my_position] - 10 agent.position = my_position agent.can_kick = can_kick _agents = [agent] _actions[agent.agent_id] = my_action else: _agents = list() my_next_position = None # Get enemy positions to take into account their mobility rows, cols = np.where(_board > constants.Item.AgentDummy.value) enemy_positions = [position for position in zip(rows, cols) if position != my_position] # List of enemies enemies = list() for position in enemy_positions: agent = characters.Bomber() agent.agent_id = board[position] - 10 agent.position = position enemies.append(agent) _agents = _agents + enemies # Overwrite bomb over agent if they overlap for bomb in _bombs: _board[bomb.position] = constants.Item.Bomb.value # Simulate list_boards = [_board.copy()] for t in range(length): # Standard simulation step _board, _agents, _bombs, _, _flames \ = model.step(_actions, _board, _agents, _bombs, _items, _flames) # Overwrite passage over my agent when it has moved to a passage if t == 0 and len(_agents) > 0: agent = _agents[0] my_next_position = agent.position if all([agent.position != my_position, _board[agent.position] != constants.Item.Flames.value, _board[agent.position] != constants.Item.Bomb.value]): # I did not die and did not stay on a bomb _board[agent.position] = constants.Item.Passage.value # Overwrite bomb over agent if they overlap for bomb in _bombs: _board[bomb.position] = constants.Item.Bomb.value # Take into account the nondeterministic mobility of enemies if t < enemy_mobility: _enemy_positions = set() for x, y in enemy_positions: # for each enemy position in the previous step for dx, dy in [(0, 0), (1, 0), (-1, 0), (0, 1), (0, -1)]: # consider the next possible position next_position = (x + dx, y + dy) if not self._on_board(next_position): # ignore if out of board continue if any([utility.position_is_passage(_board, next_position), utility.position_is_powerup(_board, next_position), (next_position == my_position and utility.position_is_agent(_board, next_position) )]): # possible as a next position # TODO : what to do with my position _enemy_positions.add(next_position) _board[next_position] = constants.Item.AgentDummy.value enemy_positions = _enemy_positions _actions = [constants.Action.Stop.value] * 4 _agents = enemies list_boards.append(_board.copy()) return list_boards, my_next_position
def position_is_passable(board, pos, enemies=[]): #hard code the smallest agent id on board if board[pos] >= 10: return False return utility.position_is_powerup( board, pos) or utility.position_is_passage(board, pos)
def act(self, obs, action_space, info): # # Definitions # board = obs['board'] recently_seen_positions = (info["since_last_seen"] < 3) board[recently_seen_positions] = info["last_seen"][recently_seen_positions] my_position = obs["position"] # tuple([x,y]): my position my_ammo = obs['ammo'] # int: the number of bombs I have my_blast_strength = obs['blast_strength'] my_enemies = [constants.Item(e) for e in obs['enemies'] if e != constants.Item.AgentDummy] if obs["teammate"] != constants.Item.AgentDummy: my_teammate = obs["teammate"] else: my_teammate = None my_kick = obs["can_kick"] # whether I can kick if verbose: print("my position", my_position, "ammo", my_ammo, "blast", my_blast_strength, "kick", my_kick, end="\t") my_next_position = {constants.Action.Stop: my_position, constants.Action.Bomb: my_position} for action in [constants.Action.Up, constants.Action.Down, constants.Action.Left, constants.Action.Right]: next_position = self._get_next_position(my_position, action) if self._on_board(next_position): if board[next_position] in [constants.Item.Rigid.value, constants.Item.Wood.value]: my_next_position[action] = None else: my_next_position[action] = next_position else: my_next_position[action] = None # # Understand current situation # if all([info["prev_action"] not in [constants.Action.Stop, constants.Action.Bomb], info["prev_position"] == my_position]): # if previously blocked, do not reapeat with some probability self._inv_tmp *= self._backoff else: self._inv_tmp = self._inv_tmp_init # enemy positions enemy_positions = list() for enemy in my_enemies: rows, cols = np.where(board==enemy.value) if len(rows) == 0: continue enemy_positions.append((rows[0], cols[0])) # teammate position teammate_position = None if my_teammate is not None: rows, cols = np.where(board==my_teammate.value) if len(rows): teammate_position = (rows[0], cols[0]) # where to place bombs to break wood digging, bomb_target = self._get_digging_positions(board, my_position, info) if digging is None: bomb_target, n_breakable \ = self._get_bomb_target(info["list_boards_no_move"][-1], my_position, my_blast_strength, constants.Item.Wood) # Positions where we kick a bomb if we move to if my_kick: kickable, might_kickable = self._kickable_positions(obs, info["moving_direction"]) else: kickable = set() might_kickable = set() # positions that might be blocked if teammate_position is None: agent_positions = enemy_positions else: agent_positions = enemy_positions + [teammate_position] might_blocked = self._get_might_blocked(board, my_position, agent_positions, might_kickable) # enemy positions over time # these might be dissappeared due to extra flames if len(enemy_positions): rows = [p[0] for p in enemy_positions] cols = [p[1] for p in enemy_positions] list_enemy_positions = [(rows, cols)] _enemy_positions = list() for t in range(self._enemy_mobility): rows, cols = list_enemy_positions[-1] for x, y in zip(rows, cols): for dx, dy in [(1, 0), (-1, 0), (0, 1), (0, -1)]: next_position = (x + dx, y + dy) if not self._on_board(next_position): continue _board = info["list_boards_no_move"][t] if any([utility.position_is_passage(_board, next_position), utility.position_is_powerup(_board, next_position)]): _enemy_positions.append(next_position) _enemy_positions = set(_enemy_positions) rows = [p[0] for p in _enemy_positions] cols = [p[1] for p in _enemy_positions] list_enemy_positions.append((rows, cols)) else: list_enemy_positions = [] # survivable actions is_survivable = dict() for a in self._get_all_actions(): is_survivable[a] = False n_survivable = dict() list_boards = dict() for my_action in self._get_all_actions(): next_position = my_next_position[my_action] if next_position is None: continue if my_action == constants.Action.Bomb: if any([my_ammo == 0, obs["bomb_blast_strength"][next_position] > 0]): continue if all([utility.position_is_flames(board, next_position), info["flame_life"][next_position] > 1]): continue if all([my_action != constants.Action.Stop, obs["bomb_blast_strength"][next_position] > 0, next_position not in set.union(kickable, might_kickable)]): continue if next_position in set.union(kickable, might_kickable): # do not kick into fog dx = next_position[0] - my_position[0] dy = next_position[1] - my_position[1] position = next_position is_fog = False while self._on_board(position): if utility.position_is_fog(board, position): is_fog = True break position = (position[0] + dx, position[1] + dy) if is_fog: continue # list of boards from next steps list_boards[my_action], _ \ = self._board_sequence(obs["board"], info["curr_bombs"], info["curr_flames"], self._search_range, my_position, my_blast_strength=my_blast_strength, my_action=my_action, can_kick=my_kick, enemy_mobility=self._enemy_mobility, enemy_bomb=self._enemy_bomb, agent_blast_strength=info["agent_blast_strength"]) # wood might be disappeared, because of overestimated bombs for t in range(len(list_boards[my_action])): wood_positions = np.where(info["list_boards_no_move"][t] == constants.Item.Wood.value) list_boards[my_action][t][wood_positions] = constants.Item.Wood.value # agents might be disappeared, because of overestimated bombs for t, positions in enumerate(list_enemy_positions): list_boards[my_action][t][positions] = constants.Item.AgentDummy.value # some bombs may explode with extra bombs, leading to under estimation for t in range(len(list_boards[my_action])): flame_positions = np.where(info["list_boards_no_move"][t] == constants.Item.Flames.value) list_boards[my_action][t][flame_positions] = constants.Item.Flames.value """ processed = Parallel(n_jobs=-1, verbose=0)( [delayed(search_time_expanded_network)(list_boards[action][1:], my_next_position[action], action) for action in list_boards] ) for survivable, my_action in processed: if my_next_position[my_action] in survivable[0]: is_survivable[my_action] = True n_survivable[my_action] = [1] + [len(s) for s in survivable[1:]] """ for my_action in list_boards: survivable = search_time_expanded_network(list_boards[my_action][1:], my_next_position[my_action]) if my_next_position[my_action] in survivable[0]: is_survivable[my_action] = True n_survivable[my_action] = [1] + [len(s) for s in survivable[1:]] survivable_actions = list() for a in is_survivable: if not is_survivable[a]: continue if might_blocked[a] and not is_survivable[constants.Action.Stop]: continue if n_survivable[a][-1] <= 1: is_survivable[a] = False continue survivable_actions.append(a) # # Choose action # if len(survivable_actions) == 0: # # return None, if no survivable actions # return None elif len(survivable_actions) == 1: # # Choose the survivable action, if it is the only choice # action = survivable_actions[0] if verbose: print("The only survivable action", action) return action.value # # Bomb at a target # # fraction of blocked node in the survival trees of enemies _list_boards = deepcopy(info["list_boards_no_move"]) if obs["bomb_blast_strength"][my_position]: for b in _list_boards: if utility.position_is_agent(b, my_position): b[my_position] = constants.Item.Bomb.value else: for b in _list_boards: if utility.position_is_agent(b, my_position): b[my_position] = constants.Item.Passage.value total_frac_blocked, n_survivable_nodes, blocked_time_positions \ = self._get_frac_blocked(_list_boards, my_enemies, board, obs["bomb_life"]) if teammate_position is not None: total_frac_blocked_teammate, n_survivable_nodes_teammate, blocked_time_positions_teammate \ = self._get_frac_blocked(_list_boards, [my_teammate], board, obs["bomb_life"]) """ np.set_printoptions(precision=3) print("enemy") print(total_frac_blocked) print("teammate") print(total_frac_blocked_teammate) print("product") prod = total_frac_blocked * (1 - total_frac_blocked_teammate) print(prod[:5,:5]) """ p_survivable = defaultdict(float) for action in n_survivable: p_survivable[action] = sum(n_survivable[action]) / self._my_survivability_threshold if p_survivable[action] > 1: p_survivable[action] = 1 block = defaultdict(float) for action in [constants.Action.Stop, constants.Action.Up, constants.Action.Down, constants.Action.Left, constants.Action.Right]: next_position = my_next_position[action] if next_position is None: continue if next_position in set.union(kickable, might_kickable): # kick will be considered later continue if all([utility.position_is_flames(board, next_position), info["flame_life"][next_position] > 1, is_survivable[constants.Action.Stop]]): # if the next position is flames, # I want to stop to wait, which must be feasible block[action] = total_frac_blocked[next_position] * p_survivable[constants.Action.Stop] if teammate_position is not None: block[action] *= (1 - total_frac_blocked_teammate[next_position]) block[action] *= self._inv_tmp block[action] -= np.log(-np.log(self.random.uniform())) continue elif not is_survivable[action]: continue if all([might_blocked[action], not is_survivable[constants.Action.Stop]]): continue block[action] = total_frac_blocked[next_position] * p_survivable[action] if teammate_position is not None: block[action] *= (1 - total_frac_blocked_teammate[next_position]) block[action] *= self._inv_tmp block[action] -= np.log(-np.log(self.random.uniform())) if might_blocked[action]: block[action] = (total_frac_blocked[my_position] * p_survivable[constants.Action.Stop] + total_frac_blocked[next_position] * p_survivable[action]) / 2 if teammate_position is not None: block[action] *= (1 - total_frac_blocked_teammate[next_position]) block[action] *= self._inv_tmp block[action] -= np.log(-np.log(self.random.uniform())) if is_survivable[constants.Action.Bomb]: list_boards_with_bomb, _ \ = self._board_sequence(board, info["curr_bombs"], info["curr_flames"], self._search_range, my_position, my_blast_strength=my_blast_strength, my_action=constants.Action.Bomb) n_survivable_nodes_with_bomb = defaultdict(int) for enemy in my_enemies: # get survivable tree of the enemy rows, cols = np.where(board==enemy.value) if len(rows) == 0: continue enemy_position = (rows[0], cols[0]) _survivable = search_time_expanded_network(list_boards_with_bomb, enemy_position) n_survivable_nodes_with_bomb[enemy] = sum([len(positions) for positions in _survivable]) n_with_bomb = sum([n_survivable_nodes_with_bomb[enemy] for enemy in my_enemies]) n_with_none = sum([n_survivable_nodes[enemy] for enemy in my_enemies]) if n_with_none == 0: total_frac_blocked_with_bomb = 0 # place more bombs, so the stacked enemy cannot kick x, y = my_position for dx, dy in [(-1, 0), (1, 0), (0, -1), (0, 1)]: next_position = (x + dx, y + dy) following_position = (x + 2 * dx, y + 2 * dy) if not self._on_board(following_position): continue if all([obs["bomb_life"][next_position] > 0, board[following_position] > constants.Item.AgentDummy.value]): total_frac_blocked_with_bomb = 1 else: total_frac_blocked_with_bomb = 1 - n_with_bomb / n_with_none if teammate_position is not None: # get survivable tree of the teammate _survivable = search_time_expanded_network(list_boards_with_bomb, teammate_position) n_survivable_nodes_with_bomb_teammate = sum([len(positions) for positions in _survivable]) n_with_bomb = n_survivable_nodes_with_bomb_teammate n_with_none = n_survivable_nodes_teammate[my_teammate] if n_with_none == 0: total_frac_blocked_with_bomb_teammate = 0 else: total_frac_blocked_with_bomb_teammate = 1 - n_with_bomb / n_with_none action = constants.Action.Bomb block[action] = total_frac_blocked_with_bomb * p_survivable[action] if teammate_position is not None: block[action] *= (1 - total_frac_blocked_with_bomb_teammate) block[action] *= self._inv_tmp block[action] -= np.log(-np.log(self.random.uniform())) for next_position in kickable: action = self._get_direction(my_position, next_position) if not is_survivable[action]: continue list_boards_with_kick, _ \ = self._board_sequence(obs["board"], info["curr_bombs"], info["curr_flames"], self._search_range, my_position, my_action=action, can_kick=True) n_survivable_nodes_with_kick = defaultdict(int) for enemy in my_enemies: # get survivable tree of the enemy rows, cols = np.where(board==enemy.value) if len(rows) == 0: continue enemy_position = (rows[0], cols[0]) _survivable = search_time_expanded_network(list_boards_with_kick, enemy_position) n_survivable_nodes_with_kick[enemy] = sum([len(positions) for positions in _survivable]) n_with_kick = sum([n_survivable_nodes_with_kick[enemy] for enemy in my_enemies]) n_with_none = sum([n_survivable_nodes[enemy] for enemy in my_enemies]) if n_with_none == 0: total_frac_blocked[next_position] = 0 else: total_frac_blocked[next_position] = 1 - n_with_kick / n_with_none if teammate_position is not None: # get survivable tree of the teammate _survivable = search_time_expanded_network(list_boards_with_kick, teammate_position) n_survivable_nodes_with_kick_teammate = sum([len(positions) for positions in _survivable]) n_with_kick = n_survivable_nodes_with_kick_teammate n_with_none = n_survivable_nodes_teammate[my_teammate] if n_with_none == 0: total_frac_blocked_teammate[next_position] = 0 else: total_frac_blocked_teammate[next_position] = 1 - n_with_kick / n_with_none block[action] = total_frac_blocked[next_position] * p_survivable[action] if teammate_position is not None: block[action] *= (1 - total_frac_blocked_teammate[next_position]) block[action] *= self._inv_tmp block[action] -= np.log(-np.log(self.random.uniform())) max_block = -np.inf best_action = None for action in block: if block[action] > max_block: max_block = block[action] best_action = action if block[constants.Action.Bomb] > self._bomb_threshold * self._inv_tmp: if teammate_position is not None: teammate_safety = total_frac_blocked_with_bomb_teammate * n_survivable_nodes_with_bomb_teammate if any([teammate_safety > self._teammate_survivability_threshold, total_frac_blocked_with_bomb_teammate < self._interfere_threshold, total_frac_blocked_with_bomb_teammate < total_frac_blocked_teammate[my_position]]): teammate_ok = True else: teammate_ok = False else: teammate_ok = True if teammate_ok: if best_action == constants.Action.Bomb: if verbose: print("Bomb is best", constants.Action.Bomb) return constants.Action.Bomb.value if best_action == constants.Action.Stop: if verbose: print("Place a bomb at a locally optimal position", constants.Action.Bomb) return constants.Action.Bomb.value # # Move towards where to bomb # if best_action not in [None, constants.Action.Bomb]: next_position = my_next_position[best_action] should_chase = (total_frac_blocked[next_position] > self._chase_threshold) if teammate_position is not None: teammate_safety = total_frac_blocked_teammate[next_position] * n_survivable_nodes_teammate[my_teammate] if any([teammate_safety > self._teammate_survivability_threshold, total_frac_blocked_teammate[next_position] < self._interfere_threshold, total_frac_blocked_teammate[next_position] < total_frac_blocked_teammate[my_position]]): teammate_ok = True else: teammate_ok = False else: teammate_ok = True if should_chase and teammate_ok: if all([utility.position_is_flames(board, next_position), info["flame_life"][next_position] > 1, is_survivable[constants.Action.Stop]]): action = constants.Action.Stop if verbose: print("Wait flames life", action) return action.value else: if verbose: print("Move towards better place to bomb", best_action) return best_action.value # Exclude the action representing stop to wait max_block = -np.inf best_action = None for action in survivable_actions: if block[action] > max_block: max_block = block[action] best_action = action # # Do not take risky actions # most_survivable_action = self._action_most_survivable(n_survivable) # ignore actions with low survivability _survivable_actions = list() for action in n_survivable: n = sum(n_survivable[action]) if not is_survivable[action]: continue elif n > self._my_survivability_threshold: _survivable_actions.append(action) else: print("RISKY", action) is_survivable[action] = False if len(_survivable_actions) > 1: survivable_actions = _survivable_actions elif best_action is not None: if verbose: print("Take the best action in danger", best_action) return best_action.value else: # Take the most survivable action if verbose: print("Take the most survivable action", most_survivable_action) return most_survivable_action.value # # Do not interfere with teammate # if all([teammate_position is not None, len(enemy_positions) > 0 or len(info["curr_bombs"]) > 0]): # ignore actions that interfere with teammate min_interfere = np.inf least_interfere_action = None _survivable_actions = list() for action in survivable_actions: if action == constants.Action.Bomb: frac = total_frac_blocked_with_bomb_teammate else: next_position = my_next_position[action] frac = total_frac_blocked_teammate[next_position] if frac < min_interfere: min_interfere = frac least_interfere_action = action if frac < self._interfere_threshold: _survivable_actions.append(action) else: print("INTERFERE", action) is_survivable[action] = False if len(_survivable_actions) > 1: survivable_actions = _survivable_actions elif best_action is not None: # Take the least interfering action if verbose: print("Take the best action in intereference", best_action) return best_action.value else: if verbose: print("Take the least interfering action", least_interfere_action) return least_interfere_action.value # # Bomb to break wood # consider_bomb = True if not is_survivable[constants.Action.Bomb]: consider_bomb = False elif self._might_break_powerup(info["list_boards_no_move"][-1], my_position, my_blast_strength, info["might_powerup"]): # if might break an item, do not bomb consider_bomb = False if consider_bomb and bomb_target[my_position]: # place bomb if I am at a bomb target if verbose: print("Bomb at a bomb target", constants.Action.Bomb) return constants.Action.Bomb.value # # Find reachable items # # List of boards simulated list_boards, _ = self._board_sequence(board, info["curr_bombs"], info["curr_flames"], self._search_range, my_position, enemy_mobility=self._enemy_mobility, enemy_bomb=self._enemy_bomb, agent_blast_strength=info["agent_blast_strength"]) # wood might be disappeared, because of overestimated bombs for t in range(len(list_boards)): wood_positions = np.where(info["list_boards_no_move"][t] == constants.Item.Wood.value) list_boards[t][wood_positions] = constants.Item.Wood.value # some bombs may explode with extra bombs, leading to under estimation for t in range(len(list_boards)): flame_positions = np.where(info["list_boards_no_move"][t] == constants.Item.Flames.value) list_boards[t][flame_positions] = constants.Item.Flames.value # List of the set of survivable time-positions at each time # and preceding positions survivable, prev, _, _ = self._search_time_expanded_network(list_boards, my_position) if len(survivable[-1]) == 0: survivable = [set() for _ in range(len(survivable))] # Items and bomb target that can be reached in a survivable manner reachable_items, reached, next_to_items \ = self._find_reachable_items(list_boards, my_position, survivable, bomb_target, info["might_powerup"]) # # Move to dig # action_to_target = self._action_to_target(my_position, reachable_items, prev, is_survivable) if all([digging is not None, action_to_target is not None]): time_to_reach = reachable_items["target"][0][0] if any([my_ammo and board[digging] in [constants.Item.Passage.value, constants.Item.ExtraBomb.value, constants.Item.IncrRange.value, constants.Item.Kick.value], info["flame_life"][digging] <= time_to_reach and utility.position_is_flames(board, digging)]): if verbose: print("Move to dig", action_to_target) return action_to_target.value # # Move towards good items # action = self._action_to_powerup(my_position, reachable_items, prev, is_survivable) if action is not None: if verbose: print("Moving toward good item", action) return action.value # # Move towards where to bomb to break wood # if action_to_target is not None: if verbose: print("Moving toward where to bomb", action_to_target) return action_to_target.value # # Move toward might powerups # action = self._action_to_might_powerup(my_position, reachable_items, prev, is_survivable) if action is not None: if verbose: print("Moving toward might-powerups", action) return action.value # # If I have seen an enemy recently and cannot see him now, them move to the last seen position # action = self._action_to_enemy(my_position, next_to_items[constants.Item.Fog], prev, is_survivable, info, my_enemies) if action is not None: if verbose: print("Moving toward last seen enemy", action) return action.value # # If I have seen a teammate recently, them move away from the last seen position # action = self._action_away_from_teammate(my_position, next_to_items[constants.Item.Fog], prev, is_survivable, info, my_teammate) if action is not None: if verbose: print("Moving away from last seen teammate", action) return action.value # # Move towards a fog where we have not seen longest # action = self._action_to_fog(my_position, next_to_items[constants.Item.Fog], prev, is_survivable, info) if action is not None: #if True: if self.random.uniform() < 0.8: if verbose: print("Moving toward oldest fog", action) return action.value # # Choose most survivable action # max_block = -np.inf best_action = None for action in survivable_actions: if action == constants.Action.Bomb: continue if block[action] > max_block: max_block = block[action] best_action = action if verbose: print("Take the best action among safe actions (nothing else to do)", best_action) if best_action is None: # this should not be the case return None else: return best_action.value
def _might_break_powerup(cls, board, my_position, blast_strength, might_powerup): """ Whether one might break a powerup by placing a bomb at my position Parameters ---------- board : array board my_position : tuple where to place a bomb blast_strength : int strength of the bomb Return ------ boolean True iff might break a powerup by placing a bomb """ x, y = my_position # To up for dx in range(1, blast_strength): if x + dx >= len(board[0]): break position = (x + dx, y) if utility.position_is_powerup( board, position) or might_powerup[position]: return True if not utility.position_is_passage(board, position): # stop searching this direction break # To down for dx in range(1, blast_strength): if x - dx < 0: break position = (x - dx, y) if utility.position_is_powerup( board, position) or might_powerup[position]: return True if not utility.position_is_passage(board, position): # stop searching this direction break # To right for dy in range(1, blast_strength): if y + dy >= len(board): break position = (x, y + dy) if utility.position_is_powerup( board, position) or might_powerup[position]: return True if not utility.position_is_passage(board, position): # stop searching this direction break # To left for dy in range(1, blast_strength): if y - dy < 0: break position = (x, y - dy) if utility.position_is_powerup( board, position) or might_powerup[position]: return True if not utility.position_is_passage(board, position): # stop searching this direction break return False