NAME

Game::Durak::Rules - what may be played, as arithmetic over a hand and a bout

VERSION

Version 0.01

SYNOPSIS

use Game::Durak::Rules qw(cap_for legal_attacks legal_beats forced closes);

my $cap = cap_for(scalar @{ $hand });        # min(6, the hand)

legal_attacks($hand, $bout);                 # ids that may be thrown in
legal_beats($hand, $bout, 'H');              # ids that beat the open card
forced($hand, $bout, 'H', 'defend', 0);      # 1 if there is nothing to decide
closes($bout, scalar @{ $defender_hand });   # 'capped', 'spent' or undef

DESCRIPTION

Functions over a hand, a bout and a trump suit. Nothing here takes a game, the talon, the other hand or the seed, so nothing here can be written into a position it was not given, and the bot's search can be handed the same functions as the rules without handing it the deal.

Which card may be thrown in is a question about rank alone, and the trump does not enter it. The signature says so, which is cheaper than a comment and harder to ignore: a reading of the rules that lets the trump matter here has nowhere to put it.

forced, and why it asks about the exchange

A TURN WITH NO CHOICE COSTS NOBODY A DEADLINE.
    -- lib/P2PGames/Game/Dominoes.pm, the site this engine feeds

An attacker with no legal throw is not deciding anything when they say they are done, and a defender who cannot beat the card in front of them is not deciding anything when they pick it up. The engine resolves both itself, and neither costs a move, an event or a turn.

The exchange is why forced takes a fifth argument. The holder of the trump six may swap it for the turned up trump, which can turn a hand that cannot beat into one that can, and can hand the attacker a rank that is already on the table. A position with a swap available is not forced, and an engine that resolves it anyway steals the one move the exchange exists for. The argument is a plain boolean because the eligibility is a provenance the game object tracks and not something a hand can be asked.

closes is only the two answers nobody chooses

The rules give three ways a defence is beaten off:

the defender has beaten all the attack cards played so far, and none of
the defender's opponents is able and willing to continue the attack; the
defender succeeds in beating six attacking cards; the defender (having
begun the defence holding fewer than six cards) has no cards left in hand

The second and third are facts about the table, and they are what closes answers: capped and spent. The first is a decision ("willing") or a forced position ("able"), so it belongs to forced and to the done move, and closes returns undef for it.

The third condition is tested first, and that is not an accident. The cap is the defender's hand before the bout when that is under six, and the defender spends exactly one card per attack card beaten, so a defender who started with fewer than six empties their hand on the same card that reaches the cap: the two conditions fire together and never apart. Asked in the other order, spent would be dead code and every such bout would be reported as capped, which is true of the count and says nothing about what happened. Asked in this order the two are distinct and both are reachable: spent is a defender with nothing left, and capped is six cards against a defender who still holds some.

FUNCTIONS

Nothing is exported by default.

cap_for

The most cards an attack may hold against a hand of that size: the hand, or six, whichever is smaller. Call it with the defender's hand before the bout, once, and keep the answer.

The cards of the hand that may be played into the bout: every card while the bout is empty, otherwise every card whose rank is already on the table, answers included. Empty once the cap is reached.

The cards of the hand that beat the open attack card. Empty when nothing is waiting to be beaten.

forced

True when the seat on turn has no choice at all: no legal move of the kind the stage calls for, and no exchange available. The caller resolves the position itself rather than asking.

closes

capped when the attack has reached its cap with everything beaten, spent when the defender has beaten everything and has nothing left, and undef otherwise. A taken bout is never closed by this function: the attacker may still throw more in.

SEE ALSO

Game::Durak, Game::Durak::Bout, Game::Durak::Card.

AUTHOR

LNATION, <email@lnation.org>

LICENSE AND COPYRIGHT

This software is Copyright (c) 2026 by LNATION.

This is free software, licensed under the Artistic License 2.0.