NAME

Game::Dominoes::Terminal - the interactive game, and the only module here that does I/O

VERSION

Version 0.01

SYNOPSIS

use Game::Dominoes;
use Game::Dominoes::Bot;
use Game::Dominoes::Terminal;

Game::Dominoes::Terminal->new(
    game => Game::Dominoes->new(seed => $bytes, players => 4),
    bots => { 2 => $bot_a, 3 => $bot_b, 4 => $bot_c },
)->start;

DESCRIPTION

Everything in this distribution that reads a handle or writes to one is here. Game::Dominoes and the modules under it do no input and no output at all, and nothing in the engine loads this module: the dominoes script does.

The handles are properties. in and out default to STDIN and STDOUT, and a test hands it two in-memory filehandles and plays a whole game in process, which is the reason for the split. start returns the Game::Dominoes::Result and never calls exit, so the script owns the exit status.

Game::Cribbage is a thousand lines of escape codes in its top level namespace module, which is why only Game::Cribbage::Board was ever reusable by anything. The top level module here is the engine.

The table

         ┌───┬───┐┌───┬───┐
         │● ●│   ││   │● ●│
         │ ● │ ● ││ ● │   │
         │● ●│   ││   │● ●│
         └───┴───┘└───┴───┘
           │
         ┌───┐
         │● ●│
┌───┬───┐│ ● │┌───┬───┐
│●  │● ●││● ●││● ●│●  │
│ ● │ ● │├───┤│ ● │   │
│  ●│● ●││● ●││● ●│  ●│
└───┴───┘│ ● │└───┴───┘
         │● ●│
         └───┘

Tiles are drawn: nine columns by five rows lying along their run, five by nine standing across it, with the pips where a domino has them. A double is laid crosswise, so it stands where the tiles either side of it lie, and that is the whole shape of All Fives on the screen rather than in the rules. Turning a domino turns its pips with it, so a tile standing across its run has the two-spot and the three-spot on the other diagonal.

The arms hang off the spinner: each starts at the spinner's own column with a stroke joining it to the tile it grew out of. They are drawn as runs across the screen rather than as columns down it, because a tile standing on end is nine rows tall and two of them would be a screenful.

A run that does not fit continues on the rows below it. "width" says how much it may use, and "compact" goes back to [6|4] on one line for a small window. "ascii" draws the same tiles in +-| and *.

The hotseat problem, which checkers did not have

Checkers is perfect information, so its hotseat mode just redraws the board. Every hand here is a secret, and a hotseat game on one screen would show seat 2 what seat 1 was just looking at. That is not cosmetic: it makes the mode useless for playing, and useless for testing too, because a bug in view is invisible when everything is on screen anyway.

So the mode is built around a hand-over. Between seats the terminal clears, says whose turn it is and nothing else, and waits for a keypress.

And the table is drawn from a view, never from the game object. If this reached into $game->hand(2) to draw, the clear-screen would be the only thing protecting a hand and a scrollback buffer would defeat it. Rendering from the view means the secret was never printed. It also makes this a second test of view, and a good one, because a leak shows up as something visible on a screen rather than as a key in a hashref nobody inspected.

Why this phase is not last

A hotseat game at a prompt is the only cheap way to play three and four seat dominoes before a website can host it. Rules bugs found here are found before they are tangled up with a database and a migration.

PROPERTIES

game

$term->game;

The Game::Dominoes being played.

bots

$term->bots;   # { 2 => $bot }

Which seats a bot plays, keyed by seat. Any seat not named is played by a person. An empty hashref is a full hotseat game; every seat named is a demonstration nobody has to sit through.

in, out

$term->in;
$term->out;

The handles, defaulting to STDIN and STDOUT. Hand it two in-memory handles and a whole game runs in process.

colour

$term->colour;

Whether to use colour. On only when out is a terminal and NO_COLOR is unset, unless it is set explicitly.

ascii

$term->ascii;

Draw without anything but plain ASCII.

compact

$term->compact;

Tiles as [6|4] on one line instead of drawn, for a window too small for the drawn table.

width

$term->width;

What the drawn table may use before a run wraps onto the rows below it. COLUMNS from the environment, or 80.

handover

$term->handover;

Whether to stop and clear between seats. On by default whenever more than one person is playing.

quit

$term->quit;

Set when the player asked to stop.

FUNCTIONS

start

my $result = $term->start;

Plays the game and returns the Game::Dominoes::Result, or undef if it was abandoned. Never calls exit.

show

$term->show($view);

Draws one screen from a view: the scores, the tile counts, the table, the open ends with what would score, and the seat's own tiles.

table

$term->table($view);

The table as a list of lines, drawn from a view. The main line runs across, a double stands across the run it is in, and the spinner's two arms hang above and below it from its own column.

hand_lines

$term->hand_lines($view);

The seat's own tiles, drawn, with what to type under each one so that nobody has to count pips to name a tile.

command

$term->command($line, $view);

Applies one line of input. Returns true to keep going, false to stop. Undefined input is end of file and stops cleanly.

help

$term->help;

The command list, as lines.

SEE ALSO

Game::Dominoes, the engine; Game::Dominoes::Bot, the opponent.

AUTHOR

LNATION <email@lnation.org>

BUGS

Please report any bugs or feature requests to bug-game-dominoes at rt.cpan.org, or through the web interface at https://rt.cpan.org/NoAuth/ReportBug.html?Queue=Game-Dominoes.

SUPPORT

You can find documentation for this module with the perldoc command.

perldoc Game::Dominoes::Terminal

ACKNOWLEDGEMENTS

LICENSE AND COPYRIGHT

This software is Copyright (c) 2026 by LNATION <email@lnation.org>.

This is free software, licensed under:

The Artistic License 2.0 (GPL Compatible)

1 POD Error

The following errors were encountered while parsing the POD:

Around line 507:

Non-ASCII character seen before =encoding in '┌───┬───┐┌───┬───┐'. Assuming UTF-8