def me_to_enemy_all_corridor(board, pos1, pos2): assert (pos1[0] == pos2[0] or pos1[1] == pos2[1]) if pos1[0] == pos2[0]: if pos1[1] < pos2[1]: direction = constants.Action.Right else: direction = constants.Action.Left else: if pos1[0] < pos2[0]: direction = constants.Action.Down else: direction = constants.Action.Up p_dirs = perpendicular_directions(direction) pos2_next = utility.get_next_position(pos2, direction) next_is_impasse = (not utility.position_on_board( board, pos2_next)) or utility.position_is_wall(board, pos2_next) if utility.position_on_board(board, pos2_next) and utility.position_is_fog( board, pos2_next): next_is_impasse = False if not (position_is_in_corridor(board, pos2, p_dirs) and next_is_impasse): # pos2:enempy must be in impasse return False all_corridor_flag = True pos = utility.get_next_position(pos1, direction) while pos != pos2: if not (utility.position_is_passage(board, pos)): all_corridor_flag = False break if not position_is_in_corridor(board, pos, p_dirs): all_corridor_flag = False break pos = utility.get_next_position(pos, direction) return all_corridor_flag
def position_has_no_escape(obs, flame_positions, consider_enemy=False): directions = [ constants.Action.Left, constants.Action.Up, constants.Action.Right, constants.Action.Down ] my_position, can_kick, board, bomb_life, blast_st, enemies, teammate=obs['position'], \ obs['can_kick'], obs['board'], obs['bomb_life'], obs['bomb_blast_strength'], obs['enemies'], obs['teammate'] next_step_flaming_positions = get_next_step_flaming_positions( my_position, board, bomb_life, blast_st, enemies, teammate) future_flaming_positions = [] for pos in list(zip(*np.where(bomb_life > 0))): sz = int(blast_st[pos]) x, y = pos future_flaming_positions.append(pos) for i in range(1, sz): pos2_list = [(x + i, y), (x - i, y), (x, y + i), (x, y - i)] for pos2 in pos2_list: if utility.position_on_board(board, pos2): future_flaming_positions.append(pos2) valid_directions = [] num_passable = 0 passable_position = None for direction in directions: next_pos = utility.get_next_position(my_position, direction) if not utility.position_on_board(board, next_pos): continue if can_kick and utility.position_in_items(board, next_pos, [constants.Item.Bomb]): if kick_test(board, blast_st, bomb_life, my_position, direction): #passed kick test, so at least kick direction is an escape return False continue flag_passable = position_is_passable(board, next_pos, enemies) if not flag_passable: continue elif flag_passable and consider_enemy and next_pos in future_flaming_positions and passable_may_next_to_enemy( board, next_pos, enemies): continue else: num_passable += 1 passable_position = next_pos if next_pos in flame_positions or next_pos in next_step_flaming_positions: continue valid_directions.append(direction) if num_passable == 0: return True if (my_position not in flame_positions) and (my_position not in next_step_flaming_positions): return False return len(valid_directions) == 0
def position_is_in_corridor(board, position, perpendicular_dirs): d1 = perpendicular_dirs[0] d2 = perpendicular_dirs[1] p1 = utility.get_next_position(position, d1) p2 = utility.get_next_position(position, d2) con1 = ((not utility.position_on_board(board, p1)) or utility.position_is_wall(board, p1)) con2 = ((not utility.position_on_board(board, p2)) or utility.position_is_wall(board, p2)) return con1 and con2
def neighbor_test(my_pos, life_value): x, y = my_pos i = x - 1 sz = len(board) while i >= 0: position = (i, y) if not utility.position_on_board(board, position): break if int(bomb_life[i, y]) <= life_value and blast_st[i, y] > abs(i - x): return False if not position_can_be_bomb_through(board, position): break i -= 1 i = x + 1 while i < sz: position = (i, y) if not utility.position_on_board(board, position): break if int(bomb_life[i, y]) <= life_value and blast_st[i, y] > abs(i - x): return False if not position_can_be_bomb_through(board, position): break i += 1 j = y - 1 while j >= 0: position = (x, j) if not utility.position_on_board(board, position): break if int(bomb_life[x, j]) <= life_value and blast_st[x, j] > abs(j - y): return False if not position_can_be_bomb_through(board, position): break j -= 1 j = y + 1 while j < sz: position = (x, j) if not utility.position_on_board(board, position): break if int(bomb_life[x, j]) <= life_value and blast_st[x, j] > abs(j - y): return False if not position_can_be_bomb_through(board, position): break j += 1 return True
def is_stuck_direction(next_position, bomb_range, next_board, enemies): Q = queue.PriorityQueue() Q.put((0, next_position)) seen = set() nx, ny = next_position is_stuck = True while not Q.empty(): dist, position = Q.get() seen.add(position) px, py = position if nx != px and ny != py: is_stuck = False break if dist > bomb_range: is_stuck = False break for row, col in [(-1, 0), (1, 0), (0, -1), (0, 1)]: new_position = (row + px, col + py) if new_position in seen: continue if not utility.position_on_board(next_board, new_position): continue if not utility.position_is_passable(next_board, new_position, enemies): continue dist = abs(row + px - nx) + abs(col + py - ny) Q.put((dist, new_position)) return is_stuck
def is_valid_position(board, position, direction, step): row, col = position invalid_values = [item.value for item \ in [constants.Item.Rigid]] if utility.position_on_board(board, position) == False: return False if constants.Action(direction) == constants.Action.Stop: return True if constants.Action(direction) == constants.Action.Up: return row - step >= 0 and board[row - step][col] not in invalid_values if constants.Action(direction) == constants.Action.Down: return row + step < len(board) and board[ row + step][col] not in invalid_values if constants.Action(direction) == constants.Action.Left: return col - step >= 0 and board[row][col - step] not in invalid_values if constants.Action(direction) == constants.Action.Right: return col + step < len(board[0]) and \ board[row][col+step] not in invalid_values raise constants.InvalidAction("We did not receive a valid direction: ", direction)
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 _check_if_flame_will_gone(obs, prev_two_obs, flame_pos): assert (prev_two_obs[0] is not None) assert (prev_two_obs[1] is not None) # check the flame group in current obs, see if # the whole group was there prev two obs # otherwise, although this flame appears in prev two obs, # it could be a old overlap new, thus will not gone next step if not (utility.position_is_flames(prev_two_obs[0]['board'], flame_pos) \ and utility.position_is_flames(prev_two_obs[1]['board'], flame_pos)): return False board = obs['board'] Q = deque(maxlen=121) Q.append(flame_pos) visited = [flame_pos] dirs = _all_directions(exclude_stop=True) while len(Q) > 0: pos = Q.popleft() if not (utility.position_is_flames(prev_two_obs[0]['board'], pos) \ and utility.position_is_flames(prev_two_obs[1]['board'], pos)): return False for d in dirs: next_pos = utility.get_next_position(pos, d) if utility.position_on_board(board, next_pos) and utility.position_is_agent(board, next_pos): if next_pos not in visited: Q.append(next_pos) visited.append(next_pos) return True
def _kick_test(board, blast_st, bomb_life, my_position, direction): 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 next_position=utility.get_next_position(my_position, direction) assert(utility.position_in_items(board, next_position, [constants.Item.Bomb])) life_value=int(bomb_life[next_position]) strength=int(blast_st[next_position]) dist=0 pos=utility.get_next_position(next_position, direction) perpendicular_dirs=[constants.Action.Left, constants.Action.Right] if direction == constants.Action.Left or direction == constants.Action.Right: perpendicular_dirs=[constants.Action.Down, constants.Action.Up] for i in range(life_value): if utility.position_on_board(board, pos) and utility.position_is_passage(board, pos): #do a check if this position happens to be in flame when the moving bomb arrives! if not (moving_bomb_check(pos, perpendicular_dirs[0], i) and moving_bomb_check(pos, perpendicular_dirs[1], i)): break dist +=1 else: break pos=utility.get_next_position(pos, direction) #can kick and kick direction is valid return dist > strength
def _get_fire_positions_in_direction(board, x, y, strength, x_dir, y_dir, fire_pos): if strength <= 0 or not utility.position_on_board(board, (x, y)): return next_x = x + x_dir next_y = y + y_dir if not utility.position_on_board(board, (next_x, next_y)): return if utility.position_in_items( board, (next_x, next_y), [constants.Item.Rigid, constants.Item.Wood]): return fire_pos.append((next_x, next_y)) EnvSimulator._get_fire_positions_in_direction(board, next_x, next_y, strength - 1, x_dir, y_dir, fire_pos)
def _filter_direction_toward_flames(board, my_position, directions, enemies): ret = [] for direction in directions: position = utility.get_next_position(my_position, direction) if utility.position_on_board( board, position) and not utility.position_is_flames(board, position): ret.append(direction) return ret
def get_next_step_flaming_positions(my_position, board, bomb_life, bomb_blast_st, enemies, teammate): def add_to_danger_positions(pos, danger_positions, board, blast_st, bomb_life, covered_bomb_positions): '''due to bombing chain, bombs with life>=2 would still blow up if they are in the danger positions ''' sz = int(blast_st[pos]) x, y = pos danger_positions.add(pos) for i in range(1, sz): pos2_list = [(x + i, y), (x - i, y), (x, y + i), (x, y - i)] for pos2 in pos2_list: if utility.position_on_board(board, pos2): danger_positions.add(pos2) if bomb_life[pos2] > 1: covered_bomb_positions.add(pos2) blast_st = bomb_blast_st all_other_agents = [e.value for e in enemies] + [teammate.value] #direction_list = [constants.Action.Left, constants.Action.Up, constants.Action.Right, constants.Action.Down] going_to_explode_bomb_positions = list(zip(*np.where(bomb_life == 1))) may_be_kicked = [] for pos in going_to_explode_bomb_positions: for direction in [ constants.Action.Left, constants.Action.Up, constants.Action.Right, constants.Action.Down ]: pos2 = utility.get_next_position(pos, direction) if not utility.position_on_board(board, pos2): continue if board[pos2] in all_other_agents: #mark to kicked #may_be_kicked.append(pos) break surely_danger_bomb_positions = [ pos for pos in going_to_explode_bomb_positions if pos not in may_be_kicked ] danger_positions = set() covered_bomb_positions = set() for pos in surely_danger_bomb_positions: add_to_danger_positions(pos, danger_positions, board, blast_st, bomb_life, covered_bomb_positions) all_covered = set() while len(covered_bomb_positions) > 0: for pos in list(covered_bomb_positions): add_to_danger_positions(pos, danger_positions, board, blast_st, bomb_life, covered_bomb_positions) all_covered.add(pos) for pos in list(covered_bomb_positions): if pos in all_covered: covered_bomb_positions.remove(pos) #print('agent pos:', my_position, 'danger:', danger_positions) return danger_positions
def _filter_invalid_directions(board, my_position, directions, enemies): ret = [] for direction in directions: position = utility.get_next_position(my_position, direction) if utility.position_on_board( board, position) and utility.position_is_passable( board, position, enemies): ret.append(direction) return ret
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 dead_end(obs): res = [0] * 6 pos = tuple(obs['position']) board = np.array(obs['board']) for direction in dirs: next_pos = util.get_next_position(pos, direction) if util.position_on_board(board, next_pos) and is_dead_end( board, pos, direction): res[direction.value] = -1 return res
def is_moving_direction(bomb_pos, direction): rev_d = _opposite_direction(direction) rev_pos = utility.get_next_position(bomb_pos, rev_d) if not utility.position_on_board(prev_obs['board'], rev_pos): return False if prev_obs['bomb_life'][rev_pos] - 1 == obs['bomb_life'][bomb_pos] \ and prev_obs['bomb_blast_strength'][rev_pos] == obs['bomb_blast_strength'][bomb_pos] \ and utility.position_is_passage(prev_obs['board'], bomb_pos): return True return False
def no_flames(obs): res = [0] * 6 my_position = obs['position'] board = obs['board'] x, y = my_position 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_flames( board, next_pos): res[act.value] = -1 return res
def _stop_condition(board, pos, exclude_agent=True): if not utility.position_on_board(board, pos): return True if utility.position_is_fog(board, pos): return True if utility.position_is_wall(board, pos): return True if not exclude_agent: if utility.position_is_agent(board, pos): return True return False
def is_moving_direction(bomb_pos, direction): # 炸弹是否在移动 rev_d = _opposite_direction(direction) # 返回 bomb 移动方向的反向 rev_pos = utility.get_next_position(bomb_pos, rev_d) # 上一帧 bomb 的位置 if not utility.position_on_board( prev_obs['board'], rev_pos): # 如果上一帧位置不在 board 上, 返回 False return False if prev_obs['bomb_life'][rev_pos] - 1 == obs['bomb_life'][bomb_pos] \ and prev_obs['bomb_blast_strength'][rev_pos] == obs['bomb_blast_strength'][bomb_pos] \ and utility.position_is_passage(prev_obs['board'], bomb_pos): return True # 与上一帧是同一个bomb 并且当前位置是一个过道0 返回 True return False
def _filter_legal_actions(state): my_position = tuple(state['position']) board = np.array(state['board']) enemies = [constants.Item(e) for e in state['enemies']] ret = [constants.Action.Bomb] for direction in directions: position = utility.get_next_position(my_position, direction) if utility.position_on_board( board, position) and utility.position_is_passable( board, position, enemies): ret.append(direction) return ret
def _opponent_test(board, candidate_position, enemies): assert (position_is_passable(board, candidate_position)) #make sure this passage is not next to the opponent, otherwise the opponent may come to this position for direction in [ constants.Action.Left, constants.Action.Up, constants.Action.Right, constants.Action.Down ]: position = utility.get_next_position(candidate_position, direction) if not utility.position_on_board(board, position): continue if utility.position_is_enemy(board, position, enemies): return False return True
def add_to_danger_positions(pos, danger_positions, board, blast_st, bomb_life, covered_bomb_positions): '''due to bombing chain, bombs with life>=2 would still blow up if they are in the danger positions ''' sz = int(blast_st[pos]) x, y = pos danger_positions.add(pos) for i in range(1, sz): pos2_list = [(x + i, y), (x - i, y), (x, y + i), (x, y - i)] for pos2 in pos2_list: if utility.position_on_board(board, pos2): danger_positions.add(pos2) if bomb_life[pos2] > 1: covered_bomb_positions.add(pos2)
def _position_will_be_flamed(board, position, bomb_life, bomb_blast_st, directions_to_check): for direction in directions_to_check: pos = utility.get_next_position(position, direction) k = 1 while utility.position_on_board(board, pos): if utility.position_is_wall(board, pos): break if bomb_life[pos] > 0 and bomb_blast_st[pos] - 1 >= k: return True pos = utility.get_next_position(pos, direction) k += 1 return False
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 valid_directions(obs): res = [0] * 6 pos = obs['position'] board = obs['board'] enemies = obs['enemies'] for act in dirs: next_pos = util.get_next_position(pos, act) if util.position_on_board(board, next_pos) and util.position_is_passable( board, next_pos, enemies): res[act.value] = 1 else: res[act.value] = -1 return res
def _get_items_in_direction(board, pos, strength, x_dir, y_dir, items): if strength <= 0 or not utility.position_on_board(board, pos): return x, y = pos next_x = x + x_dir next_y = y + y_dir if not utility.position_on_board(board, (next_x, next_y)): return item = board[(next_x, next_y)] try: if type(item) == tuple: print(item) if not item in items: items.append(item) except: if type(item) == tuple: print(item) print(item, items) if utility.position_in_items( board, (next_x, next_y), [constants.Item.Rigid, constants.Item.Wood]): return EnvSimulator._get_items_in_direction(board, (next_x, next_y), strength - 1, x_dir, y_dir, items)
def _teammate_test(board, candidate_position, teammate, next_step_flaming_positions): assert (position_is_passable(board, candidate_position)) #make sure this passage is not next to your teammate if your teammate is in "danger" (thus teammate must come here to be safe) for direction in [ constants.Action.Left, constants.Action.Up, constants.Action.Right, constants.Action.Down ]: position = utility.get_next_position(candidate_position, direction) if not utility.position_on_board(board, position): continue if board[ position] == teammate and position in next_step_flaming_positions: return False return True
def get_score(self): score = 0 # self_agent = self.self_agent self_agent_value = self.self_agent if type( self.self_agent) == int else self.self_agent.value #if the enemy agent is not alive, then the score increases for i, agent in enumerate(self.curr_agents): if 10 + i != self_agent_value: if not agent.is_alive: score += 5 else: #if we are dead, fk if not agent.is_alive: score -= score * 0.95 # if the agent is close to its enemy, then the score goes up self_agent_instance = self.curr_agents[self_agent_value - 10] for i, agent in enumerate(self.curr_agents): if 10 + i == self_agent_value: continue if not agent.is_alive: continue tar, tac = agent.position # target agent row, target agent column sar, sac = self_agent_instance.position distance = abs(tar - sar) + abs( tac - sac) #(((tar - sar) ** 2 + (tac - sac) ** 2) ** 0.5 if distance != 0: score += (1 / distance * 5) * 5 # if the agent has eaten good stuff, then score goes up if self._obs['position'] in self.last_items: val = self.last_items[self._obs['position']] # if val != constants.Item.Skull.value: score += 5 # else: # score -= 5 # if I placed bomb near wood, then score goes up for (k, v) in self.bombing_agents.items(): if v is self_agent_value - 10 and self._bomb_life[k[0]][ k[1]] == 24: for p in [(k[0] - 1, k[1]), (k[0] + 1, k[1]), (k[0], k[1] - 1), (k[0], k[1] + 1)]: if utility.position_on_board(self._board, p) and \ self._board[p] == constants.Item.Wood.value: score += 3 return score
def _count_adjacent_walls(board, position, items, enemies): walls_count = 0 not_passible_items = items[constants.Item.Wood] + items[ constants.Item.Rigid] + items[constants.Item.Bomb] + items[ constants.Item.Flames] for enemy in enemies: not_passible_items += items.get(enemy, []) for direction in [ constants.Action.Up, constants.Action.Down, constants.Action.Left, constants.Action.Right ]: new_pos = utility.get_next_position(position, direction) if not utility.position_on_board(board, new_pos) or \ new_pos in not_passible_items: walls_count = walls_count + 1 return walls_count
def passable_may_next_to_enemy(board, position, enemies): #means enemy can get here in 2 steps thus block you! assert (position_is_passable(board, position, enemies)) directions = [ constants.Action.Left, constants.Action.Up, constants.Action.Right, constants.Action.Down ] for direction in directions: next_pos = utility.get_next_position(position, direction) if not utility.position_on_board(board, next_pos): continue if utility.position_in_items(board, next_pos, enemies): return True #if position_is_passable(board, next_pos): # for d2 in directions: # next_pos2=utility.get_next_position(next_pos, direction) # if not utility.position_on_board(board, next_pos2): # continue # if utility.position_in_items(board, next_pos2, enemies): # return True return False