| import pytest |
|
|
| from telltale.game.engine import HoldemEngine |
| from telltale.game.holdem import ActionType, PlayerState, PokerError, Street |
|
|
|
|
| def make_players(stacks: tuple[int, ...] = (100, 100, 100)) -> list[PlayerState]: |
| players: list[PlayerState] = [] |
| for index, stack in enumerate(stacks): |
| players.append(PlayerState(f"p{index}", f"Player {index}", index, stack, is_human=index == 0)) |
| return players |
|
|
|
|
| def start_hand(stacks: tuple[int, ...] = (100, 100, 100)): |
| return HoldemEngine().start_hand(make_players(stacks), seed=42, dealer_button_index=0) |
|
|
|
|
| def test_starting_three_player_hand_deals_two_cards_each(): |
| hand = start_hand() |
|
|
| hole_card_counts = [] |
| for player in hand.players: |
| hole_card_counts.append(len(player.hole_cards)) |
| assert hole_card_counts == [2, 2, 2] |
|
|
|
|
| def test_preflop_blinds_are_posted_correctly(): |
| hand = start_hand() |
|
|
| assert hand.players[1].current_bet == 5 |
| assert hand.players[2].current_bet == 10 |
| assert hand.pot_contributions == {"p0": 0, "p1": 5, "p2": 10} |
|
|
|
|
| def test_big_blind_can_check_when_no_raise_occurred(): |
| engine = HoldemEngine() |
| hand = start_hand() |
|
|
| engine.apply_action(hand, ActionType.CALL) |
| engine.apply_action(hand, ActionType.CALL) |
|
|
| assert hand.current_actor_index == 2 |
| assert ActionType.CHECK in engine.legal_actions(hand) |
|
|
|
|
| def test_illegal_check_facing_bet_is_rejected(): |
| hand = start_hand() |
|
|
| with pytest.raises(PokerError, match="check is not legal"): |
| hand.apply_action(ActionType.CHECK) |
|
|
|
|
| def test_folded_players_are_skipped(): |
| hand = start_hand() |
|
|
| hand.apply_action(ActionType.FOLD) |
|
|
| assert hand.players[0].has_folded |
| assert hand.current_actor_index == 1 |
|
|
|
|
| def test_all_in_players_are_skipped(): |
| hand = start_hand(stacks=(10, 100, 100)) |
|
|
| hand.apply_action(ActionType.CALL) |
|
|
| assert hand.players[0].is_all_in |
| assert hand.current_actor_index == 1 |
|
|
|
|
| def test_street_advances_after_all_active_players_have_acted(): |
| hand = start_hand() |
|
|
| hand.apply_action(ActionType.CALL) |
| hand.apply_action(ActionType.CALL) |
| hand.apply_action(ActionType.CHECK) |
|
|
| assert hand.street == Street.FLOP |
| assert len(hand.board_cards) == 3 |
|
|
|
|
| def test_board_reaches_flop_turn_and_river(): |
| hand = start_hand() |
| hand.apply_action(ActionType.CALL) |
| hand.apply_action(ActionType.CALL) |
| hand.apply_action(ActionType.CHECK) |
| assert len(hand.board_cards) == 3 |
|
|
| for _ in range(3): |
| hand.apply_action(ActionType.CHECK) |
| assert hand.street == Street.TURN |
| assert len(hand.board_cards) == 4 |
|
|
| for _ in range(3): |
| hand.apply_action(ActionType.CHECK) |
| assert hand.street == Street.RIVER |
| assert len(hand.board_cards) == 5 |
|
|
|
|
| def test_hand_completes_when_only_one_player_remains(): |
| hand = start_hand() |
|
|
| hand.apply_action(ActionType.FOLD) |
| hand.apply_action(ActionType.FOLD) |
|
|
| assert hand.street == Street.COMPLETE |
| assert hand.players[2].stack == 105 |
|
|
|
|
| def test_public_state_hides_opponent_hole_cards_before_showdown(): |
| hand = start_hand() |
|
|
| public = hand.to_public_state("p0") |
|
|
| expected_hole_cards = [] |
| for card in hand.players[0].hole_cards: |
| expected_hole_cards.append(str(card)) |
| assert public["players"][0]["hole_cards"] == expected_hole_cards |
| assert public["players"][1]["hole_cards"] == [] |
| assert public["players"][2]["hole_cards"] == [] |
| assert "deck" not in public |
|
|
|
|
| def test_seeded_first_legal_action_simulation_conserves_chips_and_cards(): |
| hand = start_hand() |
| total_chips = 0 |
| for player in hand.players: |
| total_chips += player.stack |
| total_chips += sum(hand.pot_contributions.values()) |
| priority = [ |
| ActionType.CHECK, |
| ActionType.CALL, |
| ActionType.FOLD, |
| ActionType.BET, |
| ActionType.RAISE, |
| ActionType.ALL_IN, |
| ] |
|
|
| while hand.street != Street.COMPLETE: |
| legal = hand.legal_actions() |
| action = None |
| for candidate in priority: |
| if candidate in legal: |
| action = candidate |
| break |
| hand.apply_action(action, amount=10 if action in {ActionType.BET, ActionType.RAISE} else 0) |
|
|
| visible_and_deck_cards = [] |
| for player in hand.players: |
| for card in player.hole_cards: |
| visible_and_deck_cards.append(card) |
| visible_and_deck_cards.extend(hand.board_cards) |
| visible_and_deck_cards.extend(hand.deck.cards) |
|
|
| for player in hand.players: |
| assert player.stack >= 0 |
| assert len(visible_and_deck_cards) == len(set(visible_and_deck_cards)) |
| final_chips = 0 |
| for player in hand.players: |
| final_chips += player.stack |
| assert final_chips == total_chips |
|
|