Beispiel #1
0
def expand_bounce(game: botbowl.Game, parent: Node) -> Node:
    # noinspection PyTypeChecker
    active_proc: procedures.Bounce = game.get_procedure()
    assert type(active_proc) is procedures.Bounce

    new_parent = ChanceNode(game, parent)

    ball_pos = active_proc.piece.position

    active_player = game.get_active_player()
    adj_squares = game.get_adjacent_squares(ball_pos, occupied=False)
    sq_to_num_tz = {
        sq: game.num_tackle_zones_at(active_player, sq)
        for sq in adj_squares
    }
    num_tz_to_sq = {}

    for sq, num_tz in sq_to_num_tz.items():
        num_tz_to_sq.setdefault(num_tz, []).append(sq)

    for num_tz, count in Counter(sq_to_num_tz.values()).items():
        possible_squares = num_tz_to_sq[num_tz]
        square = np.random.choice(possible_squares, 1)[0]
        assert type(square) == botbowl.Square

        delta_x = square.x - ball_pos.x
        delta_y = square.y - ball_pos.y

        roll = botbowl.D8.d8_from_xy[(delta_x, delta_y)]
        with only_fixed_rolls(game, d8=[roll]):
            game.step()
        new_node = expand_none_action(game, new_parent)
        new_parent.connect_child(new_node, prob=count / 8)
        assert game.get_step() == new_parent.step_nbr

    sum_prob = sum(new_parent.child_probability)
    new_parent.child_probability = [
        prob / sum_prob for prob in new_parent.child_probability
    ]

    assert sum(new_parent.child_probability) == approx(1.0, abs=1e-9)
    assert game.get_step() == new_parent.step_nbr
    return new_parent
Beispiel #2
0
    def _make_plan(self, game: botbowl.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.extend(
                    path_to_move_actions(game, ball_carrier, td_path))
                #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.append(
                        Action(ActionType.START_HANDOFF, player=ball_carrier))
                    self.actions.extend(
                        path_to_move_actions(game, ball_carrier, handoff_path))
                    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) and game.num_tackle_zones_at(
                                ball_carrier, path.get_last_step()) == 0:
                        best_path = path
                        best_distance = distance_to_endzone
                if best_path is not None:
                    self.actions.append(
                        Action(ActionType.START_MOVE, player=ball_carrier))
                    self.actions.extend(
                        path_to_move_actions(game, ball_carrier, best_path))
                    #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))
                self.actions.extend(
                    path_to_move_actions(game, pickup_player, pickup_path))
                #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 and game.get_opp_endzone_x(
                            self.my_team) != game.get_ball_position().x:
                    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) and game.num_tackle_zones_at(
                                    pickup_player, path.get_last_step()) == 0:
                            best_path = path
                            best_distance = distance_to_endzone
                    if best_path is not None:
                        self.actions.extend(
                            path_to_move_actions(game,
                                                 pickup_player,
                                                 best_path,
                                                 do_assertions=False))
                        #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, player)
                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
                                           ) and game.num_tackle_zones_at(
                                               player, path.get_last_step()):
                        best_path = path
                        best_distance = distance_to_endzone
                if best_path is not None:
                    self.actions.append(
                        Action(ActionType.START_MOVE, player=player))
                    self.actions.extend(
                        path_to_move_actions(game, player, best_path))
                    #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:
                        defender = game.get_player_at(path.get_last_step())
                        if defender is None:
                            continue
                        from_position = path.steps[-2] if len(
                            path.steps) > 1 else blitzer.position
                        p_self, p_opp, p_fumble_self, p_fumble_opp = game.get_blitz_probs(
                            blitzer, from_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.extend(
                    path_to_move_actions(game, best_blitz_attacker,
                                         best_blitz_path))
                #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.extend(
                                path_to_move_actions(game, player, path))
                            #print(f"Make cage around towards ball carrier {player.role.name}")
                            return

        # Scan for assist positions
        assist_positions = set()
        for player in game.get_opp_team(self.my_team).players:
            if player.position is None or not player.state.up:
                continue
            for opponent in game.get_adjacent_opponents(player, down=False):
                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.add(open_position)

        #print("8. Move non-marked players to assist")
        for player in open_players:
            for path in pf.get_all_paths(game, player):
                if path.prob < 1.0 or path.get_last_step(
                ) not in assist_positions:
                    continue
                self.actions.append(
                    Action(ActionType.START_MOVE, player=player))
                self.actions.extend(path_to_move_actions(game, player, path))
                # print(f"Move assister {player.role.name} to {path.get_last_step().to_json}")
                return

        #print("9. Move towards the ball")
        for player in open_players:
            if player == ball_carrier or game.num_tackle_zones_in(player) > 0:
                continue

            shortest_distance = None
            path = None

            if ball_carrier is None:
                for p in pf.get_all_paths(game, player):
                    distance = p.get_last_step().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:
                for p in pf.get_all_paths(game, player):
                    distance = p.get_last_step().distance(
                        ball_carrier.position)
                    if shortest_distance is None or (
                            p.prob == 1 and distance < shortest_distance):
                        shortest_distance = distance
                        path = p

            if path is not None:
                self.actions.append(
                    Action(ActionType.START_MOVE, player=player))
                self.actions.extend(path_to_move_actions(game, player, path))
                #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))