pub struct Round { /* private fields */ }Expand description
One deal of gin rummy
See the module documentation for the flow. Construct with
Round::from_deal or, under the rand feature, Round::deal.
With the serde feature, a Round serializes to a plain snapshot of
its position, and deserialization re-validates every invariant — a
corrupt snapshot is rejected, never trusted.
Implementations§
Source§impl Round
impl Round
Sourcepub fn from_deal(
rules: Rules,
dealer: Player,
hands: [Hand; 2],
upcard: Card,
stock: Vec<Card>,
) -> Result<Self, DealError>
pub fn from_deal( rules: Rules, dealer: Player, hands: [Hand; 2], upcard: Card, stock: Vec<Card>, ) -> Result<Self, DealError>
Construct a round from a fixed initial deal
stock is face-down draw order: the last element is drawn first.
The upcard starts the discard pile, and the non-dealer moves first.
§Errors
When the hands are not 10 cards each, the stock is not the remaining 31 cards, or any card appears twice.
Sourcepub fn deal(rules: Rules, dealer: Player, rng: &mut (impl Rng + ?Sized)) -> Self
pub fn deal(rules: Rules, dealer: Player, rng: &mut (impl Rng + ?Sized)) -> Self
Shuffle and deal a fresh round: 10 cards to each player, an upcard, and a randomly ordered 31-card stock
Sourcepub const fn non_dealer(&self) -> Player
pub const fn non_dealer(&self) -> Player
The non-dealer, who receives the first upcard offer
Sourcepub const fn turn(&self) -> Option<Player>
pub const fn turn(&self) -> Option<Player>
The player to act, or None once the round is finished
During the layoff phase this is the defender.
Sourcepub const fn hand(&self, player: Player) -> Hand
pub const fn hand(&self, player: Player) -> Hand
The hand of a player
Both hands are visible by design; bots enforce their own information hygiene.
Sourcepub fn discard_pile(&self) -> &[Card]
pub fn discard_pile(&self) -> &[Card]
The discard pile; the top card is the last element
Sourcepub fn stock(&self) -> &[Card]
pub fn stock(&self) -> &[Card]
The face-down stock; the top card is the last element
The full order is visible by design; bots enforce their own information hygiene.
Sourcepub const fn knock_limit(&self) -> u8
pub const fn knock_limit(&self) -> u8
The knock limit in effect for this round
Rules::knock_limit, capped by the opening upcard’s value under
an Oklahoma ruleset (Rules::oklahoma). Read the limit here
rather than from the rules.
Sourcepub const fn initial_upcard(&self) -> Card
pub const fn initial_upcard(&self) -> Card
The opening upcard that seeded the discard pile
Remembered even after the card is drawn; under Rules::oklahoma
it sets knock_limit.
Sourcepub const fn knocker(&self) -> Option<Player>
pub const fn knocker(&self) -> Option<Player>
The player who knocked or declared big gin, if any
Sourcepub fn spread(&self) -> impl Iterator<Item = Meld> + '_
pub fn spread(&self) -> impl Iterator<Item = Meld> + '_
The knocker’s spread, extended by any layoffs so far
Empty before a knock.
Sourcepub const fn result(&self) -> Option<RoundResult>
pub const fn result(&self) -> Option<RoundResult>
The outcome, or None while the round is in play
Sourcepub fn pass(&mut self) -> Result<(), RoundError>
pub fn pass(&mut self) -> Result<(), RoundError>
Decline the initial upcard
The offer moves from the non-dealer to the dealer; when both pass, the non-dealer must open by drawing from the stock.
§Errors
RoundError::WrongPhase outside the upcard phase.
Sourcepub fn take_discard(&mut self) -> Result<Card, RoundError>
pub fn take_discard(&mut self) -> Result<Card, RoundError>
Take the top of the discard pile, returning the card
Available as the upcard decision and as the draw of a normal turn.
§Errors
RoundError::WrongPhase outside the upcard and draw phases, and
RoundError::MustDrawFromStock on the forced stock draw after both
players passed the upcard.
Sourcepub fn draw_stock(&mut self) -> Result<Card, RoundError>
pub fn draw_stock(&mut self) -> Result<Card, RoundError>
Draw the top of the stock, returning the card
§Errors
RoundError::WrongPhase outside the draw phase.
Sourcepub fn discard(&mut self, card: Card) -> Result<(), RoundError>
pub fn discard(&mut self, card: Card) -> Result<(), RoundError>
Discard a card, ending the turn
If this leaves two cards in the stock, the round ends as a dead hand: nobody scores and the same dealer redeals.
§Errors
RoundError::WrongPhase outside the discard phase,
RoundError::NotInHand, and RoundError::DiscardJustTaken for
the card drawn from the discard pile this turn.
Sourcepub fn knock(&mut self, card: Card, melds: Melds) -> Result<(), RoundError>
pub fn knock(&mut self, card: Card, melds: Melds) -> Result<(), RoundError>
Discard a card and knock, spreading the given arrangement
The knocker chooses the arrangement — it decides what the defender
may lay off — so it is passed explicitly; best_melds(hand - card)
recovers the automatic choice:
let hand = round.hand(round.turn().unwrap());
round.knock(card, best_melds(hand - card.into()))An arrangement with zero deadwood is gin: the defender may not lay off, and the round finishes immediately. Otherwise the round enters the layoff phase.
§Errors
RoundError::WrongPhase outside the discard phase,
RoundError::NotInHand, RoundError::DiscardJustTaken,
RoundError::MeldsMismatch when melds does not arrange exactly
the hand minus the discard, and RoundError::TooMuchDeadwood over
the knock limit.
Sourcepub fn declare_big_gin(&mut self, melds: Melds) -> Result<(), RoundError>
pub fn declare_big_gin(&mut self, melds: Melds) -> Result<(), RoundError>
Declare big gin: all 11 cards melded, no discard
The defender may not lay off, and the round finishes immediately. Declaring is never forced — nor ever a trap: an 11-card fully-melded hand always contains a meld of four or more cards, which can shed a card, so plain gin remains available when the rules disable big gin.
§Errors
RoundError::WrongPhase outside the discard phase,
RoundError::BigGinDisabled when Rules::big_gin_bonus is
None, RoundError::MeldsMismatch when melds does not arrange
the full hand, and RoundError::NotBigGin on any deadwood.
Sourcepub fn lay_off(&mut self, card: Card, index: usize) -> Result<(), RoundError>
pub fn lay_off(&mut self, card: Card, index: usize) -> Result<(), RoundError>
Lay off a card onto the meld at index in the knocker’s spread
Indices are stable across layoffs, so chained extensions — the ♠8 then the ♠9 onto a 5-6-7 of spades — address the same meld.
§Errors
RoundError::WrongPhase outside the layoff phase,
RoundError::NotInHand, RoundError::NoSuchMeld, and
RoundError::CannotLayOff when the card extends neither end of a
run nor completes a set.
Sourcepub fn finish_layoffs(&mut self) -> Result<RoundResult, RoundError>
pub fn finish_layoffs(&mut self) -> Result<RoundResult, RoundError>
End the layoff phase and settle the round
The defender’s remaining cards are melded optimally. The defender
wins an undercut on less deadwood than the knocker kept — or equal
deadwood under Rules::undercut_on_tie; otherwise the knocker
wins the deadwood difference.
§Errors
RoundError::WrongPhase outside the layoff phase.