NAME
Game::Merrills::Terminal - the game at a prompt, on a drawn board
VERSION
Version 0.03
SYNOPSIS
use Game::Merrills;
use Game::Merrills::Bot;
use Game::Merrills::Terminal;
Game::Merrills::Terminal->new(
game => Game::Merrills->new,
bot => Game::Merrills::Bot->new(level => 3),
human => 'white',
)->start;
DESCRIPTION
Everything in this distribution that reads a handle or writes to one is here. Game::Merrills and the modules under it do no input and no output, and nothing in the engine loads this module: the merrills script does.
The handles are properties. in and out default to STDIN and STDOUT, and a test, or a program that wants the game somewhere else, hands in its own.
The board
The board is drawn as it is on a table: three squares, one inside the next, joined at the middle of each side, with a rank number down the left and a file letter along the bottom.
7 .-----------.-----------.
| | |
6 | .-------W-------. |
| | | | |
5 | | .---.---. | |
| | | | | |
4 .---B---. .---.---.
| | | | | |
3 | | .---.---. | |
| | | | |
2 | .-------.-------. |
| | | | |
1 .-----------.-----------.
a b c d e f g
Beside it stand the two sides, each with the men it has in hand, on the board and lost.
There are three ways it can be drawn:
- painted
-
On a terminal with colour: a wooden board, gold lines, and the men as white and black discs. The men of a closed mill stand on a lighter ground.
- drawn
-
Without colour: the same board in line-drawing characters, white men as hollow discs and black men as solid ones.
- ascii
-
Dashes, bars,
WandB, as above. Every character is plain ASCII, for a terminal or a file that can take nothing else.
Marks
What the last move did is marked on the board, and the marks are shapes, so they are there with colour off as well as on:
(W) the man that just moved, where it landed
(.) the point it left
(x) the point a man was taken from
[W] the point under the cursor, drawn as the move would leave it
{B} a man that could be taken
<W> a part of the move already chosen
With colour on each mark has a ground of its own as well.
Under the board
The last two moves, said in words; each side's mills; whose turn it is and what they are to do; a warning once the game is half way to a draw for want of a mill; a draw offer, if one stands; and the result, once there is one.
Choosing a move with the keys
On a terminal, with Term::ReadKey installed, a move is chosen and not typed. The cursor is on the board itself, on one of the points that can be chosen, and the board is drawn as it would stand if the move were played.
A move is built a step at a time, because a move in this game has up to three parts: the man, the point it goes to, and the man it takes when it closes a mill. At each step only the points that some legal move allows next can be chosen, so no run of choices can end anywhere but on a legal move. The step that decides whether a mill closes shows at once which men could be taken, before anything is committed.
arrows to the nearest point that can be chosen that way
tab, shift-tab round every such point in turn; j and k do the same
a letter, a digit straight to that point, d then 2 for d2
enter, space choose
backspace, escape back a step
h what the bot would play, with the cursor moved to it
u take a move back
r play again a move taken back
t go back to typing
: type one command
q quit
? these, on the screen
Giving up and offering a draw are not on a key, so that a slip of the finger cannot end a game. Type them after :.
Each keystroke draws the whole screen again, over the last and with no clear between them, so the last two moves are drawn with it, above the board's own lines, and are never wiped by the move that answers them. A command typed after : is typed under that screen: backspace rubs a letter out and escape gives the command up.
Without Term::ReadKey, or off a terminal, moves are typed, and nothing else changes.
Typing
At the prompt, type a move as Game::Merrills::Notation writes it, a number from the moves list, or a command: help lists them. A move that is refused says why and what could be played instead, and the board is not drawn again, so the reason stays on the screen.
Taking a move back
u, or the typed undo, takes back your last move and every reply the bot made to it, so the board is as it was when it was last your turn. r, or the typed redo, plays that turn again. Each goes as far back, or forward, as you like, a turn at a time. Playing a move after taking one back forgets whatever could have been played again. With two people at one keyboard every move is a turn, and with two bots playing there is nothing to take back. With nothing to take back or play again a line says so and the game is unchanged. Loading a game starts its history afresh.
Off a terminal
When the input is not a terminal the screen is not cleared, nothing is painted unless colour is asked for, each line typed is echoed so that a transcript reads as a conversation, and the end of the input ends the game with Bye..
PROPERTIES
game
The Game::Merrills being played. Defaults to a new one.
$terminal->game;
bot
The Game::Merrills::Bot that plays the side the human does not, or a hashref of one for white and one for black. Without one, both sides are typed.
$terminal->bot;
human
Which side is typed: white, black, both for two people at one keyboard, or none for the bots to play each other. Defaults to white.
$terminal->human;
in
The handle read from. Defaults to STDIN.
$terminal->in;
out
The handle written to. Defaults to STDOUT.
$terminal->out;
interactive
Whether the input is a terminal. Worked out from in unless given.
$terminal->interactive;
colour
Whether to paint. On for a terminal unless the NO_COLOR environment variable is set to anything but the empty string, off otherwise, unless given.
$terminal->colour;
ascii
Whether to keep to plain ASCII. Defaults to false.
$terminal->ascii;
redraw
Whether the board is to be drawn before the next prompt.
$terminal->redraw(1);
highlight
A hashref of extra marks to draw, point number to candidate, takeable, chosen or option, on top of the marks of the last move. An option, a point that could be chosen, has a ground of its own and no brackets.
$terminal->highlight({ 19 => 'candidate' });
picking
Whether moves are chosen with the keys. On for a terminal that can be read a key at a time, off otherwise, unless given.
$terminal->picking;
keysource
A coderef that supplies the keys, one character a call and undef when there are no more, in place of the terminal. With one set, the whole of the key handling runs with no terminal at all. It is read when the terminal is built.
my @keys = split //, "\e[B\r";
my $terminal = Game::Merrills::Terminal->new(keysource => sub { shift @keys });
pending
An arrayref of characters read and put back, to be read again first. It is read when the terminal is built.
$terminal->pending;
keyboard
The Game::Terminal::Kit::Keys the keys are read through, built from "in", "keysource" and "pending" unless given.
$terminal->keyboard;
screen
The Game::Terminal::Kit::Screen everything is printed through, built from "out", "interactive", "colour" and "ascii" unless given.
$terminal->screen;
log
The Game::Terminal::Kit::Log holding the last moves in words and the lines printed since the last command was read. Built unless given.
$terminal->log;
frame
The lines of the choosing screen as it was last drawn, as an arrayref, or undef before the first.
$terminal->frame;
preview
The Game::Merrills::Board drawn in place of the game's own while a move is being chosen, or undef.
$terminal->preview;
frames
How many times the choosing screen has been drawn.
$terminal->frames;
history
The Game::Terminal::Kit::History the turns are kept in, each as "to_text" in Game::Merrills writes it, built in new unless one is given.
$terminal->history->can_undo;
METHODS
layer
Sets the output handle and the screen to what the current ascii and colour settings need. Called whenever either changes. Returns the terminal.
$terminal->layer;
options
my $fields = Game::Merrills::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: the bot's strength.
side-
One of
white,black,random: your side. hotseat-
A switch: two people at one keyboard, no bot.
bot-vs-bot-
A switch: watch two bots.
flying-
On or off,
--no-flyingfor off: a side down to three men may fly. seed-
Text: the same seed, the same game.
run
exit Game::Merrills::Terminal->run(@ARGV);
The whole command line program as a class method: parses the options bin/merrills documents, builds the game and the terminal, plays or replays, and returns the exit status rather than calling exit: 0 if you won, drew or quit, 1 if you lost, 2 for a bad option, a leftover word or a refused value, with the reason and the usage on STDERR. --help and --version print to STDOUT and return 0. Options are read from the arguments passed in, never from @ARGV, and the Getopt configuration the parse needs is restored before it returns. 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. An interrupt gives the terminal back and then stops the program as it would have. Colour is off under NO_COLOR as well as off a terminal.
start
Plays the game until it ends or the input does, and returns the game's result, undef if it was left unfinished.
my $result = $terminal->start;
replay
Shows a written game a move at a time: the board, then each move in words with the board after it. On a terminal it waits for enter before each move and stops at q. The game shown becomes the terminal's game. Returns its result, undef when the record stops short of one. Dies, naming the move, when the record does not play.
$terminal->replay($text);
turns
The loop inside "start": draw, let the bot move or read a line and act on it, until done.
$terminal->turns;
person_turn
Called by "turns" when the turn passes to a side with no bot: keeps the game in "history" as a turn. Returns the terminal.
$terminal->person_turn;
raw
Whether the terminal is at present being read a key at a time.
$terminal->raw;
recent
The last moves played, in words, as an arrayref.
$terminal->recent;
heard
The lines printed since the last command was read, as an arrayref, kept so that the answer to a command is shown again on the next choosing screen.
$terminal->heard;
keys_available
True when keys can be read one at a time: a key source was given, or the input is a terminal and Term::ReadKey loads.
$terminal->keys_available;
enter_raw
Starts reading the terminal a key at a time. Returns the terminal, or undef when that cannot be done.
$terminal->enter_raw;
leave_raw
Puts the terminal back to reading whole lines. Returns the terminal.
$terminal->leave_raw;
read_key
One key, as "read_key" in Game::Terminal::Kit::Keys names it: a character comes back as itself and the rest by name, up, enter, back_tab and so on, with escape for the escape key alone and unknown for a sequence that is none of them. A pressed escape does not swallow the next key. Returns undef when there are no more. Given a number of seconds it waits that long and no longer.
my $key = $terminal->read_key;
components
The parts of a move in the order they are chosen, as an arrayref of points: the point left, when there is one, the point landed on, and the man taken, when there is one.
$terminal->components($move);
matching
The legal moves whose parts begin with those given, as an arrayref.
$terminal->matching([ $from ]);
options_for
The points that can be chosen next, given the parts chosen so far: the next part of every legal move that begins with them, each once, in point order.
my $points = $terminal->options_for([ $from, $to ]);
complete
The legal move whose parts are exactly those given, or undef when the move is not finished.
my $move = $terminal->complete([ $from, $to ]);
nearest
The point among some candidates that an arrow key reaches from another: up, down, left or right. A point straight along the row or the file is preferred to a nearer one off to the side. Returns the point started from when there is nothing that way.
my $point = $terminal->nearest($from, 'right', \@candidates);
step_of
Which part is to be chosen next, given the parts chosen so far: from, to or remove.
$terminal->step_of([ $from ]);
pick
Lets the player choose a move with the keys and plays it. Returns an empty string when a move was played, a command when a key asked for one, and undef at the end of the input.
my $line = $terminal->pick;
done_picking
Clears what choosing put on the board. Returns the terminal.
$terminal->done_picking;
show_choice
Draws the choosing screen: the parts chosen, the points that can be chosen, the point under the cursor, and anything to be said.
$terminal->show_choice(\@prefix, \@options, $at, \@notice);
legend
The line naming the keys, at the foot of the choosing screen.
$terminal->legend;
key_lines
What the keys do, as an arrayref of lines.
$terminal->key_lines;
set_picking
Switches between choosing with the keys and typing, for the pick and type commands.
$terminal->set_picking('type');
later
Says a line so that it survives the screen being drawn again: after the board when typing, and on the next choosing screen when choosing.
$terminal->later('Taken back.');
bot_for
The bot that plays a side, or undef.
$terminal->bot_for('black');
bot_plays
True when a side's moves come from a bot and not from the keyboard.
$terminal->bot_plays('black');
bot_move
Has the bot choose and play a move for the side to move. Returns the move.
$terminal->bot_move;
played
Records a move just played: puts it into words, keeps it among the recent moves, and asks for the board to be drawn. Returns the move.
$terminal->played($move);
name_of
What to call a side: You for the human's, its name otherwise.
$terminal->name_of('white');
narrate
A move in words, in the past tense.
$terminal->narrate($move); # 'Black moved f4 to g4, closed a mill and took your man on a1.'
read_line
Prints a prompt and reads a line, without its newline. Returns undef at the end of the input. Takes the prompt, or uses the side to move. While keys are being read the line is typed a key at a time under the choosing screen, and escape gives it up, which returns the empty string.
my $line = $terminal->read_line;
say
Prints a line, and keeps it among those "heard". Returns the terminal.
$terminal->say('Taken back.');
clear
Clears the screen, when the input is a terminal. Returns the terminal.
$terminal->clear;
paint
Text wrapped in the colours named, or the text alone when colour is off.
$terminal->paint('Draw', 'mill');
ink_of
The name of the colour a side is written in.
$terminal->ink_of('white');
render
Clears the screen and draws the board and what goes under it.
$terminal->render;
glyphs
The characters the board is drawn with at the current settings, as a hashref.
$terminal->glyphs->{white};
marks
The marks to draw, as a hashref of point number to kind: those of the last move, with "highlight" on top.
$terminal->marks;
canvas
The board as rows of cells, before any character or colour is chosen.
$terminal->canvas;
board_lines
The board as an arrayref of lines ready to print.
print "$_\n" for @{ $terminal->board_lines };
aside
What stands beside the board, as an arrayref indexed by board row.
$terminal->aside;
mills_of
A side's closed mills, as an arrayref of strings such as a7-d7-g7.
$terminal->mills_of('white');
status_lines
What goes under the board, as an arrayref of lines.
$terminal->status_lines;
command
Acts on a line typed at the prompt. Returns true when it is time to stop.
$terminal->command('d2-d3');
play
Plays a move typed as notation, or says why not.
$terminal->play('d2');
play_number
Plays a move by its number in the moves list.
$terminal->play_number(3);
show_moves
Prints the legal moves, numbered.
$terminal->show_moves;
hint
What the bot would play for the side to move, as a hashref of move and a line to print.
$terminal->hint->{line};
show_hint
Prints the hint.
$terminal->show_hint;
take_back
The undo command and the u key: your last move and every reply a bot made to it, taken back. Returns 0.
$terminal->take_back;
play_again
The redo command and the r key: the turn "take_back" took back, played again. Returns 0.
$terminal->play_again;
swap
Puts another game in place of this one: clears the "highlight" and the "preview", forgets the recent moves in words and the bot's last search, and asks for the board to be drawn again. Returns the terminal.
$terminal->swap($game);
save
Writes the game to a file.
$terminal->save('game.txt');
load
Reads a game from a file and carries on from where it stopped. A loaded game starts a new history.
$terminal->load('game.txt');
set_level
Sets the strength of the bot.
$terminal->set_level(4);
toggle
Turns ascii or colour over.
$terminal->toggle('ascii');
offer_draw
Offers a draw for the human's side. A bot answers at once, taking it unless it thinks it is ahead.
$terminal->offer_draw;
answer_draw
Accepts or declines a draw that was offered.
$terminal->answer_draw('accept');
resign
Gives the game up for the human's side.
$terminal->resign;
quit
Asks whether to leave an unfinished game. Returns true to stop.
$terminal->quit;
help_lines
The help, as an arrayref of lines.
$terminal->help_lines;
show_help
Prints the help.
$terminal->show_help;
PACKAGE VARIABLES
%COMMAND-
The commands and what each does.
%GLYPH-
The characters for each way of drawing the board.
%INK,%GROUND-
The colours, as terminal codes, for what is written and what it stands on.
%BRACKET-
The pair of characters for each kind of mark.
%KEY-
The keys that stand for a command while choosing.
CONSTANTS
- PITCH
-
4, the columns from one file to the next.
- WIDTH
-
27, the columns of the board.
- ROWS
-
13, the rows of the board.
- RECENT
-
2, how many moves are kept in words.
AUTHOR
LNATION, <email at lnation.org>
LICENSE AND COPYRIGHT
This software is Copyright (c) 2026 by LNATION.
This is free software, licensed under:
The Artistic License 2.0 (GPL Compatible)