예제 #1
0
 def start_handoff_actions(self, game: botbowl.Game,
                           action_choice) -> ActionProbList:
     ball = game.get_ball()
     if ball is not None and ball.is_carried:
         if ball.position in action_choice.positions:
             return [(Action(ActionType.START_HANDOFF,
                             position=[ball.position]), 1)]
     return []
예제 #2
0
    def move_actions(self, game: botbowl.Game,
                     action_choice) -> ActionProbList:
        if game.get_player_action_type(
        ) in self.player_actiontypes_without_move_actions:
            return []

        action_probs = []

        player = game.get_active_player()
        ball_carrier = game.get_ball_carrier()
        is_ball_carrier = player is game.get_ball_carrier()

        if player.state.moves > 0 and not is_ball_carrier:
            return []
        elif is_ball_carrier and player.state.moves > 0:
            action_probs.append((Action(ActionType.END_PLAYER_TURN), 1))

        ball = game.get_ball()

        ball_on_floor_pos = game.get_ball(
        ).position if not ball.is_carried else None
        opp_ball_carrier = ball_carrier if ball_carrier is not None and ball_carrier.team is not game.active_team else None

        is_home = player.team is game.state.home_team

        for pos in action_choice.positions:
            prob = 1
            if ball_on_floor_pos is not None:
                if pos == ball_on_floor_pos:
                    prob = 3
                elif pos.distance(ball_on_floor_pos) == 1:
                    prob = 2
            elif opp_ball_carrier is not None and pos.distance(
                    opp_ball_carrier.position):
                prob = 2

            elif is_ball_carrier and game.arena.is_in_opp_endzone(
                    pos, is_home):
                prob = 2

            action = Action(ActionType.MOVE, position=pos)
            action_probs.append((action, prob))

        return action_probs
예제 #3
0
def get_heuristic(game: botbowl.Game) -> HeuristicVector:
    """
    Heuristic based on game state, calculated from home teams perspective
    zero sum, meaning away team's heuristic is negative of home team's heuristic
    :returns: array with different heuristics, multiply it with
    """
    score, tv_on_pitch, ball_position, ball_carried, ball_marked = 0.0, 0.0, 0.0, 0.0, 0.0
    home = game.state.home_team
    away = game.state.away_team

    score += home.state.score - away.state.score

    tv_on_pitch += sum(p.role.cost
                       for p in game.get_players_on_pitch(team=home))
    tv_on_pitch -= sum(p.role.cost
                       for p in game.get_players_on_pitch(team=away))
    tv_on_pitch /= 50000.0  # normalized to cost of lineman

    ball = game.get_ball()
    if ball is not None and ball.position is not None:
        ball_position -= ball.position.x  # negative because home team scores at x = 0

        home_marking_ball = len(
            game.get_adjacent_players(ball.position, team=home,
                                      standing=True)) > 0
        away_marking_ball = len(
            game.get_adjacent_players(ball.position, team=away,
                                      standing=True)) > 0

        if ball.is_carried:
            ball_carrier = game.get_player_at(ball.position)
            if ball_carrier.team == home:
                ball_carried += 1
                ball_marked -= away_marking_ball
            elif ball_carrier.team == away:
                ball_carried -= 1
                ball_marked += home_marking_ball
        else:
            ball_marked += (home_marking_ball - away_marking_ball)

    return HeuristicVector(score=score,
                           tv_on_pitch=tv_on_pitch,
                           ball_position=ball_position,
                           ball_carried=ball_carried,
                           ball_marked=ball_marked)
예제 #4
0
def expand_moving(game: botbowl.Game, parent: Node) -> Node:
    # noinspection PyTypeChecker
    active_proc: Union[procedures.GFI, procedures.Dodge] = game.get_procedure()
    assert type(active_proc) is procedures.Dodge or type(
        active_proc) is procedures.GFI

    move_action_proc: procedures.MoveAction = first(
        proc for proc in reversed(game.state.stack.items)
        if isinstance(proc, procedures.MoveAction))

    is_blitz = type(move_action_proc) is procedures.BlitzAction
    is_handoff = type(move_action_proc) is procedures.HandoffAction

    player = move_action_proc.player

    if move_action_proc.steps is not None:
        final_step = move_action_proc.steps[-1]
    else:
        if is_blitz:
            block_proc: procedures.Block = first(
                filter(lambda proc: type(proc) is procedures.Block,
                       game.state.stack.items))
            final_step = block_proc.defender.position
        elif is_handoff:
            raise ValueError()
        else:
            final_step = active_proc.position

    is_pickup = game.get_ball(
    ).position == final_step and not game.get_ball().is_carried
    path = move_action_proc.paths[final_step]
    """
    This block of code sets two important variables: 
        probability_success - probability of the remaining path  
        rolls - list[int] - the remaining rolls of the path 
    Normal case we just fetch this from the path object. If we're in a rerolled proc, it's nasty... 
    """
    if active_proc.roll is None:
        probability_success = path.prob
        rolls = list(collapse(path.rolls))
        if is_pickup:
            # remove the pickup roll and probability
            rolls.pop()
            probability_success /= game.get_pickup_prob(
                active_proc.player, final_step)

    else:
        with only_fixed_rolls(game):
            game.step()

        new_proc = game.get_procedure()
        if type(new_proc) not in {procedures.GFI, procedures.Dodge}:
            assert not active_proc.reroll.use_reroll
            return expand_none_action(game, parent)

        # if we get here, it means that a reroll was used.
        assert new_proc is active_proc
        assert active_proc.roll is None
        assert active_proc.reroll is None

        current_step = active_proc.position
        try:
            assert player.position.distance(
                current_step) == 1 or is_pickup or is_blitz
        except AssertionError as e:
            raise e

        i = 0
        while path.steps[i] != current_step:
            i += 1
        remaining_current_step_rolls = path.rolls[i][:]

        if is_pickup and current_step == final_step:
            remaining_current_step_rolls.pop()

        num_current_step_remaining_rolls = len(
            list(
                filter(lambda x: type(x) in {procedures.GFI, procedures.Dodge},
                       game.state.stack.items)))
        assert num_current_step_remaining_rolls in [1, 2]
        remaining_current_step_rolls = remaining_current_step_rolls[
            -num_current_step_remaining_rolls:]

        probability_success = reduce(
            operator.mul,
            map(lambda d: (7 - d) / 6, remaining_current_step_rolls), 1.0)
        rolls = list(collapse(remaining_current_step_rolls))

        if current_step != final_step:
            step_count = game.get_step()
            if player.position != current_step:
                try:
                    game.move(player, current_step)
                except AssertionError as e:
                    raise e
            new_path = pf.get_safest_path(game,
                                          player,
                                          final_step,
                                          blitz=is_blitz)
            game.revert(step_count)

            #try:
            #    # assert new_path.steps == path.steps[-len(new_path):]  this assert can't be made because of small randomness in pathfinder
            #    assert list(collapse(new_path.rolls)) == list(collapse(path.rolls[-len(new_path):])), f"{new_path.rolls} != {path.rolls[-len(new_path):]}"
            #except AssertionError as e:
            #    raise e
            try:
                rolls.extend(collapse(new_path.rolls))
            except AttributeError as e:
                raise e
            probability_success *= new_path.prob

            if is_pickup:
                # remove the pickup roll and probability
                rolls.pop()
                probability_success /= game.get_pickup_prob(
                    active_proc.player, final_step)

    try:
        p = np.array(rolls) / sum(rolls)
        index_of_failure = np.random.choice(range(len(rolls)), 1, p=p)[0]
    except ValueError as e:
        raise e

    # STEP UNTIL FAILURE (possibly no steps at all)
    with only_fixed_rolls(game, d6=[6] * index_of_failure):
        while len(botbowl.D6.FixedRolls) > 0:
            if len(game.get_available_actions()) > 0:
                raise AttributeError("wrong")
            game.step()

    new_parent = ChanceNode(game, parent)
    debug_step_count = game.get_step()

    # SUCCESS SCENARIO
    with only_fixed_rolls(game, d6=[6] * (len(rolls) - index_of_failure)):
        while len(botbowl.D6.FixedRolls) > 0:
            if type(game.get_procedure()) not in {
                    procedures.GFI, procedures.Block, procedures.Dodge,
                    procedures.Move, procedures.MoveAction,
                    procedures.BlitzAction
            }:
                raise AttributeError("wrong")
            if len(game.get_available_actions()) > 0:
                raise AttributeError("wrong")
            if type(game.get_procedure()
                    ) is procedures.Block and not game.get_procedure().gfi:
                raise AttributeError("wrong")
            game.step()
    success_node = expand_none_action(game, new_parent, moving_handled=True)
    new_parent.connect_child(success_node, probability_success)

    assert debug_step_count == game.get_step()

    # FAILURE SCENARIO
    with only_fixed_rolls(game, d6=[1]):
        while len(botbowl.D6.FixedRolls) > 0:
            if len(game.get_available_actions()) > 0:
                raise AttributeError("wrong")
            game.step()
    fail_node = expand_none_action(game, new_parent, moving_handled=True)
    new_parent.connect_child(fail_node, 1 - probability_success)

    assert debug_step_count == game.get_step()

    return new_parent