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 _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 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 _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 _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 _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_covered_by_bomb(obs, pos, bomb_real_life_map): # return a tuple (True/False, min_bomb_life_value, max life value) min_bomb_pos, max_bomb_pos = None, None min_bomb_value, max_bomb_value = INT_MAX, -INT_MAX if obs['bomb_life'][pos] > 0: min_bomb_value, max_bomb_value = bomb_real_life_map[pos], bomb_real_life_map[pos] min_bomb_pos, max_bomb_pos = pos, pos dirs = _all_directions(exclude_stop=True) board = obs['board'] for d in dirs: next_pos = pos while True: next_pos = utility.get_next_position(next_pos, d) if _stop_condition(board, next_pos, exclude_agent=True): # here should assume agents are dynamic break if obs['bomb_life'][next_pos] > 0 and obs['bomb_blast_strength'][next_pos] - 1 >= _manhattan_distance(pos, next_pos): if bomb_real_life_map[next_pos] < min_bomb_value: min_bomb_value = bomb_real_life_map[next_pos] min_bomb_pos = next_pos if bomb_real_life_map[next_pos] > max_bomb_value: max_bomb_value = bomb_real_life_map[next_pos] max_bomb_pos = next_pos break if min_bomb_pos is not None: return True, min_bomb_value, max_bomb_value return False, INT_MAX, -INT_MAX
def check_if_in_bomb_range(board, bombs, position): for b in bombs: #Set the direction to trace direction = None if (b['position'][0] == position[0] and abs(b['position'][1] - position[1]) <= b['blast_strength']): if b['position'][1] < position[1]: direction = constants.Action.Right elif b['position'][1] > position[1]: direction = constants.Action.Left elif (b['position'][1] == position[1] and abs(b['position'][0] - position[0]) <= b['blast_strength']): if b['position'][0] < position[0]: direction = constants.Action.Down elif b['position'][0] > position[0]: direction = constants.Action.Up else: continue if direction is None: return True #Trace from bomb to see if there's block in the way new_pos = b['position'] while new_pos != position: new_pos = utility.get_next_position(new_pos, direction) if board[new_pos] in [ constants.Item.Rigid.value, constants.Item.Wood.value ]: break if new_pos == position: return True return False
def is_dead_end(board, position, direction): npos = util.get_next_position(position, direction) counter = 0 for d in dirs: if not util.is_valid_direction(board, npos, d): counter += 1 return counter >= 3
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 _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_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 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 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_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 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 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 _filter_recently_visited(directions, my_position, recently_visited_positions): ret = [] for direction in directions: if not utility.get_next_position( my_position, direction) in recently_visited_positions: ret.append(direction) if not ret: ret = directions return ret
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 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 _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 _filter_unsafe_directions(board, my_position, directions, bombs): ret = [] for direction in directions: x, y = utility.get_next_position(my_position, direction) is_bad = False for bomb in bombs: bomb_x, bomb_y = bomb['position'] blast_strength = bomb['blast_strength'] if (x == bomb_x and abs(bomb_y - y) <= blast_strength) or \ (y == bomb_y and abs(bomb_x - x) <= blast_strength): is_bad = True break if not is_bad: ret.append(direction) return ret
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
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
def agent_on_bomb_next_move_test(my_position, direction, board, bomb_life, blast_st): assert (bomb_life[my_position] > 0) st = min(int(bomb_life[my_position]), int(blast_st[my_position])) pos = my_position 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] corrdior_pos = [True] for i in range(st): next_pos = utility.get_next_position(pos, direction) if not utility.position_on_board(board, next_pos): break if not position_is_passable(board, next_pos): break if is_in_corridor(board, next_pos, perpendicular_dirs): corrdior_pos.append(True) else: corrdior_pos.append(False) pos = next_pos return not all(corrdior_pos)
def move_moving_bombs_to_next_position(prev_obs, obs): 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 bombs = zip(*np.where(obs['bomb_life'] > 1)) moving_bombs = [] for bomb_pos in bombs: moving_dir = None for d in [constants.Action.Left, constants.Action.Right, \ constants.Action.Up, constants.Action.Down]: if is_moving_direction(bomb_pos, d): moving_dir = d break if moving_dir is not None: moving_bombs.append((bomb_pos, moving_dir)) board = obs['board'] bomb_life = obs['bomb_life'] bomb_blast_strength = obs['bomb_blast_strength'] for bomb_pos, moving_dir in moving_bombs: next_pos = utility.get_next_position(bomb_pos, moving_dir) if not utility.position_on_board(obs['board'], next_pos): continue if utility.position_is_passage(obs['board'], next_pos): board[next_pos] = board[bomb_pos] bomb_life[next_pos] = bomb_life[bomb_pos] bomb_blast_strength[next_pos] = bomb_blast_strength[bomb_pos] board[bomb_pos] = constants.Item.Passage.value bomb_life[bomb_pos] = 0 bomb_blast_strength[bomb_pos] = 0 return obs