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, W and B, 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-flying for 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)