def move_player_out_of_square(game, player, x, y, p_used=None, p_down=None): # places the player at a random position that is not in the given square. xx = x if isinstance(x, Iterable) else (x,x) yy = y if isinstance(y, Iterable) else (y,y) x_min = xx[0] x_max = xx[1] y_min = yy[0] y_max = yy[1] board_x_max = len(game.state.pitch.board[0]) -2 board_y_max = len(game.state.pitch.board) -2 i = 0 while True: x = randint(1, board_x_max) y = randint(1, board_y_max) if x_min <= x <= x_max and y_min <= y <= y_max: i += 1 assert i<5000 continue if game.state.pitch.board[y][x] is None: break game.move(player, Square(x,y)) set_player_state(player, p_used=p_used, p_down=p_down)
def get_boundary_square(game, steps, from_position): steps = int(steps) if steps == 0: if game.state.pitch.board[from_position.y][from_position.x] is None: return from_position else: steps += 1 # return a position that is 'steps' away from 'from_position' # checks are done so it's square is available board_x_max = len(game.state.pitch.board[0]) -2 board_y_max = len(game.state.pitch.board) -2 assert steps > 0 avail_squares = steps*8 squares_per_side = 2*steps i = 0 while True: i +=1 assert i<5000 sq_index = randint(0, avail_squares -1) steps_along_side = sq_index % squares_per_side # up, including left corner if sq_index // squares_per_side == 0: dx = - steps + steps_along_side dy = - steps # right, including upper corner elif sq_index // squares_per_side == 1: dx = + steps dy = - steps + steps_along_side # down, including right corner elif sq_index // squares_per_side == 2: dx = + steps - steps_along_side dy = + steps # left, including lower corner elif sq_index // squares_per_side == 3: dx = - steps dy = + steps - steps_along_side else: assert False position = Square(from_position.x + dx, from_position.y + dy) x = position.x y = position.y if x < 1 or x > board_x_max or y < 1 or y > board_y_max: continue if game.state.pitch.board[y][x] is None: #it should y first, don't ask. break return position
def _reset_lecture(self, game): # ### CONFIG ### # board_x_max = len(game.state.pitch.board[0]) -2 board_y_max = len(game.state.pitch.board) -2 #Level configuration level = self.get_level() self.dices = 3-(level % self.dice_mod) blocker_skill = (level // self.dice_mod) % self.own_skills_mod victim_skill = (level // self.dice_mod // self.own_skills_mod) % self.opp_skills_mod blocker_team = get_home_players(game) victim_team = get_away_players(game) victim = victim_team.pop() move_player_within_square(game, victim, x = [2,board_x_max-1], y = [2, board_y_max-1], give_ball=random.random() < 0.5) x = victim.position.x y = victim.position.y blocker = blocker_team.pop() move_player_within_square(game, blocker, x = [x-1,x+1], y = [y-1, y+1]) #Setup skills if random.random() < 0.8: blocker.extra_skills = self.own_skills[blocker_skill] if random.random() < 0.8: victim.extra_skills = self.opp_skills[victim_skill] #setup assists if needed for two die target_str = victim.get_st() + 1 + victim.get_st()*(self.dices==3) blocker_assists = target_str - blocker.get_st() for _ in range(blocker_assists): move_player_within_square(game, blocker_team.pop(), x = [x-1,x+1], y = [y-1, y+1], p_used=1) #Setup rest of players: move_players_out_of_square(game, blocker_team, [x-4, x+4], [y-4, y+4], p_used=1) move_players_out_of_square(game, victim_team, [x-4, x+4], [y-4, y+4]) #Randomly place ball ball_pos = Square( randint(1,board_x_max), randint(1,board_y_max)) game.get_ball().move_to( ball_pos ) game.get_ball().is_carried = game.get_player_at(ball_pos) is not None game.set_available_actions() a = Action(action_type=ActionType.START_BLOCK, position = blocker.position, player = blocker ) game.step(a) a = Action(action_type=ActionType.BLOCK, position = victim.position, player = victim) game.step(a) self.actions = [a.action_type for a in game.get_available_actions() ] assert True in [a in self.actions for a in ChooseBlockDie.action_types] self.victim = victim self.blocker = blocker assert game.state.active_player == self.blocker
def swap_game(game): moved_players = [] board_x_max = len(game.state.pitch.board[0]) -2 player_to_move = get_home_players(game) + get_away_players(game) for p in player_to_move: if p in moved_players: continue old_x = p.position.x new_x = 27 - old_x potential_swap_p = game.state.pitch.board[p.position.y][new_x] if potential_swap_p is not None: game.move(potential_swap_p , Square(0,0) ) game.move(p, Square(new_x, p.position.y) ) if potential_swap_p is not None: game.move(potential_swap_p , Square(old_x, p.position.y) ) moved_players.append(potential_swap_p)
def move_player_within_square(game, player, x, y, give_ball=False, p_used=None, p_down=None): # places the player at a random position within the given square. assert isinstance(give_ball, bool) board_x_max = len(game.state.pitch.board[0]) -2 board_y_max = len(game.state.pitch.board) -2 xx = sorted(x) if isinstance(x, Iterable) else (x,x) yy = sorted(y) if isinstance(y, Iterable) else (y,y) x_min = max(xx[0] , 1 ) x_max = min(xx[1] , board_x_max ) y_min = max(yy[0] , 1 ) y_max = min(yy[1] , board_y_max ) assert x_min <= x_max assert y_min <= y_max i = 0 while True: i += 1 assert i < 5000 x = randint(x_min, x_max) y = randint(y_min, y_max) #if x < 1 or x > board_x_max or y < 1 or y > board_y_max: # continue if game.state.pitch.board[y][x] is None: break game.move(player, Square(x,y)) if give_ball == True: game.get_ball().move_to( player.position ) game.get_ball().is_carried = True set_player_state(player, p_used=p_used, p_down=p_down)
def _reset_lecture(self, game): if self.home_defence and not self.debug: if game.away_agent.name.find("selfplay") < 0: print(f"expected away agent name 'selfplay*', got '{game.away_agent.name}'") assert False # ### CONFIG ### # board_x_max = len(game.state.pitch.board[0]) -2 board_y_max = len(game.state.pitch.board) -2 #Level configuration level = self.get_level() home_on_defence = self.home_defence # have to pick from argument to init() noise = level % self.noise_mod scatter_1 = (level // self.noise_mod) % self.scatter1_mod scatter_2 = (level // self.noise_mod // self.scatter1_mod) % self.scatter2_mod offence_support = (level // self.noise_mod // self.scatter1_mod // self.scatter2_mod) % self.offence_support_mod if level%2==0: temp = scatter_1 scatter_1 = scatter_2 scatter_2 = temp #get players if not home_on_defence: #0 - home team threatens score, offence = get_home_players(game) defence = get_away_players(game) else: #1 - away team threatens score defence = get_home_players(game) offence = get_away_players(game) #swap needed #setup ball carrier p_carrier = offence.pop() move_player_within_square(game, p_carrier, x=[3, 9], y=[2, board_y_max-1], give_ball=False) extra_ma = (p_carrier.position.x - 1) - p_carrier.get_ma() p_carrier.extra_ma = max(min(extra_ma, 2), 0) ball_x = p_carrier.position.x ball_y = p_carrier.position.y #setup players intended for action marker_up_pos = Square(ball_x-1, max(ball_y-1, 1) ) marker_down_pos = Square(ball_x-1, min(ball_y+1, board_y_max) ) guy1 = defence.pop() guy2 = defence.pop() game.move(guy1, get_boundary_square(game, scatter_1, marker_up_pos)) game.move(guy2, get_boundary_square(game, scatter_2, marker_down_pos) ) guy1.state.up = guy1.position.distance(p_carrier.position) > 2 guy2.state.up = guy2.position.distance(p_carrier.position) > 2 #setup rest of screen (in state used p_used_defence = (1-level/ self.max_level) * home_on_defence p_used_offence = 0 upwards_y = ball_y - 4 downwards_y = ball_y + 4 while upwards_y > 0: #Potential special case at equal 0. move_player_within_square(game, defence.pop(), x=[ball_x-2, ball_x+2], y = upwards_y, p_used=p_used_defence) upwards_y -= 4 while downwards_y < board_y_max+1: #Potential special case at equal to board_y_max +1 move_player_within_square(game, defence.pop(), x=[ball_x-2, ball_x+2], y = downwards_y, p_used=p_used_defence) downwards_y += 4 #setup offensive support for _ in range(offence_support): move_player_within_square(game, offence.pop(), x=[ball_x-3,ball_x+3], y=[ball_y-3,ball_y+3] ) #setup other players randomly move_players_out_of_square(game, defence, x=[0, ball_x + 5], y=[0,20], p_used = p_used_defence) #home move_players_out_of_square(game, offence, x=[0, ball_x + 5], y=[0,20], p_used = p_used_offence) if home_on_defence: swap_game(game) #flips the board if level == 0 or (noise == 0 and random.random() < 0.5): #set_trace() game.set_available_actions() action_type = ActionType.START_MOVE a = Action(action_type=action_type, position = guy1.position, player = guy1) game.step(a) else: pass game.get_ball().move_to(p_carrier.position) game.get_ball().is_carried = True #Log turn self.turn = deepcopy(game.state.home_team.state.turn) self.opp_turn = deepcopy(game.state.away_team.state.turn)
def _reset_lecture(self, game): assert game.is_pass_available() # ### CONFIG ### # board_x_max = len(game.state.pitch.board[0]) -2 board_y_max = len(game.state.pitch.board) -2 #Level configuration extra_pass_dist = 1 if self.handoff else 4 level = self.get_level() noise = (level % self.noise_mod) dist_pass = (level // self.noise_mod) % self.pass_dist_mod + extra_pass_dist dist_to_td = (level // self.noise_mod // self.pass_dist_mod) % self.score_dist_mod +1 #1 = start in td zone #get players home_players = get_home_players(game) away_players = get_away_players(game) #setup scorer p_score = home_players.pop() p_score_x = dist_to_td p_score_y = randint(2, board_y_max -1 ) p_score.extra_skills = [] if random.random() < 0.5 else [Skill.CATCH] game.move(p_score, Square( p_score_x, p_score_y) ) #setup passer p_pass = home_players.pop() #p_pass.extra_skills = [] if random.random() < 0.5 else [Skill.PASS] #Pass skill not implemeted p_pass_x = p_score_x + dist_pass dx = abs(p_pass_x - p_score_x) move_player_within_square(game, p_pass, x=p_pass_x, y=[p_score_y-dx, p_score_y+dx], give_ball=True ) #setup passer movement left max_movement = p_pass.get_ma() + 2 #add two to remove GFIs p_pass.state.moves = max_movement if self.handoff: if dx > 1: #make sure passer can't score but can reach scorer to_not_score= p_pass_x -1 #double gfi to score is ok. to_handoff = dx #double gfi to score is ok. assert to_handoff <= to_not_score p_pass.state.moves -= randint(to_handoff, min(to_not_score, max_movement) ) else: #PassAction if noise > 0: #make sure passer can't reach scorer: to_not_handoff = dx-2 p_pass.state.moves = p_pass.get_ma() + 2 #add two to remove GFIs p_pass.state.moves -= randint(0, min(to_not_handoff, max_movement) ) assert 0 <= p_pass.state.moves if level == 0 or (noise == 0 and random.random() < 0.2) : # Start the pass/handoff action action_type = ActionType.START_HANDOFF if self.handoff else ActionType.START_PASS a = Action(action_type=action_type, position = p_pass.position, player = p_pass ) game.step(a) if True: x_min = 0 x_max = max(p_score.position.x, p_pass.position.x)+1 y_min = min(p_score.position.y, p_pass.position.y)-1 y_max = max(p_score.position.y, p_pass.position.y)+1 if noise <= 2: p_used = 1-noise/2 p_down = 1-noise/2 else: p_used = 0 p_down = 0 move_players_out_of_square(game, away_players, [x_min, x_max], [y_min , y_max], p_down=p_down ) move_players_out_of_square(game, home_players, [x_min, x_max], [y_min , y_max], p_used=p_used, p_down=p_down ) self.turn = deepcopy(game.state.home_team.state.turn) if False: print("pass moves: {}/{}".format(p_pass.state.moves, p_pass.get_ma() ))
def _reset_lecture(self, game): # ### CONFIG ### # board_x_max = len(game.state.pitch.board[0]) -2 board_y_max = len(game.state.pitch.board) -2 #Level configuration level = self.get_level() noise = (level % self.noise_mod) action_started = (level // self.noise_mod) % self.action_started_mod distance = (level // self.noise_mod // self.action_started_mod) % self.dst_mod home_players = get_home_players(game) away_players = get_away_players(game) #setup LoS oppenent for _ in range( randint(3,6)): p = away_players.pop() move_player_within_square(game, p, x=13, y=[5,11]) #setup rest of opponents move_players_out_of_square(game, away_players, x=[12, 50], y=[0, 20]) #get ball carrier p_carrier = home_players.pop() if level / self.max_level < random.random(): p_carrier.extra_skills = [Skill.SURE_HANDS] #setup LoS own team p_used = 1 - level / (self.max_level) p_used = max(1, p_used) for _ in range( randint(3,6)): p = home_players.pop() move_player_within_square(game, p, x=14, y=[5,11], p_used=p_used) #setup rest of team for _ in range(len(home_players)): p = home_players.pop() move_player_within_square(game, p, x=[15,19], y=[1,board_y_max], p_used=p_used) #setup ball center_square = Square(20,8) scatter_ball(game, max(1,noise), center_square) #setup ball carrier if noise == 0: move_player_within_square(game, p_carrier, center_square.x, center_square.y) else: game.move(p_carrier, get_boundary_square(game, 1+distance, game.get_ball().position)) if p_carrier.position.x < 15: move_player_within_square(game, p_carrier, x=[15,18], y=[p_carrier.position.y-1, p_carrier.position.y+1] ) if action_started == 0: game.set_available_actions() action_type = ActionType.START_MOVE a = Action(action_type=action_type, position = p_carrier.position, player = p_carrier ) game.step(a) self.turn = deepcopy(game.state.home_team.state.turn) self.carrier = p_carrier
class Pathfinder: DIRECTIONS = [ Square(-1, -1), Square(-1, 0), Square(-1, 1), Square(0, -1), Square(0, 1), Square(1, -1), Square(1, 0), Square(1, 1) ] def __init__(self, game, player, trr=False, directly_to_adjacent=False, can_block=False, can_handoff=False, can_foul=False): self.game = game self.player = player self.trr = trr self.directly_to_adjacent = directly_to_adjacent self.can_block = can_block self.can_handoff = can_handoff self.can_foul = can_foul self.ma = player.get_ma() - player.state.moves self.gfis = 3 if player.has_skill(Skill.SPRINT) else 2 self.locked_nodes = np.full((game.arena.height, game.arena.width), None) self.nodes = np.full((game.arena.height, game.arena.width), None) self.tzones = np.zeros((game.arena.height, game.arena.width), dtype=np.uint8) self.current_prob = 1 self.open_set = PriorityQueue() self.risky_sets = {} self.target_found = False for p in game.get_players_on_pitch(): if p.team != player.team and p.has_tackle_zone(): for square in game.get_adjacent_squares(p.position): self.tzones[square.y][square.x] += 1 def get_path(self, target): paths = self.get_paths(target) if len(paths) > 0: return paths[0] return None def get_paths(self, target=None): ma = self.player.get_ma() - self.player.state.moves self.ma = max(0, ma) gfis_used = 0 if ma >= 0 else -ma self.gfis = 3 - gfis_used if self.player.has_skill( Skill.SPRINT) else 2 - gfis_used if self.ma + self.gfis <= 0: return [] can_dodge = self.player.has_skill( Skill.DODGE) and Skill.DODGE not in self.player.state.used_skills can_sure_feet = self.player.has_skill( Skill.SURE_FEET ) and Skill.SURE_FEET not in self.player.state.used_skills can_sure_hands = self.player.has_skill(Skill.SURE_HANDS) rr_states = {(self.trr, can_dodge, can_sure_feet, can_sure_hands): 1} node = Node(None, self.player.position, self.ma, self.gfis, euclidean_distance=0, rr_states=rr_states) if not self.player.state.up: node = self._expand_stand_up(node) self.nodes[node.position.y][node.position.x] = node self.open_set.put((0, node)) self._expansion(target) self._clear() while not self.target_found and len(self.risky_sets) > 0: self._prepare_nodes() self._expansion(target) self._clear() return self._collect_paths(target) def _get_pickup_target(self, to_pos): zones_to = self.tzones[to_pos.y][to_pos.x] modifiers = 1 if not self.player.has_skill(Skill.BIG_HAND): modifiers -= int(zones_to) if self.game.state.weather == WeatherType.POURING_RAIN: if not self.player.has_skill(Skill.BIG_HAND): modifiers -= 1 if self.player.has_skill(Skill.EXTRA_ARMS): modifiers += 1 target = Rules.agility_table[self.player.get_ag()] - modifiers return min(6, max(2, target)) def _get_handoff_target(self, catcher): modifiers = self.game.get_catch_modifiers(catcher, handoff=True) target = Rules.agility_table[catcher.get_ag()] - modifiers return min(6, max(2, target)) def _get_dodge_target(self, from_pos, to_pos): zones_from = self.tzones[from_pos.y][from_pos.x] if zones_from == 0: return None zones_to = int(self.tzones[to_pos.y][to_pos.x]) modifiers = 1 ignore_opp_mods = False if self.player.has_skill(Skill.STUNTY): modifiers = 1 ignore_opp_mods = True if self.player.has_skill(Skill.TITCHY): modifiers += 1 ignore_opp_mods = True if self.player.has_skill(Skill.TWO_HEADS): modifiers += 1 if not ignore_opp_mods: modifiers -= zones_to target = Rules.agility_table[self.player.get_ag()] - modifiers return min(6, max(2, target)) def _expand(self, node: Node, target=None): if target is not None: # TODO: handoff? if type(target) == Square and target.distance( node.position) > node.moves_left + node.gfis_left: return if type(target) == int and abs( target - node.position.x) > node.moves_left + node.gfis_left: return if type(target) == Square and node.position == target: self.target_found = True return if type(target) == int and node.position.x == target: self.target_found = True return if node.block_dice is not None or node.handoff_roll is not None: return out_of_moves = False if node.moves_left + node.gfis_left == 0: if not self.can_handoff: return out_of_moves = True for direction in self.DIRECTIONS: next_node = self._expand_node(node, direction, out_of_moves=out_of_moves) if next_node is None: continue rounded_p = round(next_node.prob, 6) if rounded_p < self.current_prob: self._add_risky_move(rounded_p, next_node) else: self.open_set.put((next_node.euclidean_distance, next_node)) self.nodes[next_node.position.y][ next_node.position.x] = next_node def _expand_node(self, node, direction, out_of_moves=False): euclidean_distance = node.euclidean_distance + 1 if direction.x == 0 or direction.y == 0 else node.euclidean_distance + 1.41421 to_pos = self.game.state.pitch.squares[node.position.y + direction.y][node.position.x + direction.x] if not (1 <= to_pos.x < self.game.arena.width - 1 and 1 <= to_pos.y < self.game.arena.height - 1): return None player_at = self.game.get_player_at(to_pos) if player_at is not None: if player_at.team == self.player.team and self.can_handoff and player_at.can_catch( ): return self._expand_handoff_node(node, to_pos) elif player_at.team != self.player.team and self.can_block and player_at.state.up: return self._expand_block_node(node, euclidean_distance, to_pos, player_at) elif player_at.team != self.player.team and self.can_foul and not player_at.state.up: return self._expand_foul_node(node, to_pos, player_at) return None if not out_of_moves: return self._expand_move_node(node, euclidean_distance, to_pos) return None def _expand_move_node(self, node, euclidean_distance, to_pos): best_node = self.nodes[to_pos.y][to_pos.x] best_before = self.locked_nodes[to_pos.y][to_pos.x] gfi = node.moves_left == 0 moves_left_next = max(0, node.moves_left - 1) gfis_left_next = node.gfis_left - 1 if gfi else node.gfis_left total_moves_left = moves_left_next + gfis_left_next if best_node is not None: best_total_moves_left = best_node.moves_left + best_node.gfis_left if total_moves_left < best_total_moves_left: return None if total_moves_left == best_total_moves_left and euclidean_distance > best_node.euclidean_distance: return None next_node = Node(node, to_pos, moves_left_next, gfis_left_next, euclidean_distance) if gfi: next_node.apply_gfi() if self.tzones[node.position.y][node.position.x] > 0: target = self._get_dodge_target(node.position, to_pos) next_node.apply_dodge(target) if self.game.get_ball_position() == to_pos: target = self._get_pickup_target(to_pos) next_node.apply_pickup(target) if best_before is not None and self._dominant( next_node, best_before) == best_before: return None return next_node def _expand_foul_node(self, node, to_pos, player_at): best_node = self.nodes[to_pos.y][to_pos.x] best_before = self.locked_nodes[to_pos.y][to_pos.x] assists_from, assists_to = self.game.num_assists_at(self.player, player_at, node.position, foul=True) target = min( 12, max(2, player_at.get_av() + 1 - assists_from + assists_to)) next_node = Node(node, to_pos, 0, 0, node.euclidean_distance) next_node.apply_foul(target) if best_node is not None and self._best(next_node, best_node) == best_node: return None if best_before is not None and self._dominant( next_node, best_before) == best_before: return None return next_node def _expand_handoff_node(self, node, to_pos): best_node = self.nodes[to_pos.y][to_pos.x] best_before = self.locked_nodes[to_pos.y][to_pos.x] player_at = self.game.get_player_at(to_pos) next_node = Node(node, to_pos, 0, 0, node.euclidean_distance) target = self._get_handoff_target(player_at) next_node.apply_handoff(target) if best_node is not None and self._best(next_node, best_node) == best_node: return None if best_before is not None and self._dominant( next_node, best_before) == best_before: return None return next_node def _expand_block_node(self, node, euclidean_distance, to_pos, player_at): best_node = self.nodes[to_pos.y][to_pos.x] best_before = self.locked_nodes[to_pos.y][to_pos.x] block_dice = self.game.num_block_dice_at(attacker=self.player, defender=player_at, position=node.position, blitz=True) gfi = node.moves_left == 0 moves_left_next = node.moves_left - 1 if not gfi else node.moves_left gfis_left_next = node.gfis_left - 1 if gfi else node.gfis_left next_node = Node(node, to_pos, moves_left_next, gfis_left_next, euclidean_distance, block_dice=block_dice) if gfi: next_node.apply_gfi() if best_node is not None and self._best(next_node, best_node) == best_node: return None if best_before is not None and self._dominant( next_node, best_before) == best_before: return None return next_node def _add_risky_move(self, prob, node): if prob not in self.risky_sets: self.risky_sets[prob] = [] self.risky_sets[prob].append(node) def _expand_stand_up(self, node): if self.player.has_skill(Skill.JUMP_UP): return Node(node, self.player.position, self.ma, self.gfis, euclidean_distance=0) elif self.ma < 3: target = max( 2, min(6, 4 - self.game.get_stand_up_modifier(self.player))) next_node = Node(node, self.player.position, 0, self.gfis, euclidean_distance=0) next_node.apply_stand_up(target) return next_node next_node = Node(node, self.player.position, self.ma - 3, self.gfis, euclidean_distance=0) return next_node def _best(self, a: Node, b: Node): if self.directly_to_adjacent and a.position.distance( self.player.position) == 1 and a.moves_left > b.moves_left: return a if self.directly_to_adjacent and b.position.distance( self.player.position) == 1 and b.moves_left > a.moves_left: return b a_moves_left = a.moves_left + a.gfis_left b_moves_left = b.moves_left + b.gfis_left block = a.block_dice is not None foul = a.foul_roll is not None if a.prob > b.prob: return a if b.prob > a.prob: return b if foul and a.foul_roll < b.foul_roll: return a if foul and b.foul_roll < a.foul_roll: return b if block and a.block_dice > b.block_dice: return a if block and b.block_dice > a.block_dice: return b if a_moves_left > b_moves_left: return a if b_moves_left > a_moves_left: return b if a.euclidean_distance < b.euclidean_distance: return a if b.euclidean_distance < a.euclidean_distance: return b return None def _dominant(self, a: Node, b: Node): if self.directly_to_adjacent and a.position.distance( self.player.position) == 1 and a.moves_left > b.moves_left: return a if self.directly_to_adjacent and b.position.distance( self.player.position) == 1 and b.moves_left > a.moves_left: return b a_moves_left = a.moves_left + a.gfis_left b_moves_left = b.moves_left + b.gfis_left # TODO: Write out as above if a.prob > b.prob and ( a.foul_roll is None or a.foul_roll <= b.foul_roll) and ( a.block_dice is None or a.block_dice >= b.block_dice) and ( a_moves_left > b_moves_left or (a_moves_left == b_moves_left and a.euclidean_distance < b.euclidean_distance)): return a if b.prob > a.prob and ( b.foul_roll is None or b.foul_roll <= a.foul_roll) and ( b.block_dice is None or b.block_dice >= a.block_dice) and ( b_moves_left > a_moves_left or (b_moves_left == a_moves_left and b.euclidean_distance < a.euclidean_distance)): return b return None def _clear(self): for y in range(self.game.arena.height): for x in range(self.game.arena.width): node = self.nodes[y][x] if node is not None: before = self.locked_nodes[y][x] if before is None or self._best(node, before) == node: self.locked_nodes[y][x] = node self.nodes[y][x] = None self.open_set = PriorityQueue() def _prepare_nodes(self): if len(self.risky_sets) > 0: probs = sorted(self.risky_sets.keys()) self.current_prob = probs[-1] for node in self.risky_sets[probs[-1]]: best_before = self.locked_nodes[node.position.y][ node.position.x] if best_before is not None and self._dominant( best_before, node) == best_before: continue existing_node = self.nodes[node.position.y][node.position.x] if existing_node is None or self._best(existing_node, node) == node: self.open_set.put((node.euclidean_distance, node)) self.nodes[node.position.y][node.position.x] = node del self.risky_sets[probs[-1]] def _expansion(self, target=None): while not self.open_set.empty(): _, best_node = self.open_set.get() self._expand(best_node, target) def _collect_paths(self, target=None): if type(target) == Square: node = self.locked_nodes[target.y][target.x] if node is not None: return [self._collect_path(node)] return [] paths = [] for y in range(self.game.arena.height): for x in range(self.game.arena.width): if self.player.position.x == x and self.player.position.y == y: continue if type(target) == int and not target == x: continue node = self.locked_nodes[y][x] if node is not None: paths.append(self._collect_path(node)) return paths def _collect_path(self, node): prob = node.prob steps = [node.position] rolls = [node.rolls] block_dice = node.block_dice foul_roll = node.foul_roll handoff_roll = node.handoff_roll node = node.parent while node is not None: steps.append(node.position) rolls.append(node.rolls) node = node.parent steps = list(reversed(steps))[1:] rolls = list(reversed(rolls))[1:] return Path(steps, prob=prob, rolls=rolls, block_dice=block_dice, foul_roll=foul_roll, handoff_roll=handoff_roll)