Exemple #1
0
def expand_pickup(game: botbowl.Game, parent: Node) -> Node:
    # noinspection PyTypeChecker
    active_proc: procedures.Pickup = game.get_procedure()
    assert type(active_proc) is procedures.Pickup
    assert active_proc.roll is None

    probability_success = game.get_pickup_prob(active_proc.player,
                                               active_proc.ball.position)

    new_parent = ChanceNode(game, parent)

    # SUCCESS SCENARIO
    with only_fixed_rolls(game, d6=[6]):
        game.step()
    success_node = expand_none_action(game, new_parent, pickup_handled=True)
    new_parent.connect_child(success_node, probability_success)

    assert game.get_step() == new_parent.step_nbr

    # FAILURE SCENARIO
    assert not active_proc.player.has_skill(
        Skill.SURE_HANDS), 'cant handle sure hands yet'
    with only_fixed_rolls(game, d6=[1]):
        game.step()
    fail_node = expand_none_action(game, new_parent, pickup_handled=True)
    new_parent.connect_child(fail_node, 1 - probability_success)

    assert game.get_step() == new_parent.step_nbr

    return new_parent
Exemple #2
0
def test_pickup_score():
    game, (player, ) = get_custom_game_turn(player_positions=[(5, 5)],
                                            ball_position=(3, 3),
                                            forward_model_enabled=True,
                                            pathfinding_enabled=True)

    player.role.ma = 4
    game.set_available_actions()

    weights = HeuristicVector(score=1,
                              ball_marked=0,
                              ball_carried=0,
                              ball_position=0,
                              tv_on_pitch=0)

    tree = SearchTree(game)
    policy = MockPolicy()

    def search_select_step():
        for i in range(40):
            do_mcts_branch(tree, policy, weights, exploration_coeff=0.5)
        info: MCTS_Info = tree.root_node.info
        print("   ")
        print_node(tree.root_node, weights)
        return info.actions[np.argmax(info.visits)]

    a = search_select_step()
    assert a.action_type == ActionType.START_MOVE and a.position == player.position
    with only_fixed_rolls(game):
        game.step(a)

    tree.set_new_root(game)

    setup_node: ActionNode = first(
        filter(lambda n: n.simple_hash.find('Setup') > 0,
               tree.all_action_nodes))
    assert setup_node.get_accum_prob() == approx(2 / 3)

    a = search_select_step()
    assert a.action_type == ActionType.MOVE and a.position == Square(3, 3)
    with only_fixed_rolls(game, d6=[3]):
        game.step(a)
    tree.set_new_root(game)

    a = search_select_step()
    assert a.action_type == ActionType.MOVE and a.position.x == 1
    with only_fixed_rolls(game):
        game.step(a)
    tree.set_new_root(game)
Exemple #3
0
def expand_none_action(game: botbowl.Game,
                       parent: Node,
                       moving_handled=False,
                       pickup_handled=False) -> Node:
    """
    :param game: the game state is changed during expansion but restored to state of argument 'parent'
    :param parent: shall represent the current state of argument 'game'. game state is restored to parent.step_nbr
    :param moving_handled:
    :param pickup_handled:
    :returns: A subclass of Node:
                - ChanceNode in a nestled structure with multiple ActionNode as leaf nodes.
                - ActionNode if only one possible outcome.
        param game is changed but restored to initial state af
    Called recursively.
    """

    while len(game.state.available_actions) == 0:
        proc = game.get_procedure()
        expand_func = get_expanding_function(proc, moving_handled,
                                             pickup_handled)

        if expand_func is not None:
            assert len(botbowl.D6.FixedRolls) == 0
            return_node = expand_func(game, parent)
            assert len(botbowl.D6.FixedRolls) == 0
            game.revert(parent.step_nbr)
            return return_node

        with only_fixed_rolls(game):
            game.step()

    action_node = ActionNode(game, parent)
    game.revert(parent.step_nbr)
    assert parent.step_nbr == game.get_step()
    return action_node
Exemple #4
0
def test_set_new_root():
    game, (player1, player2,
           opp_player) = get_custom_game_turn(player_positions=[(5, 5),
                                                                (6, 6)],
                                              opp_player_positions=[(4, 4)],
                                              ball_position=(5, 5),
                                              pathfinding_enabled=True,
                                              forward_model_enabled=True)

    action_p2_1 = Action(ActionType.START_BLITZ, position=player2.position)
    action_p2_2 = Action(ActionType.BLOCK, position=Square(4, 4))

    action_p1_1 = Action(ActionType.START_MOVE, position=player1.position)
    action_p1_2 = Action(ActionType.MOVE, position=Square(1, 5))

    tree = SearchTree(deepcopy(game))
    assert tree.root_node.depth == 0

    # Move player 2
    new_node, = tree.expand_action_node(tree.root_node, action_p2_1)
    new_nodes = tree.expand_action_node(new_node, action_p2_2)

    assert new_node.depth == 1
    assert new_nodes[0].depth == 2
    assert len(tree.all_action_nodes) == 2 + 4

    # Move player 1
    new_node, = tree.expand_action_node(tree.root_node, action_p1_1)
    assert len(tree.all_action_nodes) == 2 + 4 + 1
    new_nodes = tree.expand_action_node(new_node, action_p1_2)

    assert new_node.depth == 1
    assert new_nodes[0].depth == 2
    assert len(tree.all_action_nodes) == 2 + 4 + 1 + 7

    game.step(action_p1_1)
    tree.set_new_root(game)

    assert len(tree.all_action_nodes) == 8
    assert new_node is tree.root_node
    assert new_node.depth == 0
    assert new_nodes[0].depth == 1

    with only_fixed_rolls(game, d6=[6]):
        game.step(action_p1_2)

    tree.set_new_root(game)
    tree.expand_action_node(tree.root_node,
                            Action(ActionType.SETUP_FORMATION_SPREAD))
    assert new_nodes[0] is tree.root_node
    assert len(tree.all_action_nodes) == 2

    game.step(Action(ActionType.SETUP_FORMATION_SPREAD))
    game.step(Action(ActionType.END_SETUP))
    tree.set_new_root(game)
    assert len(tree.all_action_nodes) == 1
    assert len(tree.root_node.children) == 0
Exemple #5
0
def expand_throw_in(game: botbowl.Game, parent: Node) -> Node:
    # noinspection PyTypeChecker
    active_proc: procedures.ThrowIn = game.get_procedure()
    assert type(active_proc) is procedures.ThrowIn

    d6_fixes = [3, 4] if game.arena.height > 7 else [1, 2]

    with only_fixed_rolls(game, d3=[2], d6=d6_fixes):
        game.step()

    assert active_proc is not game.get_procedure()

    return expand_none_action(game, parent)
Exemple #6
0
def handle_ko_wakeup(game: botbowl.Game, parent: Node) -> Node:
    # noinspection PyTypeChecker
    active_proc: procedures.PreKickoff = game.get_procedure()
    assert type(active_proc) is procedures.PreKickoff

    d6_fixes = [1] * len(game.get_knocked_out(active_proc.team))

    with only_fixed_rolls(game, d6=d6_fixes):
        while active_proc is game.get_procedure():
            game.step()

    assert active_proc is not game.get_procedure()

    return expand_none_action(game, parent)
Exemple #7
0
def expand_injury(game: botbowl.Game, parent: Node) -> Node:
    # noinspection PyTypeChecker
    proc: procedures.Injury = game.get_procedure()
    assert not proc.foul

    if proc.in_crowd:
        with only_fixed_rolls(game, d6=[5, 4]):  # straight to KO
            game.step()
        return expand_none_action(game, parent)

    p_removal = accumulated_prob_2d_roll[8]
    new_parent = ChanceNode(game, parent)
    expand_with_fixes(game, new_parent, p_removal, d6=[5, 4])  # KO
    expand_with_fixes(game, new_parent, 1 - p_removal, d6=[1, 1])  # Stun
    return new_parent
Exemple #8
0
def test_expand_throw_in():
    game, (attacker,
           defender) = get_custom_game_turn(player_positions=[(5, 2)],
                                            opp_player_positions=[(5, 1)],
                                            ball_position=(5, 1),
                                            forward_model_enabled=True,
                                            pathfinding_enabled=True)

    with only_fixed_rolls(game, block_dice=[BBDieResult.DEFENDER_DOWN]):
        game.step(Action(ActionType.START_BLOCK, position=attacker.position))
        game.step(Action(ActionType.BLOCK, position=defender.position))
        game.step(Action(ActionType.SELECT_DEFENDER_DOWN))

    action = Action(ActionType.PUSH, position=Square(5, 0))

    tree = SearchTree(game)
    tree.expand_action_node(tree.root_node, action)
    assert len(tree.all_action_nodes) == 2
Exemple #9
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
Exemple #10
0
def expand_action(game: botbowl.Game, action: botbowl.Action,
                  parent: ActionNode) -> Node:
    """
    :param game: game object used for calculations. Will be reverted to original state.
    :param action: action to be evaluated.
    :param parent: parent node
    :returns - list of tuples containing (Steps, probability) for each possible outcome.
             - probabilities sums to 1.0
    Not called recursively
    """
    # noinspection PyProtectedMember
    assert game._is_action_allowed(action)
    assert game.trajectory.enabled
    game.config.fast_mode = False

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

    return expand_none_action(game, parent)
Exemple #11
0
def expand_with_fixes(game, parent, probability, **fixes):
    with only_fixed_rolls(game, **fixes):
        game.step()
    new_node = expand_none_action(game, parent)
    parent.connect_child(new_node, probability)
Exemple #12
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