def test_out_of_bounds(): game = get_game_turn(empty=True) player = game.get_reserves(game.state.home_team)[0] position = Square(1, 1) game.put(player, position) for neighbor in game.get_adjacent_squares(position): if game.is_out_of_bounds(neighbor): path = pf.get_safest_path(game, player, neighbor) assert path is None
def test_neighbors(): game = get_game_turn(empty=True) player = game.get_reserves(game.state.home_team)[0] position = Square(5, 5) game.put(player, position) for neighbor in game.get_adjacent_squares(position): path = pf.get_safest_path(game, player, neighbor) assert len(path) == 1 and path.steps[0] == neighbor assert path.prob == 1.0
def test_gfi(): game = get_game_turn(empty=True) player = game.get_reserves(game.state.home_team)[0] position = Square(5, 5) game.put(player, position) for gfis in [0, 1, 2]: moves = player.get_ma() + gfis target = Square(position.x + moves, position.y) path = pf.get_safest_path(game, player, target) assert len(path.steps) == moves and path.steps[-1] == target assert path.prob == (5 / 6)**gfis
def test_avoid_path(): game = get_game_turn(empty=True) player = game.get_reserves(game.state.home_team)[0] position = Square(1, 8) game.put(player, position) opp = game.get_reserves(game.state.away_team)[0] opp_position = Square(3, 8) game.put(opp, opp_position) target_a = Square(6, 8) path = pf.get_safest_path(game, player, target_a) assert path is not None assert len(path.steps) == 6 assert path.prob == 1.0
def test_dodge_needed_path_long(): game = get_game_turn(empty=True) player = game.get_reserves(game.state.home_team)[0] position = Square(1, 8) game.put(player, position) opp = game.get_reserves(game.state.away_team)[0] opp_position = Square(3, 8) game.put(opp, opp_position) target_a = Square(9, 8) path = pf.get_safest_path(game, player, position=target_a) assert path is not None assert len(path.steps) == 8 assert path.prob == (4 / 6) * (5 / 6) * (5 / 6)
def test_all_paths(skills_and_rerolls): game = get_game_turn(empty=True) player = game.get_reserves(game.state.home_team)[0] position = Square(1, 1) game.put(player, position) skill, reroll = skills_and_rerolls if skill: player.extra_skills = [Skill.SURE_FEET] for y in range(game.arena.height): for x in range(game.arena.width): square = Square(x, y) if position != square and not game.is_out_of_bounds(square): if position.distance(square) != player.get_ma() + 2: continue # TODO: REMOVE path = pf.get_safest_path(game, player, square, allow_team_reroll=reroll) if position.distance(square) > player.get_ma() + 2: assert path is None else: p = 5 / 6 if position.distance(square) == player.get_ma() + 2: p_clean = p * p p_reroll_one = (p * (1 - p) * p) p_reroll_both = (1 - p) * p * (1 - p) * p if reroll and not skill or not reroll and skill: p = p_clean + p_reroll_one * 2 elif reroll and skill: p = p_clean + p_reroll_one * 2 + p_reroll_both else: p = p_clean assert path is not None assert path.prob == pytest.approx(p, PROP_PRECISION) elif position.distance(square) == player.get_ma() + 1: assert path is not None if reroll or skill: p = p + (1 - p) * p assert path.prob == pytest.approx(p, PROP_PRECISION) else: assert path is not None p = 1.0 assert path.prob == p
def test_sure_feet_over_ag4_dodge(sure_feet): game = get_game_turn(empty=True) player = game.get_reserves(game.state.home_team)[0] player.role.ma = 4 player.role.ag = 4 if sure_feet: player.extra_skills = [Skill.SURE_FEET] game.put(player, Square(4, 1)) opp1 = game.get_reserves(game.state.away_team)[0] game.put(opp1, Square(3, 3)) opp2 = game.get_reserves(game.state.away_team)[1] game.put(opp2, Square(5, 3)) target = Square(2, 4) path = pf.get_safest_path(game, player, target) assert path is not None if sure_feet: assert len(path.steps) == 5 p = (5 / 6) assert path.prob == p + (1 - p) * p else: assert len(path.steps) == 4 p = (5 / 6) assert path.prob == p
def _make_plan(self, game, ball_carrier): #print("1. Stand up marked players") for player in self.my_team.players: if player.position is not None and not player.state.up and not player.state.stunned and not player.state.used: if game.num_tackle_zones_in(player) > 0: self.actions.append(Action(ActionType.START_MOVE, player=player)) self.actions.append(Action(ActionType.STAND_UP)) #print(f"Stand up marked player {player.role.name}") return #print("2. Move ball carrier to endzone") if ball_carrier is not None and ball_carrier.team == self.my_team and not ball_carrier.state.used: #print("2.1 Can ball carrier score with high probability") td_path = pf.get_safest_path_to_endzone(game, ball_carrier, allow_team_reroll=True) if td_path is not None and td_path.prob >= 0.7: self.actions.append(Action(ActionType.START_MOVE, player=ball_carrier)) self.actions.append(Action(ActionType.MOVE, position=td_path.steps[-1])) #print(f"Score with ball carrier, p={td_path.prob}") return #print("2.2 Hand-off action to scoring player") if game.is_handoff_available(): # Get players in scoring range unused_teammates = [] for player in self.my_team.players: if player.position is not None and player != ball_carrier and not player.state.used and player.state.up: unused_teammates.append(player) # Find other players in scoring range handoff_p = None handoff_path = None for player in unused_teammates: if game.get_distance_to_endzone(player) > player.num_moves_left(): continue td_path = pf.get_safest_path_to_endzone(game, player, allow_team_reroll=True) if td_path is None: continue handoff_path = pf.get_safest_path(game, ball_carrier, player.position, allow_team_reroll=True) if handoff_path is None: continue p_catch = game.get_catch_prob(player, handoff=True, allow_catch_reroll=True, allow_team_reroll=True) p = td_path.prob * handoff_path.prob * p_catch if handoff_p is None or p > handoff_p: handoff_p = p handoff_path = handoff_path # Hand-off if high probability or last turn if handoff_path is not None and (handoff_p >= 0.7 or self.my_team.state.turn == 8): self.actions = [Action(ActionType.START_HANDOFF, player=ball_carrier), Action(ActionType.MOVE, handoff_path.steps[-1])] return #print("2.3 Move safely towards the endzone") if game.num_tackle_zones_in(ball_carrier) == 0: paths = pf.get_all_paths(game, ball_carrier) best_path = None best_distance = 100 target_x = game.get_opp_endzone_x(self.my_team) for path in paths: distance_to_endzone = abs(target_x - path.steps[-1].x) if path.prob == 1 and (best_path is None or distance_to_endzone < best_distance): best_path = path best_distance = distance_to_endzone if best_path is not None: steps = [] for step in best_path.steps: if game.num_tackle_zones_at(ball_carrier, step) > 0: break if len(steps) >= ball_carrier.num_moves_left(): break steps.append(step) if len(steps) > 0: self.actions.append(Action(ActionType.START_MOVE, player=ball_carrier)) for step in steps: self.actions.append(Action(ActionType.MOVE, position=step)) #print(f"Move ball carrier {ball_carrier.role.name}") return #print("3. Safe blocks") attacker, defender, p_self_up, p_opp_down, block_p_fumble_self, block_p_fumble_opp = self._get_safest_block(game) if attacker is not None and p_self_up > 0.94 and block_p_fumble_self == 0: self.actions.append(Action(ActionType.START_BLOCK, player=attacker)) self.actions.append(Action(ActionType.BLOCK, position=defender.position)) #print(f"Safe block with {attacker.role.name} -> {defender.role.name}, p_self_up={p_self_up}, p_opp_down={p_opp_down}") return #print("4. Pickup ball") if game.get_ball_carrier() is None: pickup_p = None pickup_player = None pickup_path = None for player in self.my_team.players: if player.position is not None and not player.state.used: if player.position.distance(game.get_ball_position()) <= player.get_ma() + 2: path = pf.get_safest_path(game, player, game.get_ball_position()) if path is not None: p = path.prob if pickup_p is None or p > pickup_p: pickup_p = p pickup_player = player pickup_path = path if pickup_player is not None and pickup_p > 0.33: self.actions.append(Action(ActionType.START_MOVE, player=pickup_player)) if not pickup_player.state.up: self.actions.append(Action(ActionType.STAND_UP)) for step in pickup_path.steps: self.actions.append(Action(ActionType.MOVE, position=step)) #print(f"Pick up the ball with {pickup_player.role.name}, p={pickup_p}") # Find safest path towards endzone if game.num_tackle_zones_at(pickup_player, game.get_ball_position()) == 0: paths = pf.get_all_paths(game, pickup_player, from_position=game.get_ball_position(), num_moves_used=len(pickup_path)) best_path = None best_distance = 100 target_x = game.get_opp_endzone_x(self.my_team) for path in paths: distance_to_endzone = abs(target_x - path.steps[-1].x) if path.prob == 1 and (best_path is None or distance_to_endzone < best_distance): best_path = path best_distance = distance_to_endzone if best_path is not None: steps = [] for step in best_path.steps: if game.num_tackle_zones_at(pickup_player, step) > 0: break if len(steps) + len(pickup_path.steps) >= pickup_player.get_ma(): break steps.append(step) if len(steps) > 0: self.actions.append(Action(ActionType.START_MOVE, player=ball_carrier)) for step in steps: self.actions.append(Action(ActionType.MOVE, position=step)) #print(f"- Move ball carrier {pickup_player.role.name}") return # Scan for unused players that are not marked open_players = [] for player in self.my_team.players: if player.position is not None and not player.state.used and game.num_tackle_zones_in(player) == 0: open_players.append(player) #print("5. Move receivers into scoring distance if not already") for player in open_players: if player.has_skill(Skill.CATCH) and player != ball_carrier: if game.get_distance_to_endzone(player) > player.num_moves_left(): continue paths = pf.get_all_paths(game, ball_carrier) best_path = None best_distance = 100 target_x = game.get_opp_endzone_x(self.my_team) for path in paths: distance_to_endzone = abs(target_x - path.steps[-1].x) if path.prob == 1 and (best_path is None or distance_to_endzone < best_distance): best_path = path best_distance = distance_to_endzone if best_path is not None: steps = [] for step in best_path.steps: if len(steps) >= player.get_ma() + (3 if not player.state.up else 0): break if game.num_tackle_zones_at(player, step) > 0: break if step.distance(best_path.steps[-1]) < player.get_ma(): break steps.append(step) if len(steps) > 0: self.actions.append(Action(ActionType.START_MOVE, player=player)) if not player.state.up: self.actions.append(Action(ActionType.STAND_UP)) for step in steps: self.actions.append(Action(ActionType.MOVE, position=step)) print(f"Move receiver {player.role.name}") return #print("6. Blitz with open block players") if game.is_blitz_available(): best_blitz_attacker = None best_blitz_defender = None best_blitz_score = None best_blitz_path = None for blitzer in open_players: if blitzer.position is not None and not blitzer.state.used and blitzer.has_skill(Skill.BLOCK): blitz_paths = pf.get_all_paths(game, blitzer, blitz=True) for path in blitz_paths: final_position = path.steps[-2] if len(path.steps) > 1 else blitzer.position for defender in game.get_adjacent_players(final_position, team=game.get_opp_team(blitzer.team)): p_self, p_opp, p_fumble_self, p_fumble_opp = game.get_blitz_probs(blitzer, final_position, defender) p_self_up = path.prob * (1-p_self) p_opp = path.prob * p_opp p_fumble_opp = p_fumble_opp * path.prob if blitzer == game.get_ball_carrier(): p_fumble_self = path.prob + (1 - path.prob) * p_fumble_self score = p_self_up + p_opp + p_fumble_opp - p_fumble_self if best_blitz_score is None or score > best_blitz_score: best_blitz_attacker = blitzer best_blitz_defender = defender best_blitz_score = score best_blitz_path = path if best_blitz_attacker is not None and best_blitz_score >= 1.25: self.actions.append(Action(ActionType.START_BLITZ, player=best_blitz_attacker)) self.actions.append(Action(ActionType.MOVE, position=best_blitz_path.steps[-1])) #print(f"Blitz with {best_blitz_attacker.role.name}, score={best_blitz_score}") return #print("7. Make cage around ball carrier") cage_positions = [ Square(game.get_ball_position().x - 1, game.get_ball_position().y - 1), Square(game.get_ball_position().x + 1, game.get_ball_position().y - 1), Square(game.get_ball_position().x - 1, game.get_ball_position().y + 1), Square(game.get_ball_position().x + 1, game.get_ball_position().y + 1) ] if ball_carrier is not None: for cage_position in cage_positions: if game.get_player_at(cage_position) is None and not game.is_out_of_bounds(cage_position): for player in open_players: if player == ball_carrier or player.position in cage_positions: continue if player.position.distance(cage_position) > player.num_moves_left(): continue if game.num_tackle_zones_in(player) > 0: continue path = pf.get_safest_path(game, player, cage_position) if path is not None and path.prob > 0.94: self.actions.append(Action(ActionType.START_MOVE, player=player)) self.actions.append(Action(ActionType.MOVE, position=path.steps[-1])) #print(f"Make cage around towards ball carrier {player.role.name}") return # Scan for assist positions assist_positions = [] for player in game.get_opp_team(self.my_team).players: if player.position is None or not player.state.up: continue opponents = game.get_adjacent_opponents(player, down=False) for opponent in opponents: att_str, def_str = game.get_block_strengths(player, opponent) if def_str >= att_str: for open_position in game.get_adjacent_squares(player.position, occupied=False): if len(game.get_adjacent_players(open_position, team=self.opp_team, down=False)) == 1: assist_positions.append(open_position) #print("8. Move non-marked players to assist") for player in open_players: paths = pf.get_all_paths(game, player) for assist_position in assist_positions: assist_path = None for path in paths: if path.steps[-1] == assist_position: if path.prob == 1: self.actions.append(Action(ActionType.START_MOVE, player=player)) self.actions.append(Action(ActionType.MOVE, position=path.steps[-1])) #print(f"Move assister {player.role.name} to {assist_position.to_json}") return #print("9. Move towards the ball") for player in open_players: if player == ball_carrier: continue if game.num_tackle_zones_in(player) > 0: continue if ball_carrier is None: paths = pf.get_all_paths(game, player) shortest_distance = None path = None for p in paths: distance = p.steps[-1].distance(game.get_ball_position()) if shortest_distance is None or (p.prob == 1 and distance < shortest_distance): shortest_distance = distance path = p elif ball_carrier.team != self.my_team: paths = pf.get_all_paths(game, player) shortest_distance = None path = None for p in paths: distance = p.steps[-1].distance(ball_carrier.position) if shortest_distance is None or (p.prob == 1 and distance < shortest_distance): shortest_distance = distance path = p else: continue if path is not None: if len(path.steps) > 0: self.actions.append(Action(ActionType.START_MOVE, player=player)) self.actions.append(Action(ActionType.MOVE, position=path.steps[-1])) #print(f"Move towards ball {player.role.name}") return #print("10. Risky blocks") attacker, defender, p_self_up, p_opp_down, block_p_fumble_self, block_p_fumble_opp = self._get_safest_block(game) if attacker is not None and (p_opp_down > (1-p_self_up) or block_p_fumble_opp > 0): self.actions.append(Action(ActionType.START_BLOCK, player=attacker)) self.actions.append(Action(ActionType.BLOCK, position=defender.position)) #print(f"Block with {player.role.name} -> {defender.role.name}, p_self_up={p_self_up}, p_opp_down={p_opp_down}") return #print("11. End turn") self.actions.append(Action(ActionType.END_TURN))