Пример #1
0
def test_cant_buy_more_than_max_card():
    players = [SimplePlayer(Color.RED), SimplePlayer(Color.BLUE)]
    state = State(players)

    with pytest.raises(ValueError):  # not enough money
        apply_action(
            state,
            Action(players[0].color, ActionType.BUY_DEVELOPMENT_CARD, None))

    player_deck_replenish(state, players[0].color, SHEEP, 26)
    player_deck_replenish(state, players[0].color, WHEAT, 26)
    player_deck_replenish(state, players[0].color, ORE, 26)

    for i in range(25):
        apply_action(
            state,
            Action(players[0].color, ActionType.BUY_DEVELOPMENT_CARD, None))

    # assert must have all victory points
    assert player_num_dev_cards(state, players[0].color) == 25
    assert get_dev_cards_in_hand(state, players[0].color, VICTORY_POINT) == 5

    with pytest.raises(ValueError):  # not enough cards in bank
        apply_action(
            state,
            Action(players[0].color, ActionType.BUY_DEVELOPMENT_CARD, None))

    assert player_num_resource_cards(state, players[0].color) == 3
Пример #2
0
def robber_possibilities(state, color) -> List[Action]:
    actions = []
    for (coordinate, tile) in state.board.map.land_tiles.items():
        if coordinate == state.board.robber_coordinate:
            continue  # ignore. must move robber.

        # each tile can yield a (move-but-cant-steal) action or
        #   several (move-and-steal-from-x) actions.
        to_steal_from = set()  # set of player_indexs
        for _, node_id in tile.nodes.items():
            building = state.board.buildings.get(node_id, None)
            if building is not None:
                candidate_color = building[0]
                if (player_num_resource_cards(state, candidate_color) >= 1
                        and color != candidate_color  # can't play yourself
                    ):
                    to_steal_from.add(candidate_color)

        if len(to_steal_from) == 0:
            actions.append(
                Action(color, ActionType.MOVE_ROBBER,
                       (coordinate, None, None)))
        else:
            for enemy_color in to_steal_from:
                actions.append(
                    Action(color, ActionType.MOVE_ROBBER,
                           (coordinate, enemy_color, None)))

    return actions
Пример #3
0
def test_end_turn_goes_to_next_player(fake_roll_dice):
    fake_roll_dice.return_value = (1, 2)  # not a 7

    players = [SimplePlayer(Color.RED), SimplePlayer(Color.BLUE)]
    game = Game(players)
    actions = []
    while not any(
        a.action_type == ActionType.ROLL for a in game.state.playable_actions
    ):
        actions.append(game.play_tick())

    p0_color = game.state.colors[0]
    p1_color = game.state.colors[1]
    assert (
        game.state.current_prompt == ActionPrompt.PLAY_TURN
        and game.state.current_color() == p0_color
    )
    assert game.state.playable_actions == [Action(p0_color, ActionType.ROLL, None)]

    game.execute(Action(p0_color, ActionType.ROLL, None))
    assert game.state.current_prompt == ActionPrompt.PLAY_TURN
    assert game.state.current_color() == p0_color
    assert player_has_rolled(game.state, p0_color)
    assert Action(p0_color, ActionType.END_TURN, None) in game.state.playable_actions

    game.execute(Action(p0_color, ActionType.END_TURN, None))
    assert game.state.current_prompt == ActionPrompt.PLAY_TURN
    assert game.state.current_color() == p1_color
    assert not player_has_rolled(game.state, p0_color)
    assert not player_has_rolled(game.state, p1_color)
    assert game.state.playable_actions == [Action(p1_color, ActionType.ROLL, None)]
Пример #4
0
def action_from_json(data):
    color = Color[data[0]]
    action_type = ActionType[data[1]]
    if action_type == ActionType.BUILD_ROAD:
        action = Action(color, action_type, tuple(data[2]))
    elif action_type == ActionType.MARITIME_TRADE:
        value = tuple(data[2])
        action = Action(color, action_type, value)
    else:
        action = Action(color, action_type, data[2])
    return action
Пример #5
0
def normalize_action(action):
    normalized = action
    if normalized.action_type == ActionType.ROLL:
        return Action(action.color, action.action_type, None)
    elif normalized.action_type == ActionType.MOVE_ROBBER:
        return Action(action.color, action.action_type, action.value[0])
    elif normalized.action_type == ActionType.BUILD_ROAD:
        return Action(action.color, action.action_type, tuple(sorted(action.value)))
    elif normalized.action_type == ActionType.BUY_DEVELOPMENT_CARD:
        return Action(action.color, action.action_type, None)
    elif normalized.action_type == ActionType.DISCARD:
        return Action(action.color, action.action_type, None)
    return normalized
Пример #6
0
def test_play_monopoly_player_steals_cards():
    player_to_act = SimplePlayer(Color.RED)
    player_to_steal_from_1 = SimplePlayer(Color.BLUE)
    player_to_steal_from_2 = SimplePlayer(Color.ORANGE)
    players = [player_to_act, player_to_steal_from_1, player_to_steal_from_2]
    state = State(players)

    player_deck_replenish(state, player_to_act.color, MONOPOLY)
    player_deck_replenish(state, player_to_steal_from_1.color, ORE, 3)
    player_deck_replenish(state, player_to_steal_from_1.color, WHEAT, 1)
    player_deck_replenish(state, player_to_steal_from_2.color, ORE, 2)
    player_deck_replenish(state, player_to_steal_from_2.color, WHEAT, 1)

    action_to_execute = Action(player_to_act.color, ActionType.PLAY_MONOPOLY,
                               ORE)
    apply_action(state, action_to_execute)

    assert player_num_resource_cards(state, player_to_act.color, ORE) == 5
    assert player_num_resource_cards(state, player_to_steal_from_1.color,
                                     ORE) == 0
    assert player_num_resource_cards(state, player_to_steal_from_1.color,
                                     WHEAT) == 1
    assert player_num_resource_cards(state, player_to_steal_from_2.color,
                                     ORE) == 0
    assert player_num_resource_cards(state, player_to_steal_from_2.color,
                                     WHEAT) == 1
Пример #7
0
def maritime_trade_possibilities(state, color) -> List[Action]:
    trade_offers = set()

    # Get lowest rate per resource
    port_resources = set(state.board.get_player_port_resources(color))
    rates: Dict[FastResource, int] = {
        WOOD: 4,
        BRICK: 4,
        SHEEP: 4,
        WHEAT: 4,
        ORE: 4
    }
    if None in port_resources:
        rates = {WOOD: 3, BRICK: 3, SHEEP: 3, WHEAT: 3, ORE: 3}
    for resource in port_resources:
        if resource != None:
            rates[resource] = 2

    # For resource in hand
    for resource in RESOURCES:
        amount = player_num_resource_cards(state, color, resource)
        if amount >= rates[resource]:
            resource_out: List[Any] = [resource] * rates[resource]
            resource_out += [None] * (4 - rates[resource])
            for j_resource in RESOURCES:
                if (resource != j_resource and freqdeck_count(
                        state.resource_freqdeck, j_resource) > 0):
                    trade_offer = tuple(resource_out + [j_resource])
                    trade_offers.add(trade_offer)

    return list(
        map(lambda t: Action(color, ActionType.MARITIME_TRADE, t),
            trade_offers))
Пример #8
0
def test_defeating_your_own_largest_army_doesnt_give_more_vps():
    # Arrange: Buy all dev cards
    players = [SimplePlayer(Color.RED), SimplePlayer(Color.BLUE)]
    state = State(players)
    player_deck_replenish(state, players[0].color, SHEEP, 26)
    player_deck_replenish(state, players[0].color, WHEAT, 26)
    player_deck_replenish(state, players[0].color, ORE, 26)
    for i in range(25):
        apply_action(
            state,
            Action(players[0].color, ActionType.BUY_DEVELOPMENT_CARD, None))
    assert get_largest_army(state) == (None, None)
    assert get_actual_victory_points(state, Color.RED) == 5

    # Act - Assert
    play_dev_card(state, Color.RED, KNIGHT)
    play_dev_card(state, Color.RED, KNIGHT)
    play_dev_card(state, Color.RED, KNIGHT)
    assert get_largest_army(state) == (Color.RED, 3)
    assert get_actual_victory_points(state, Color.RED) == 7

    # Act - Assert
    play_dev_card(state, Color.RED, KNIGHT)
    assert get_largest_army(state) == (Color.RED, 4)
    assert get_actual_victory_points(state, Color.RED) == 7
Пример #9
0
def test_play_monopoly_no_monopoly_card():
    players = [SimplePlayer(Color.RED), SimplePlayer(Color.BLUE)]
    game = Game(players)

    action_to_execute = Action(players[0].color, ActionType.PLAY_MONOPOLY, ORE)

    with pytest.raises(ValueError):  # no monopoly
        game.execute(action_to_execute)
Пример #10
0
def test_sequence():
    players = [
        SimplePlayer(Color.RED),
        SimplePlayer(Color.BLUE),
        SimplePlayer(Color.WHITE),
        SimplePlayer(Color.ORANGE),
    ]
    state = State(players)

    p0_color = state.colors[0]
    assert state.current_prompt == ActionPrompt.BUILD_INITIAL_SETTLEMENT
    assert Action(p0_color, ActionType.BUILD_SETTLEMENT,
                  0) in state.playable_actions
    assert Action(p0_color, ActionType.BUILD_SETTLEMENT,
                  50) in state.playable_actions

    apply_action(state, state.playable_actions[0])
Пример #11
0
def test_play_year_of_plenty_no_year_of_plenty_card():
    players = [SimplePlayer(Color.RED), SimplePlayer(Color.BLUE)]
    game = Game(players)

    action_to_execute = Action(
        players[0].color, ActionType.PLAY_YEAR_OF_PLENTY, [ORE, WHEAT]
    )

    with pytest.raises(ValueError):  # no year of plenty card
        game.execute(action_to_execute)
Пример #12
0
def test_execute_action_on_copies_doesnt_conflict():
    players = [
        SimplePlayer(Color.RED),
        SimplePlayer(Color.BLUE),
        SimplePlayer(Color.WHITE),
        SimplePlayer(Color.ORANGE),
    ]
    game = Game(players)
    p0_color = game.state.colors[0]
    game.execute(Action(p0_color, ActionType.BUILD_SETTLEMENT, 0))

    action = Action(p0_color, ActionType.BUILD_ROAD, (0, 1))

    game_copy = game.copy()
    game_copy.execute(action)

    game_copy = game.copy()
    game_copy.execute(action)

    game.execute(action)
Пример #13
0
def initial_road_possibilities(state, color) -> List[Action]:
    # Must be connected to last settlement
    last_settlement_node_id = state.buildings_by_color[color][
        BuildingType.SETTLEMENT][-1]

    buildable_edges = filter(
        lambda edge: last_settlement_node_id in edge,
        state.board.buildable_edges(color),
    )
    return [
        Action(color, ActionType.BUILD_ROAD, edge) for edge in buildable_edges
    ]
Пример #14
0
def test_moving_robber_steals_correctly():
    players = [SimplePlayer(Color.RED), SimplePlayer(Color.BLUE)]
    state = State(players)

    player_deck_replenish(state, players[1].color, WHEAT, 1)
    state.board.build_settlement(Color.BLUE, 3, initial_build_phase=True)

    action = Action(players[0].color, ActionType.MOVE_ROBBER,
                    ((2, 0, -2), None, None))
    apply_action(state, action)
    assert player_num_resource_cards(state, players[0].color) == 0
    assert player_num_resource_cards(state, players[1].color) == 1

    action = Action(
        players[0].color,
        ActionType.MOVE_ROBBER,
        ((0, 0, 0), players[1].color, WHEAT),
    )
    apply_action(state, action)
    assert player_num_resource_cards(state, players[0].color) == 1
    assert player_num_resource_cards(state, players[1].color) == 0
Пример #15
0
def road_building_possibilities(state, color) -> List[Action]:
    key = player_key(state, color)

    has_money = player_resource_freqdeck_contains(state, color,
                                                  ROAD_COST_FREQDECK)
    has_roads_available = state.player_state[f"{key}_ROADS_AVAILABLE"] > 0

    if has_money and has_roads_available:
        buildable_edges = state.board.buildable_edges(color)
        return [
            Action(color, ActionType.BUILD_ROAD, edge)
            for edge in buildable_edges
        ]
    else:
        return []
Пример #16
0
def city_possibilities(state, color) -> List[Action]:
    key = player_key(state, color)

    has_money = player_resource_freqdeck_contains(state, color,
                                                  CITY_COST_FREQDECK)
    has_cities_available = state.player_state[f"{key}_CITIES_AVAILABLE"] > 0

    if has_money and has_cities_available:
        return [
            Action(color, ActionType.BUILD_CITY,
                   node_id) for node_id in get_player_buildings(
                       state, color, BuildingType.SETTLEMENT)
        ]
    else:
        return []
Пример #17
0
def test_play_year_of_plenty_not_enough_resources():
    players = [SimplePlayer(Color.RED), SimplePlayer(Color.BLUE)]
    player_to_act = players[0]
    game = Game(players)
    game.state.resource_freqdeck = [0, 0, 0, 0, 0]
    player_deck_replenish(game.state, player_to_act.color, YEAR_OF_PLENTY)

    action_to_execute = Action(
        player_to_act.color,
        ActionType.PLAY_YEAR_OF_PLENTY,
        [ORE, WHEAT],
    )

    with pytest.raises(ValueError):  # not enough cards in bank
        game.execute(action_to_execute)
Пример #18
0
def settlement_possibilities(state,
                             color,
                             initial_build_phase=False) -> List[Action]:
    if initial_build_phase:
        buildable_node_ids = state.board.buildable_node_ids(
            color, initial_build_phase=True)
        return [
            Action(color, ActionType.BUILD_SETTLEMENT, node_id)
            for node_id in buildable_node_ids
        ]
    else:
        key = player_key(state, color)
        has_money = player_resource_freqdeck_contains(
            state, color, SETTLEMENT_COST_FREQDECK)
        has_settlements_available = (
            state.player_state[f"{key}_SETTLEMENTS_AVAILABLE"] > 0)
        if has_money and has_settlements_available:
            buildable_node_ids = state.board.buildable_node_ids(color)
            return [
                Action(color, ActionType.BUILD_SETTLEMENT, node_id)
                for node_id in buildable_node_ids
            ]
        else:
            return []
Пример #19
0
def test_can_only_play_one_dev_card_per_turn():
    players = [
        SimplePlayer(Color.RED),
        SimplePlayer(Color.BLUE),
        SimplePlayer(Color.WHITE),
        SimplePlayer(Color.ORANGE),
    ]
    state = State(players)

    player_deck_replenish(state, players[0].color, YEAR_OF_PLENTY, 2)
    action = Action(players[0].color, ActionType.PLAY_YEAR_OF_PLENTY,
                    2 * [BRICK])
    apply_action(state, action)
    with pytest.raises(ValueError):  # shouldnt be able to play two dev cards
        apply_action(state, action)
Пример #20
0
def test_trade_execution():
    players = [
        SimplePlayer(Color.RED),
        SimplePlayer(Color.BLUE),
        SimplePlayer(Color.WHITE),
        SimplePlayer(Color.ORANGE),
    ]
    state = State(players)

    player_deck_replenish(state, players[0].color, BRICK, 4)
    trade_offer = tuple([BRICK] * 4 + [ORE])
    action = Action(players[0].color, ActionType.MARITIME_TRADE, trade_offer)
    apply_action(state, action)

    assert player_num_resource_cards(state, players[0].color) == 1
    assert sum(state.resource_freqdeck) == 19 * 5 + 4 - 1
Пример #21
0
def test_buying_road_is_payed_for():
    players = [SimplePlayer(Color.RED), SimplePlayer(Color.BLUE)]
    state = State(players)

    state.is_initial_build_phase = False
    state.board.build_settlement(players[0].color, 3, True)
    action = Action(players[0].color, ActionType.BUILD_ROAD, (3, 4))
    player_freqdeck_add(
        state,
        players[0].color,
        freqdeck_from_listdeck([WOOD, BRICK]),
    )
    apply_action(state, action)

    assert player_num_resource_cards(state, players[0].color, WOOD) == 0
    assert player_num_resource_cards(state, players[0].color, BRICK) == 0
Пример #22
0
def test_discard_config(fake_roll_dice):
    fake_roll_dice.return_value = (1, 6)
    players = [SimplePlayer(Color.RED), SimplePlayer(Color.BLUE)]
    game = Game(players, discard_limit=10)
    while not any(
        a.action_type == ActionType.ROLL for a in game.state.playable_actions
    ):
        game.play_tick()

    until_nine = 9 - player_num_resource_cards(game.state, players[1].color)
    player_deck_replenish(game.state, players[1].color, WHEAT, until_nine)
    assert player_num_resource_cards(game.state, players[1].color) == 9
    game.play_tick()  # should be p0 rolling.

    assert game.state.playable_actions != [
        Action(players[1].color, ActionType.DISCARD, None)
    ]
Пример #23
0
def generate_playable_actions(state) -> List[Action]:
    action_prompt = state.current_prompt
    color = state.current_color()

    if action_prompt == ActionPrompt.BUILD_INITIAL_SETTLEMENT:
        return settlement_possibilities(state, color, True)
    elif action_prompt == ActionPrompt.BUILD_INITIAL_ROAD:
        return initial_road_possibilities(state, color)
    elif action_prompt == ActionPrompt.MOVE_ROBBER:
        return robber_possibilities(state, color)
    elif action_prompt == ActionPrompt.PLAY_TURN:
        if state.is_road_building:
            actions = road_building_possibilities(state, color)
        elif not player_has_rolled(state, color):
            actions = [Action(color, ActionType.ROLL, None)]
            if player_can_play_dev(state, color, "KNIGHT"):
                actions.append(Action(color, ActionType.PLAY_KNIGHT_CARD,
                                      None))
        else:
            actions = [Action(color, ActionType.END_TURN, None)]
            actions.extend(road_building_possibilities(state, color))
            actions.extend(settlement_possibilities(state, color))
            actions.extend(city_possibilities(state, color))

            can_buy_dev_card = (player_can_afford_dev_card(state, color)
                                and len(state.development_listdeck) > 0)
            if can_buy_dev_card:
                actions.append(
                    Action(color, ActionType.BUY_DEVELOPMENT_CARD, None))

            # Play Dev Cards
            if player_can_play_dev(state, color, "YEAR_OF_PLENTY"):
                actions.extend(
                    year_of_plenty_possibilities(color,
                                                 state.resource_freqdeck))
            if player_can_play_dev(state, color, "MONOPOLY"):
                actions.extend(monopoly_possibilities(color))
            if player_can_play_dev(state, color, "KNIGHT"):
                actions.append(Action(color, ActionType.PLAY_KNIGHT_CARD,
                                      None))
            if (player_can_play_dev(state, color, "ROAD_BUILDING")
                    and len(road_building_possibilities(state, color)) > 0):
                actions.append(
                    Action(color, ActionType.PLAY_ROAD_BUILDING, None))

            # Trade
            actions.extend(maritime_trade_possibilities(state, color))
        return actions
    elif action_prompt == ActionPrompt.DISCARD:
        return discard_possibilities(color)
    else:
        raise RuntimeError("Unknown ActionPrompt")
Пример #24
0
def year_of_plenty_possibilities(color, freqdeck: List[int]) -> List[Action]:
    options = set()
    for i, first_card in enumerate(RESOURCES):
        for j in range(i, len(RESOURCES)):
            second_card = RESOURCES[j]  # doing it this way to not repeat

            to_draw = freqdeck_from_listdeck([first_card, second_card])
            if freqdeck_contains(freqdeck, to_draw):
                options.add((first_card, second_card))
            else:  # try allowing player select 1 card only.
                if freqdeck_can_draw(freqdeck, 1, first_card):
                    options.add((first_card, ))
                if freqdeck_can_draw(freqdeck, 1, second_card):
                    options.add((second_card, ))

    return list(
        map(
            lambda cards: Action(color, ActionType.PLAY_YEAR_OF_PLENTY,
                                 tuple(cards)),
            options,
        ))
Пример #25
0
def test_play_year_of_plenty_gives_player_resources():
    players = [SimplePlayer(Color.RED), SimplePlayer(Color.BLUE)]
    state = State(players)

    player_to_act = players[0]
    player_deck_replenish(state, player_to_act.color, YEAR_OF_PLENTY, 1)

    action_to_execute = Action(player_to_act.color,
                               ActionType.PLAY_YEAR_OF_PLENTY, [ORE, WHEAT])

    apply_action(state, action_to_execute)

    for card_type in RESOURCES:
        if card_type == ORE or card_type == WHEAT:
            assert player_num_resource_cards(state, player_to_act.color,
                                             card_type) == 1
            assert freqdeck_count(state.resource_freqdeck, card_type) == 18
        else:
            assert player_num_resource_cards(state, player_to_act.color,
                                             card_type) == 0
            assert freqdeck_count(state.resource_freqdeck, card_type) == 19
    assert get_dev_cards_in_hand(state, player_to_act.color,
                                 YEAR_OF_PLENTY) == 0
Пример #26
0
def monopoly_possibilities(color) -> List[Action]:
    return [
        Action(color, ActionType.PLAY_MONOPOLY, card) for card in RESOURCES
    ]
Пример #27
0
def execute_spectrum(game, action):
    """Returns [(game_copy, proba), ...] tuples for result of given action.
    Result probas should add up to 1. Does not modify self"""
    deterministic_actions = set([
        ActionType.END_TURN,
        ActionType.BUILD_SETTLEMENT,
        ActionType.BUILD_ROAD,
        ActionType.BUILD_CITY,
        ActionType.PLAY_KNIGHT_CARD,
        ActionType.PLAY_YEAR_OF_PLENTY,
        ActionType.PLAY_ROAD_BUILDING,
        ActionType.MARITIME_TRADE,
        ActionType.
        DISCARD,  # for simplicity... ok if reality is slightly different
        ActionType.
        PLAY_MONOPOLY,  # for simplicity... we assume good card-counting and bank is visible...
    ])
    if action.action_type in deterministic_actions:
        copy = game.copy()
        copy.execute(action, validate_action=False)
        return [(copy, 1)]
    elif action.action_type == ActionType.BUY_DEVELOPMENT_CARD:
        results = []
        for card in DEVELOPMENT_CARDS:
            option_action = Action(action.color, action.action_type, card)
            option_game = game.copy()
            try:
                option_game.execute(option_action, validate_action=False)
            except Exception:
                # ignore exceptions, since player might imagine impossible outcomes.
                # ignoring means the value function of this node will be flattened,
                # to the one before.
                pass
            results.append((option_game, starting_devcard_proba(card)))
        return results
    elif action.action_type == ActionType.ROLL:
        results = []
        for roll in range(2, 13):
            outcome = (roll // 2, math.ceil(roll / 2))

            option_action = Action(action.color, action.action_type, outcome)
            option_game = game.copy()
            option_game.execute(option_action, validate_action=False)
            results.append((option_game, number_probability(roll)))
        return results
    elif action.action_type == ActionType.MOVE_ROBBER:
        (coordinate, robbed_color, _) = action.value
        if robbed_color is None:  # no one to steal, then deterministic
            copy = game.copy()
            copy.execute(action, validate_action=False)
            return [(copy, 1)]
        else:
            results = []
            for card in RESOURCES:
                option_action = Action(
                    action.color,
                    action.action_type,
                    (coordinate, robbed_color, card),
                )
                option_game = game.copy()
                try:
                    option_game.execute(option_action, validate_action=False)
                except Exception:
                    # ignore exceptions, since player might imagine impossible outcomes.
                    # ignoring means the value function of this node will be flattened,
                    # to the one before.
                    pass
                results.append((option_game, 1 / 5.0))
            return results
    else:
        raise RuntimeError("Unknown ActionType " + str(action.action_type))
Пример #28
0
def discard_possibilities(color) -> List[Action]:
    return [Action(color, ActionType.DISCARD, None)]