NAME
Game::Durak::Terminal - a deal of durak at a terminal
VERSION
Version 0.04
SYNOPSIS
my $terminal = Game::Durak::Terminal->new(
seed => $thirty_two_bytes, level => 3, seat => 1,
);
my $result = $terminal->play;
DESCRIPTION
One screen and one line of input. The screen is the trump, the talon and the heap, the bout laid out as attack over answer, and your hand numbered from one; the input is a number, or t to take, d when you are done throwing in, x to exchange the trump six, r to give up and q to leave.
This is the second consumer the distribution exists for, and it is also the fastest way to find a rule that is wrong: three deals by hand against the top rung catch more than a suite does, because a suite only asks the questions somebody thought of.
Everything that prints lives here
No other module in the distribution writes to a handle, and t/18-silent.t is a grep that says so. A library that prints cannot be embedded, and a library that prints only on the paths nobody tested is worse.
out and in are attributes rather than STDOUT and STDIN, so the whole of this module can be driven by a test with two string handles.
One painted table, or a transcript
At a terminal the deal is a single table redrawn in place: Game::Durak::Table builds the screen and this module writes it at the top left. Through a pipe the same deal prints as plain lines, one position after another, because a screen redrawn in place makes a file that is unreadable and a test that can only be read by eye.
The choice follows -t unless paint says otherwise, and both paths go through the same loop and the same sentences.
A card is a letter and a glyph
name_of gives the ASCII pair and the glyph is decoration: --ascii turns the glyphs off for a terminal that has no font for them, and colour follows -t and NO_COLOR unless it is asked for either way. NO_COLOR set to anything but the empty string turns colour off.
METHODS
game, out, in, seed, level, seat, ascii, colour, paint, width, height
What the terminal was built with. seat is the seat you play, 1 or 2, and the other one is the bot. width and height are the painted screen's, and default to eighty by twenty-four.
keyboard, screen, log
The Game::Terminal::Kit::Keys the keys are read with, the Game::Terminal::Kit::Screen everything is written through, and the Game::Terminal::Kit::Log that holds what "told" was given until the next painted frame. Each is built from the other arguments unless one is passed.
open_screen, position, close_screen
The three moments of a deal: before the first move, at every position that is yours to answer, and after the last. Each one draws whichever way the terminal is running, so run has one loop rather than two.
table_of, frame, show
The Game::Durak::Table this terminal draws with, one screen from a view as a list of strings, and that screen written to the handle at the top left with the cursor left where the answer is typed.
A frame carries colour only when colour is on. The painted screen and the plain one are the same grid either way, which is what makes the whole of it assertable by a test with no terminal in it at all.
headline
The most recent things that happened, newest last, cut to the room the header bar has. It takes the LATEST lines that fit rather than the first, because a bout can produce four events at once and the one that matters is the last.
told
Says something to the player: a line at a time when the terminal is printing plain lines, and into the next frame's header when it is painting.
start
Builds the game from the seed and keeps it. Dies for a seed the engine refuses, because a terminal with no game is not a game.
play
Plays until the deal ends or the reader says so, and returns the result hashref, or undef when somebody quit.
options
my $fields = Game::Durak::Terminal->options;
The options a person would set before a game, in the order a launcher shows them. Each is a hash reference with a name, a type and a help, and min and max or choices where the type has them. A new list is returned each time.
level-
A whole number from 1 to 5: how hard the bot plays.
seat-
One of
1,2: your seat; the bot takes the other. seed-
Text: the deal; the same seed deals the same cards.
run
exit Game::Durak::Terminal->run(@ARGV);
The whole command line program as a class method: parses the options bin/durak documents, builds the terminal, plays the deal, prints the seed at the end, and returns the exit status rather than calling exit: 0 if you won or the deal was stopped, 1 if you were the fool, 2 for a bad option or a refused value, with the usage on STDERR. --help prints the usage on STDOUT and returns 0. The deal runs under "guard" in Game::Terminal::Kit::Run: signal handlers are local to the call and the terminal is left in cooked mode on every way out, so a launcher such as Game::Terminal can call it in its own process and carry on afterwards. Colour is off under NO_COLOR as well as off a terminal.
table, hand_line, bout_line, card, card_suit
The screen, in pieces. Each takes what it needs and returns a string, so a test can assert what a person would see without running a deal.
playable
The positions in the hand that can be played right now, counting from one. A prompt that says "a card" and means four of the eleven is a prompt that gets answered wrong, so the numbers are listed rather than the range.
cards
$terminal->cards(1); # '1 card'
One card or several, because "1 cards" is what a first deal at the terminal found and it is the sort of thing nobody fixes later.
prompt_for, move_for
What the seat may do, as a line, and the move a line of input means, or undef when it means nothing. move_for only ever returns a move that is in the view's legal, which is why an illegal move at the terminal is a reprompt and not a refusal.
picking
Whether a card is pointed at rather than numbered at a prompt. Settable, because a failed enter_raw turns it off for the rest of the deal. Unset, it follows "keys_available".
keys_available, enter_raw, leave_raw
Whether there is anything to read keys with, and cbreak on and off. cbreak rather than raw, so an interrupt stays an interrupt. "run" gives the terminal back on a signal and on an error as well as at the end, because a program that dies in cbreak leaves the shell with no echo.
keysource, pending, raw
A coderef returning ONE character in place of Term::ReadKey, characters read and given back, and the cbreak state. keysource is what lets t/21 drive the key loop with no terminal and no Term::ReadKey installed; one character a call, because a source handing back "\e[C" whole never becomes a right arrow. keysource and pending are read when the terminal is built.
read_key
One keystroke: the character itself, or a name from "The names" in Game::Terminal::Kit::Keys. A name is always longer than one character, so it never collides with one.
picker
my $picker = $terminal->picker($view);
A Game::Terminal::Kit::Picker over the cards "playable" lists, in the order the hand is drawn, each with the move it would make as its value.
pick_move, pick_keys, pick_help
The key loop, its one-line legend and its key list. Left, up and shift-tab go one way round the cards, right, down and tab the other, home and end to the ends, and a digit to the card with that number. pick_move returns a move for "apply" in Game::Durak exactly as "move_for" does, so the loop in "run" does not care which of the two answered.
The cursor walks only the playable cards. Most of a Durak hand is unplayable most of the time: an attack is capped by the ranks already on the table and a defence by what it has to beat, so a cursor crawling the whole hand would sit mostly on cards that refuse. They are the positions "playable" already lists.
The moves that are not cards keep their own keys, t, d, x and r, for the reason Game::Schnapsen::Terminal gives: a card is a thing in your hand and taking the table is not, so they do not belong in the same row.
card_line
What the card under the cursor would do, as the line above the table:
9S: they must beat it, a trump, 4 more could follow
JS: beats the 9S
Both halves come out of the view the engine already handed over, the bout's cap and its unbeaten attack, so nothing here recomputes a rule. The trump is called out because spending one is the cost a player is weighing, and the room left in the bout because the last card it can hold is a different decision from the first.
narration, narrate
What a list of events did, from one seat's side of the table, as a list of strings; and the same, printed. They are separate because the painted screen puts those strings in the header bar rather than printing them, and a sentence that only exists inside a print can be used by exactly one of the two.
say
Prints a line to out. The one place in the distribution that prints.
SEE ALSO
Game::Durak, Game::Durak::Bot.
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.