added docs
This commit is contained in:
@@ -0,0 +1,140 @@
|
||||
# 2. Game rules as implemented
|
||||
|
||||
This chapter lists every rule the code enforces and where. The original rules
|
||||
are in `wizard_instructions/`; where this implementation deliberately differs
|
||||
from them, it is marked **(house rule)**.
|
||||
|
||||
All rule code lives in the game engine: stateless helpers in
|
||||
`wizard_server/game_logic/wizard_game/rules.py`, flow in `game.py`.
|
||||
|
||||
## Cards
|
||||
|
||||
60 cards (`cards.py`):
|
||||
|
||||
| Cards | Count | Notes |
|
||||
|-------|-------|-------|
|
||||
| Number cards 1–13 in red, yellow, green, blue | 52 | the only cards with a suit |
|
||||
| Wizards | 4 | always highest |
|
||||
| Jesters | 4 | always lowest |
|
||||
|
||||
Every card has a fixed **id 0–59**, used in JSON and in the AI encoding:
|
||||
|
||||
| Ids | Cards |
|
||||
|-----|-------|
|
||||
| 0–12 | red 1–13 |
|
||||
| 13–25 | yellow 1–13 |
|
||||
| 26–38 | green 1–13 |
|
||||
| 39–51 | blue 1–13 |
|
||||
| 52–55 | wizards |
|
||||
| 56–59 | jesters |
|
||||
|
||||
Formula for number cards: `id = suit_index * 13 + (rank - 1)` with suit order
|
||||
red, yellow, green, blue.
|
||||
|
||||
## Players and rounds
|
||||
|
||||
- 3 to 6 players (`rules.MIN_PLAYERS`, `rules.MAX_PLAYERS`).
|
||||
- Number of rounds = `60 // players` (`rules.total_rounds`): 3 → 20, 4 → 15,
|
||||
5 → 12, 6 → 10.
|
||||
- In round *n* every player gets *n* cards, so round *n* has *n* tricks.
|
||||
|
||||
## Dealing, dealer and starting player
|
||||
|
||||
`WizardGame._deal`:
|
||||
|
||||
1. A fresh 60-card stack is shuffled every round.
|
||||
2. Cards are dealt one at a time from the top of the stack, starting with the
|
||||
player left of the dealer.
|
||||
3. The **next card of the stack is turned up as the trump card**. It is taken
|
||||
off the stack: it is in nobody's hand and can never be played.
|
||||
4. In the last round all 60 cards are dealt, so there is **no trump card**.
|
||||
|
||||
The first dealer is random (or `first_dealer=` in tests). The dealer moves one
|
||||
seat to the left every round. The player left of the dealer (the *starter*)
|
||||
bids first and leads the first trick.
|
||||
|
||||
## Trump
|
||||
|
||||
`rules.trump_suit(trump_card)`:
|
||||
|
||||
| Trump card | Trump suit |
|
||||
|------------|------------|
|
||||
| number card | its suit |
|
||||
| wizard | **no trump** (house rule — the original rules let the dealer choose) |
|
||||
| jester | no trump |
|
||||
| none (last round) | no trump |
|
||||
|
||||
## Bidding
|
||||
|
||||
`WizardGame._allowed_bids` and `_bid`:
|
||||
|
||||
- Players bid in turn, starting with the starter; the dealer bids last.
|
||||
- A bid is any number from 0 to the number of cards in hand.
|
||||
- **The sum of all bids may never equal the number of cards in hand**
|
||||
(house rule — in the original rules this is the optional "notequal"
|
||||
variant; here it is always on). Only the last bidder is restricted: the one
|
||||
value that would make the sum equal the round number is removed from their
|
||||
legal bids. Example: round 5, the others bid 1, 2 and 0 → the last player may
|
||||
bid anything except 2.
|
||||
- Option **hidden bids** ("hiddentip", `GameOptions.hidden_bids`): while
|
||||
bidding is in progress, each player only sees their own bid; everyone can
|
||||
see *who* has already bid (`Observation.has_bid`). All bids are revealed as
|
||||
soon as the last bid is placed (`WizardGame.observation`).
|
||||
Note: because of the sum rule, the last bidder can deduce the sum of the
|
||||
hidden bids from the forbidden value.
|
||||
|
||||
## Playing a trick
|
||||
|
||||
The starter leads the first trick; afterwards the winner of the previous
|
||||
trick leads.
|
||||
|
||||
### Which suit must be followed — `rules.lead_suit(trick)`
|
||||
|
||||
Look at the cards of the trick in playing order:
|
||||
|
||||
- Jesters are skipped.
|
||||
- If the first non-jester card is a **wizard**, there is no suit to follow for
|
||||
the whole trick.
|
||||
- If the first non-jester card is a **number card**, its suit must be
|
||||
followed.
|
||||
- If only jesters were played so far, there is no suit yet (the next number
|
||||
card will set it).
|
||||
|
||||
### Which cards may be played — `rules.legal_cards(hand, trick)`
|
||||
|
||||
- Wizards and jesters may always be played.
|
||||
- If there is a lead suit and the player holds at least one number card of
|
||||
that suit, they must play a card of that suit (or a wizard/jester).
|
||||
- Otherwise any card may be played.
|
||||
|
||||
### Who wins the trick — `rules.trick_winner(trick, trump)`
|
||||
|
||||
In this order:
|
||||
|
||||
1. The **first wizard** played wins.
|
||||
2. Otherwise the **highest trump** wins.
|
||||
3. Otherwise the **highest card of the lead suit** wins.
|
||||
4. If only jesters were played, the **first jester** wins.
|
||||
|
||||
## Scoring
|
||||
|
||||
`rules.round_score(bid, tricks)` at the end of each round:
|
||||
|
||||
| Result | Points |
|
||||
|--------|--------|
|
||||
| tricks == bid | 20 + 10 × tricks |
|
||||
| tricks ≠ bid | −10 × \|tricks − bid\| |
|
||||
|
||||
Examples: bid 0, took 0 → +20. Bid 3, took 3 → +50. Bid 2, took 0 → −20.
|
||||
|
||||
## End of the game
|
||||
|
||||
After the last round (`WizardGame._complete_round`) the phase becomes
|
||||
`finished`. All players with the highest total score win (ties are possible,
|
||||
`WizardGame.winners`).
|
||||
|
||||
## Rule variants from the original that are not implemented
|
||||
|
||||
- `dmmixed` and `wiztournament` (tournament card distributions).
|
||||
- The dealer choosing trump when a wizard is turned up (replaced by the house
|
||||
rule "wizard = no trump").
|
||||
Reference in New Issue
Block a user