コード例 #1
0
    def __init__(
        self,
        players: Iterable[Player],
        seed: int = None,
        discard_limit: int = 7,
        vps_to_win: int = 10,
        catan_map: CatanMap = None,
        initialize: bool = True,
    ):
        """Creates a game (doesn't run it).

        Args:
            players (Iterable[Player]): list of players, should be at most 4.
            seed (int, optional): Random seed to use (for reproducing games). Defaults to None.
            discard_limit (int, optional): Discard limit to use. Defaults to 7.
            vps_to_win (int, optional): Victory Points needed to win. Defaults to 10.
            catan_map (CatanMap, optional): Map to use. Defaults to None.
            initialize (bool, optional): Whether to initialize. Defaults to True.
        """
        if initialize:
            self.seed = seed or random.randrange(sys.maxsize)
            random.seed(self.seed)

            self.id = str(uuid.uuid4())
            self.vps_to_win = vps_to_win
            self.state = State(players, catan_map, discard_limit=discard_limit)
コード例 #2
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
コード例 #3
0
def test_building_settlement_gives_vp():
    players = [SimplePlayer(Color.RED), SimplePlayer(Color.BLUE)]
    state = State(players)

    build_settlement(state, state.colors[0], 0, True)
    assert state.player_state["P0_VICTORY_POINTS"] == 1
    assert state.player_state["P0_ACTUAL_VICTORY_POINTS"] == 1
コード例 #4
0
def test_robber_possibilities():
    red = SimplePlayer(Color.RED)
    blue = SimplePlayer(Color.BLUE)
    orange = SimplePlayer(Color.ORANGE)
    players = [red, blue, orange]
    state = State(players)

    # one for each resource tile (excluding desert)
    assert len(robber_possibilities(state, Color.RED)) == 18

    # assert same number of possibilities, b.c. players have no cards.
    state.board.build_settlement(Color.BLUE, 3, initial_build_phase=True)
    state.board.build_settlement(Color.ORANGE, 0, initial_build_phase=True)
    assert len(robber_possibilities(state, Color.RED)) == 18

    # assert same number of possibilities, b.c. only one player to rob in this tile
    player_deck_replenish(state, orange.color, WHEAT)
    assert len(robber_possibilities(state, Color.RED)) == 18

    # now possibilites increase by 1 b.c. we have to decide to steal from blue or orange
    # Unless desert is (0,0,0)... in which case still at 18...
    player_deck_replenish(state, blue.color, WHEAT)
    possibilities = len(robber_possibilities(state, Color.RED))
    assert possibilities == 19 or (
        possibilities == 18
        and state.board.map.land_tiles[(0, 0, 0)].resource is None)
コード例 #5
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
コード例 #6
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
コード例 #7
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
コード例 #8
0
def test_playable_actions():
    players = [SimplePlayer(Color.RED), SimplePlayer(Color.BLUE)]
    state = State(players)

    actions = generate_playable_actions(state)
    assert len(actions) == 54
    assert actions[0].action_type == ActionType.BUILD_SETTLEMENT
コード例 #9
0
def test_playable_cards():
    player = SimplePlayer(Color.RED)

    state = State([player])
    player_deck_replenish(state, Color.RED, "KNIGHT")
    player_clean_turn(state, Color.RED)

    assert player_can_play_dev(state, Color.RED, "KNIGHT")
コード例 #10
0
def test_robber_possibilities_simple():
    red = SimplePlayer(Color.RED)
    blue = SimplePlayer(Color.BLUE)
    orange = SimplePlayer(Color.ORANGE)
    players = [red, blue, orange]
    state = State(players)

    # one for each resource tile (excluding desert)
    assert len(robber_possibilities(state, Color.RED)) == 18
コード例 #11
0
def test_building_city_gives_vp():
    players = [SimplePlayer(Color.RED), SimplePlayer(Color.BLUE)]
    state = State(players)

    build_settlement(state, state.colors[0], 0, True)
    player_deck_replenish(state, state.colors[0], WHEAT, 2)
    player_deck_replenish(state, state.colors[0], ORE, 2)
    build_city(state, state.colors[0], 0)
    assert state.player_state["P0_VICTORY_POINTS"] == 2
    assert state.player_state["P0_ACTUAL_VICTORY_POINTS"] == 2
コード例 #12
0
def test_cant_steal_devcards():
    # Arrange: Have RED buy 1 dev card (and have no resource cards)
    players = [SimplePlayer(Color.RED), SimplePlayer(Color.BLUE)]
    state = State(players)
    player_deck_replenish(state, Color.RED, WHEAT)
    player_deck_replenish(state, Color.RED, ORE)
    player_deck_replenish(state, Color.RED, SHEEP)
    buy_dev_card(state, Color.RED, KNIGHT)

    # Act: Attempt to steal a resource
    with pytest.raises(IndexError):  # no resource cards in hand
        player_deck_random_draw(state, Color.RED)
コード例 #13
0
def test_can_trade_with_port():
    players = [SimplePlayer(Color.RED)]

    state = State(players)
    state.board.build_settlement(Color.RED, 26, initial_build_phase=True)

    port_tile = state.board.map.tiles[(3, -3, 0)]  # port with node_id=25,26
    resource_out = port_tile.resource or WHEAT  # type: ignore
    num_out = 3 if port_tile.resource is None else 2  # type: ignore
    player_deck_replenish(state, Color.RED, resource_out, num_out)

    possibilities = maritime_trade_possibilities(state, Color.RED)
    assert len(possibilities) == 4
コード例 #14
0
def test_4to1_maritime_trade_possibilities():
    player = SimplePlayer(Color.RED)
    state = State([player])

    possibilities = maritime_trade_possibilities(state, player.color)
    assert len(possibilities) == 0

    player_deck_replenish(state, player.color, WHEAT, 4)
    possibilities = maritime_trade_possibilities(state, player.color)
    print(possibilities)
    assert len(possibilities) == 4

    player_deck_replenish(state, player.color, BRICK, 4)
    possibilities = maritime_trade_possibilities(state, player.color)
    assert len(possibilities) == 8
コード例 #15
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)
コード例 #16
0
def test_road_possible_actions():
    player = SimplePlayer(Color.RED)
    state = State([player])

    assert len(road_building_possibilities(
        state, Color.RED)) == 0  # no money or place

    state.board.build_settlement(Color.RED, 3, initial_build_phase=True)
    assert len(road_building_possibilities(state, Color.RED)) == 0  # no money

    player_deck_replenish(state, player.color, WOOD)
    player_deck_replenish(state, player.color, BRICK)
    assert len(road_building_possibilities(state, Color.RED)) == 3

    state.board.build_settlement(Color.RED, 1, initial_build_phase=True)
    assert len(road_building_possibilities(state, Color.RED)) == 6
コード例 #17
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
コード例 #18
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])
コード例 #19
0
def test_city_playable_actions():
    player = SimplePlayer(Color.RED)
    state = State([player])

    assert len(city_possibilities(state, Color.RED)) == 0  # no money or place

    state.board.build_settlement(Color.RED, 3, initial_build_phase=True)
    build_settlement(state, player.color, 3, True)
    assert len(city_possibilities(state, Color.RED)) == 0  # no money

    player_deck_replenish(state, Color.RED, WHEAT, 2)
    player_deck_replenish(state, Color.RED, ORE, 3)
    assert len(city_possibilities(state, Color.RED)) == 1

    state.board.build_settlement(Color.RED, 0, initial_build_phase=True)
    build_settlement(state, player.color, 0, True)
    assert len(city_possibilities(state, Color.RED)) == 2
コード例 #20
0
def test_settlement_possible_actions():
    player = SimplePlayer(Color.RED)
    state = State([player])

    assert len(settlement_possibilities(state,
                                        Color.RED)) == 0  # no money or place

    state.board.build_settlement(Color.RED, 3, initial_build_phase=True)
    state.board.build_road(Color.RED, (3, 4))
    state.board.build_road(Color.RED, (4, 5))
    assert len(settlement_possibilities(state, Color.RED)) == 0  # no money

    player_freqdeck_add(state, player.color, SETTLEMENT_COST_FREQDECK)
    assert len(settlement_possibilities(state, Color.RED)) == 1

    state.board.build_road(Color.RED, (5, 0))
    assert len(settlement_possibilities(state, Color.RED)) == 2
コード例 #21
0
def test_largest_army_calculation_when_no_one_has_three():
    red = SimplePlayer(Color.RED)
    blue = SimplePlayer(Color.BLUE)
    white = SimplePlayer(Color.WHITE)
    state = State([red, blue, white])

    player_deck_replenish(state, Color.RED, WHEAT, 2)
    player_deck_replenish(state, Color.RED, SHEEP, 2)
    player_deck_replenish(state, Color.RED, ORE, 2)
    player_deck_replenish(state, Color.BLUE, WHEAT, 1)
    player_deck_replenish(state, Color.BLUE, SHEEP, 1)
    player_deck_replenish(state, Color.BLUE, ORE, 1)
    buy_dev_card(state, Color.RED, KNIGHT)
    buy_dev_card(state, Color.RED, KNIGHT)
    buy_dev_card(state, Color.BLUE, KNIGHT)

    play_dev_card(state, Color.RED, KNIGHT)

    color, count = get_largest_army(state)
    assert color is None and count is None
コード例 #22
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
コード例 #23
0
def test_largest_army_calculation_on_tie():
    red = SimplePlayer(Color.RED)
    blue = SimplePlayer(Color.BLUE)
    white = SimplePlayer(Color.WHITE)
    state = State([red, blue, white])

    player_deck_replenish(state, red.color, KNIGHT, 3)
    player_deck_replenish(state, blue.color, KNIGHT, 4)
    play_dev_card(state, Color.RED, KNIGHT)
    play_dev_card(state, Color.RED, KNIGHT)
    play_dev_card(state, Color.RED, KNIGHT)
    play_dev_card(state, Color.BLUE, KNIGHT)
    play_dev_card(state, Color.BLUE, KNIGHT)
    play_dev_card(state, Color.BLUE, KNIGHT)

    color, count = get_largest_army(state)
    assert color is Color.RED and count == 3

    play_dev_card(state, Color.BLUE, KNIGHT)

    color, count = get_largest_army(state)
    assert color is Color.BLUE and count == 4
コード例 #24
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
コード例 #25
0
class Game:
    """
    Initializes a map, decides player seating order, and exposes two main
    methods for executing the game (play and play_tick; to advance until
    completion or just by one decision by a player respectively).
    """

    def __init__(
        self,
        players: Iterable[Player],
        seed: int = None,
        discard_limit: int = 7,
        vps_to_win: int = 10,
        catan_map: CatanMap = None,
        initialize: bool = True,
    ):
        """Creates a game (doesn't run it).

        Args:
            players (Iterable[Player]): list of players, should be at most 4.
            seed (int, optional): Random seed to use (for reproducing games). Defaults to None.
            discard_limit (int, optional): Discard limit to use. Defaults to 7.
            vps_to_win (int, optional): Victory Points needed to win. Defaults to 10.
            catan_map (CatanMap, optional): Map to use. Defaults to None.
            initialize (bool, optional): Whether to initialize. Defaults to True.
        """
        if initialize:
            self.seed = seed or random.randrange(sys.maxsize)
            random.seed(self.seed)

            self.id = str(uuid.uuid4())
            self.vps_to_win = vps_to_win
            self.state = State(players, catan_map, discard_limit=discard_limit)

    def play(self, accumulators=[], decide_fn=None):
        """Executes game until a player wins or exceeded TURNS_LIMIT.

        Args:
            accumulators (list[Accumulator], optional): list of Accumulator classes to use.
                Their .consume method will be called with every action, and
                their .finalize method will be called when the game ends (if it ends)
                Defaults to [].
            decide_fn (function, optional): Function to overwrite current player's decision with.
                Defaults to None.
        Returns:
            Color: winning color or None if game exceeded TURNS_LIMIT
        """
        initial_game_state = self.copy()
        for accumulator in accumulators:
            accumulator.before(initial_game_state)
        while self.winning_color() is None and self.state.num_turns < TURNS_LIMIT:
            self.play_tick(decide_fn=decide_fn, accumulators=accumulators)
        final_game_state = self.copy()
        for accumulator in accumulators:
            accumulator.after(final_game_state)
        return self.winning_color()

    def play_tick(self, decide_fn=None, accumulators=[]):
        """Advances game by one ply (player decision).

        Args:
            decide_fn (function, optional): Function to overwrite current player's decision with.
                Defaults to None.

        Returns:
            Action: Final action (modified to be used as Log)
        """
        player = self.state.current_player()
        actions = self.state.playable_actions

        action = (
            decide_fn(player, self, actions)
            if decide_fn is not None
            else player.decide(self, actions)
        )
        # Call accumulator.step here, because we want game_before_action, action
        if len(accumulators) > 0:
            game_snapshot = self.copy()
            for accumulator in accumulators:
                accumulator.step(game_snapshot, action)
        return self.execute(action)

    def execute(self, action: Action, validate_action: bool = True) -> Action:
        """Internal call that carries out decided action by player"""
        if validate_action and action not in self.state.playable_actions:
            raise ValueError(
                f"{action} not in playable actions: {self.state.playable_actions}"
            )

        return apply_action(self.state, action)

    def winning_color(self) -> Union[Color, None]:
        """Gets winning color

        Returns:
            Union[Color, None]: Might be None if game truncated by TURNS_LIMIT
        """
        result = None
        for color in self.state.colors:
            key = player_key(self.state, color)
            if (
                self.state.player_state[f"{key}_ACTUAL_VICTORY_POINTS"]
                >= self.vps_to_win
            ):
                result = color

        return result

    def copy(self) -> "Game":
        """Creates a copy of this Game, that can be modified without
        repercusions on this one (useful for simulations).

        Returns:
            Game: Game copy.
        """
        game_copy = Game([], None, None, initialize=False)
        game_copy.seed = self.seed
        game_copy.id = self.id
        game_copy.vps_to_win = self.vps_to_win
        game_copy.state = self.state.copy()
        return game_copy
コード例 #26
0
def test_initial_placement_possibilities():
    red = SimplePlayer(Color.RED)
    state = State([red])
    assert len(settlement_possibilities(state, Color.RED, True)) == 54
コード例 #27
0
def test_maritime_possibities_respect_bank_not_having_cards():
    player = SimplePlayer(Color.RED)
    state = State([player])
    player_deck_replenish(state, player.color, WHEAT)
    assert len(maritime_trade_possibilities(state, player.color)) == 0