Example

The following is a simple example of how to use the library to simulate a round of whist with some very basic bots (the source code for example_strategy.py and printing_event_listener.py is also included further below).

print_one_round.py
 1"""Use the `whist_backend` module to make 5 simple bots play a round of whist."""
 2
 3from __future__ import annotations
 4
 5from typing import TYPE_CHECKING
 6
 7from example_strategy import NoThinkingStrategy
 8from printing_event_listener import PrinterEventListener
 9
10from whist_backend import RoundSimulator
11
12if TYPE_CHECKING:
13    from whist_backend.player_strategy_protocol import PlayerStrategy
14
15if __name__ == "__main__":
16    strategies: list[PlayerStrategy] = [
17        NoThinkingStrategy("Player 1"),
18        NoThinkingStrategy("Player 2"),
19        NoThinkingStrategy("Player 3"),
20        NoThinkingStrategy("Player 4"),
21        NoThinkingStrategy("Player 5"),
22    ]
23    round_simulator = RoundSimulator(
24        strategies, event_listeners=[PrinterEventListener()]
25    )
26    results = round_simulator.play_round()
27    for result in results:
28        print(f"{result[0].name} won {result[1]} points.")

Sample output for this script would look something like:

Sample output
REVEALED CARD:  4♥ (trump suit ♥, hand size 6)
PLAYERS:
Player 1: hand  8♠, 3♥,10♣, 7♣, K♣, 5♠
Player 2: hand  Q♣, 5♥, 9♦, 9♥, J♦, 5♦
Player 3: hand  K♠, A♣, K♦, 6♥, 2♥, 8♦
Player 4: hand  Q♠, A♦, 4♦, Q♦, 2♣,10♥
Player 5: hand  6♦, 5♣, 7♥, 6♣,10♠, 3♠

BETS:
Player 1: 0
Player 2: 0
Player 3: 0
Player 4: 0
Player 5: 7

A new trick is starting with 0
Player 1 played  8♠ (remaining cards  3♥,10♣, 7♣, K♣, 5♠).
Player 2 played  Q♣ (remaining cards  5♥, 9♦, 9♥, J♦, 5♦).
Player 3 played  K♠ (remaining cards  A♣, K♦, 6♥, 2♥, 8♦).
Player 4 played  Q♠ (remaining cards  A♦, 4♦, Q♦, 2♣,10♥).
Player 5 played 10♠ (remaining cards  6♦, 5♣, 7♥, 6♣, 3♠).
Player 3 won the trick!

A new trick is starting with 2
Player 3 played  A♣ (remaining cards  K♦, 6♥, 2♥, 8♦).
Player 4 played  2♣ (remaining cards  A♦, 4♦, Q♦,10♥).
Player 5 played  5♣ (remaining cards  6♦, 7♥, 6♣, 3♠).
Player 1 played 10♣ (remaining cards  3♥, 7♣, K♣, 5♠).
Player 2 played  5♥ (remaining cards  9♦, 9♥, J♦, 5♦).
Player 2 won the trick!

A new trick is starting with 1
Player 2 played  9♦ (remaining cards  9♥, J♦, 5♦).
Player 3 played  K♦ (remaining cards  6♥, 2♥, 8♦).
Player 4 played  A♦ (remaining cards  4♦, Q♦,10♥).
Player 5 played  6♦ (remaining cards  7♥, 6♣, 3♠).
Player 1 played  3♥ (remaining cards  7♣, K♣, 5♠).
Player 1 won the trick!

A new trick is starting with 0
Player 1 played  7♣ (remaining cards  K♣, 5♠).
Player 2 played  9♥ (remaining cards  J♦, 5♦).
Player 3 played  6♥ (remaining cards  2♥, 8♦).
Player 4 played  4♦ (remaining cards  Q♦,10♥).
Player 5 played  6♣ (remaining cards  7♥, 3♠).
Player 2 won the trick!

A new trick is starting with 1
Player 2 played  J♦ (remaining cards  5♦).
Player 3 played  8♦ (remaining cards  2♥).
Player 4 played  Q♦ (remaining cards 10♥).
Player 5 played  7♥ (remaining cards  3♠).
Player 1 played  K♣ (remaining cards  5♠).
Player 5 won the trick!

A new trick is starting with 4
Player 5 played  3♠ (remaining cards ).
Player 1 played  5♠ (remaining cards ).
Player 2 played  5♦ (remaining cards ).
Player 3 played  2♥ (remaining cards ).
Player 4 played 10♥ (remaining cards ).
Player 4 won the trick!

Player 1 won -1 points.
Player 2 won -2 points.
Player 3 won -1 points.
Player 4 won -1 points.
Player 5 won -6 points.
example_strategy.py
 1from __future__ import annotations
 2
 3from typing import TYPE_CHECKING, Optional
 4
 5from whist_backend import Bet, PlayerStrategy
 6
 7if TYPE_CHECKING:
 8    from whist_backend.cards import Card
 9
10
11class NoThinkingStrategy(PlayerStrategy):
12    def __init__(self, name: str) -> None:
13        self.name = name
14
15    def make_move(self, allowed_cards: list[Card]) -> Card:
16        return allowed_cards[0]
17
18    def make_bet(self, disallowed_bet: Optional[int] = None) -> Bet:
19        return Bet(disallowed_bet + 1 if disallowed_bet is not None else 0, False)
printing_event_listener.py
 1from __future__ import annotations
 2
 3from typing import TYPE_CHECKING, Iterator
 4
 5from whist_backend import WhistEventListener
 6
 7if TYPE_CHECKING:
 8    from whist_backend import Bet, Card, Suit
 9    from whist_backend.round_simulator import _Player
10
11
12class PrinterEventListener(WhistEventListener):
13    """A simple example WhistEventListener that prints information about game events."""
14
15    def notify_round_start(
16        self,
17        revealed_card: Card,
18        trump_suit: Suit,
19        hand_size: int,
20        players: list[_Player],
21    ) -> None:
22        print(
23            f"REVEALED CARD: {revealed_card} "
24            + f"(trump suit {trump_suit}, hand size {hand_size})"
25        )
26        print("PLAYERS:")
27        for player in players:
28            print(player)
29        print()
30
31    def notify_card_played(self, player: _Player, played_card: Card) -> None:
32        print(
33            f"{player.strategy.name} played {played_card} "
34            f"(remaining cards {','.join(map(str, player.hand))})."
35        )
36
37    def notify_trick_won(self, player: _Player) -> None:
38        print(f"{player.strategy.name} won the trick!")
39        print()
40
41    def notify_bets_made(self, bets: Iterator[tuple[_Player, Bet]]) -> None:
42        print("BETS:")
43        for player, bet in bets:
44            print(f"{player.strategy.name}: {bet}")
45        print()
46
47    def notify_new_trick(self, player_id: int) -> None:
48        print(f"A new trick is starting with {player_id}")