NAME
Game::Durak::Card - a card is an integer, and the trump decides what beats it
VERSION
Version 0.01
SYNOPSIS
use Game::Durak::Card qw(rank_of suit_of power_of name_of id_of beats);
rank_of(1); # 'A'
suit_of(1); # 'S'
name_of(1); # 'AS'
id_of('6C'); # 36
beats(id_of('KS'), id_of('QS'), 'H'); # 1, a higher spade
beats(id_of('6H'), id_of('AS'), 'H'); # 1, the trump six over the ace
beats(id_of('6H'), id_of('AH'), 'H'); # 0, a trump takes a HIGHER trump
DESCRIPTION
Cards are integers 1 to 36, suit-major, nine ranks to a suit: 1 to 9 are spades ace down to six, 10 to 18 hearts, 19 to 27 diamonds, 28 to 36 clubs. A hand is therefore a list of small integers and a deal is a permutation.
The ace is high, the six is low, and this is the third rank order in the tree
Game::Gin::Card and the copies of it made for the other card games on peer2peergames number a fifty-two card pack ace low over thirteen ranks. Game::Schnapsen::Card numbers a twenty-four card pack over six ranks with the ten above the king. This pack is nine ranks, ace high and six low, and no card id, rank index or power in this distribution means what it means in either of those.
That is also the reason this module exists rather than a deck being added to the marriage family's card module. That module derives a rank from ($id - 1) % @RANKS with six ranks in the list, so a nine rank pack moves every id it has: the ten of spades stops being id 2 and hearts stop starting at id 7. A thirty-six card pack is not a superset of a twenty-four card one, it is a different pack.
power_of is the authority on rank order and it is derived from the order of the rank list and from nothing else.
beats(), and the order of its three answers
The rules the function is built from, from https://www.pagat.com/beating/podkidnoy_durak.html:
A card which is not a trump can be beaten by playing a higher card of the
same suit, or by any trump. A trump card can only be beaten by playing a
higher trump. Note that a non-trump attack can always be beaten by a
trump, even if the defender also holds cards in the suit of the attack
card - there is no requirement to "follow suit".
Three sentences, and the order between them is the rule and not an optimisation:
If the attack card is a trump, only a higher trump beats it. Nothing else, ever.
Otherwise any trump beats it.
Otherwise a higher card of the same suit beats it.
Written with the trump clause first, the function is right about every attack made with one of the twenty-seven plain cards and lets the trump six beat the trump ace. Written with the suit comparison dropped from the last answer, it lets a higher card of an unrelated plain suit beat anything. Both mutations are carried in t/01-cards.t, which asserts which vectors each one changes rather than that something changed.
There is no fourth answer. Two cards are never equal, because the pack holds no duplicates, and a card is not offered against itself.
FUNCTIONS
Nothing is exported by default.
rank_of
One of A, K, Q, J, T, 9, 8, 7, 6. Dies for an id that is not a card.
suit_of
One of S, H, D, C.
power_of
How the card ranks within its suit: 8 for an ace down to 0 for a six. Higher wins, and it says nothing at all about a card of another suit.
name_of
Two characters, rank then suit: AS, TD, 6C. The suit is a letter and never a glyph: a consumer that encodes a view to JSON and embeds it in a page gets mojibake out of a glyph, so glyphs belong in the consumer.
long_name_of
ace of spades, for a sentence.
face_of
face_of(id_of('TS')); # '10'
face_of(id_of('AS')); # 'A'
What a player reads on the card. The only difference from rank_of is the ten, which this pack spells T because a card here is a two character id and one character a rank keeps both the arithmetic and the fixtures simple. Nobody has ever seen a ten with a T on it, so anything drawing a card for a person asks for this and anything computing with one asks for rank_of.
id_of
The id for a name, or undef. Case insensitive. This one parses input, so it returns undef rather than dying.
ids_of_suit
ids_of_suit('H'); # [ 10 .. 18 ], ace down to six
The nine ids of a suit, in id order, as an arrayref.
six_of
six_of('D'); # 27
The id of a suit's six. The lowest card of the suit, and the one card that may be exchanged for the turned up trump.
beats
beats($defending_card, $attacking_card, $trump_suit);
True if the first card beats the second with that suit as trumps. Dies for an id that is not a card or a suit that is not a suit, because both mean a bug upstream rather than a move a player has made.
ranks, suits
The rank list, high to low, and the suit list.
CARDS
36, as a constant.
SEE ALSO
Game::Durak, Game::Durak::Deck.
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.