NAME

Game::Durak::Bout - one attack and its defence, laid out on the table

VERSION

Version 0.01

SYNOPSIS

my $bout = Game::Durak::Bout->build(
    attacker => 1, defender => 2, cap => 6,
);

$bout->add_attack($card);
$bout->unbeaten;              # that card, until it is answered
$bout->add_beat($other);
$bout->all_beaten;            # 1
$bout->ranks;                 # { K => 1, A => 1 }, both cards

DESCRIPTION

A bout is the unit of play. One seat attacks, the other answers card by card, and at the end the whole thing is either thrown away or picked up by the seat that could not answer it.

The two lists are parallel: beats->[$i] is the card that answered attacks->[$i], or undef while it is unanswered. That is the layout the table has, "each card played by the defender is placed face up on top of the card it is beating, slightly offset so that the values of all cards can be seen", and it is also what a consumer needs in order to draw it.

The cap is fixed when the bout opens

the total number of cards played by the attackers during a bout must
never exceed six; if the defender had fewer than six cards before the
bout, the number of cards played by the attackers must not be more than
the number of cards in the defender's hand.
    -- https://www.pagat.com/beating/podkidnoy_durak.html

Read that again for the word before. The defender's hand shrinks by one for every card they beat, so a cap recomputed from the hand as it stands falls as the bout runs: a defender who started with four cards and has beaten two would be attackable twice more instead of four times. Every deal still finishes, every card is still accounted for, and the game is quietly a gentler one than the rules describe.

So the cap is set once, by the constructor, from the defender's hand at that moment, and this class offers no way to change it. room is what is left.

A bout is never opened against an empty hand

build dies at a cap of zero. A seat with no cards and no talon to draw from is out of the deal, which is decided before the next bout opens, so a zero cap means the caller has skipped that decision. It is a bug and not a refusal.

METHODS

build

Game::Durak::Bout->build(attacker => 1, defender => 2, cap => 4);

Two different seats and a cap of one to six. Dies otherwise.

attacker, defender

The seat numbers, 1 or 2. A bout does not change hands: when the defence is beaten off the deal opens a new bout the other way round.

cap

The most cards the attack may hold, set once.

attacks, beats

The two parallel lists. Treat them as read only and use add_attack and add_beat.

taken

True once the defender has picked the bout up. The cards stay on the table until the bout closes, because the attacker may still throw more in.

size, room

How many cards the attack holds, and how many more it may hold.

unbeaten

The first attack card with no answer, or undef. Outside the pile-on after a take there is never more than one, because the attacker plays a card and waits.

all_beaten

True when every attack card has been answered.

cards

Every card on the table, attacks and answers together, for the discard or for the defender's hand.

ranks

The set of ranks on the table, the defender's answers included. This is what a new attack card has to match:

each new attack card must be of the same rank as some card already played
during the current bout - either an attack card or a card played by the
defender

A set built from the attack cards alone is a subset, so nothing ever refuses a move it should allow, no invariant breaks, and the attack is narrower than the game's for ever.

add_attack, add_beat

Put a card on the table. Both die rather than refusing, because the rules answer legality before either is called.

SEE ALSO

Game::Durak, Game::Durak::Rules.

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.