NAME
Game::Reversi::Terminal - a playable game on two filehandles
VERSION
Version 0.01
SYNOPSIS
my $terminal = Game::Reversi::Terminal->new(
level => 3, variant => 'historic', colour => 'b');
my $result = $terminal->start; # returns, never exits
# and in a test, with no pipe and no subprocess:
my $terminal = Game::Reversi::Terminal->new(in => $in, out => $out);
DESCRIPTION
A person against Game::Reversi::Bot, on a board drawn in text.
The distribution ships something playable because a rules engine nobody can play is a rules engine nobody has checked. This one found two faults during phase 06 that the suite had not.
It is separate from the engine on purpose
Game::Cribbage put about a thousand lines of escape codes in the top level namespace module, which is why only its ::Board ever turned out to be reusable. The top level module here is the engine, and this is a consumer of it exactly as a web adapter would be. The suite asserts that the engine can be loaded without pulling this in.
in and out are properties
Defaulting to STDIN and STDOUT. That is what makes a terminal testable: the suite drives a whole game in process against in memory handles, with no pipe, no fork, and no subprocess writing into the TAP stream.
start returns, and never exits
A module that calls exit cannot be tested and cannot be embedded. The exit status belongs to bin/reversi, which is the only part that knows it is a program.
The board
a b c d e f g h
┌───┬───┬───┬───┬───┬───┬───┬───┐
8 │ │ │ │ │ │ │ │ │ 8
├───┼───┼───┼───┼───┼───┼───┼───┤
...
5 │ │ │ │ ● │ ○ │ * │ │ │ 5
├───┼───┼───┼───┼───┼───┼───┼───┤
4 │ │ │ * │ ○ │ ● │ │ │ │ 4
...
└───┴───┴───┴───┴───┴───┴───┴───┘
a b c d e f g h
A ruled grid with the discs sitting in it, because a reversi board is a grid and eight rows of dots are a list. ascii draws the same board as X and O on +-|, for a terminal that cannot manage the rest.
Two glyphs, not one glyph in two colours
Two discs told apart only by an escape code are indistinguishable in half the terminals in the world and unreadable to a good fraction of the people using the other half. A filled disc and a hollow one are as far apart on a screen as they are on a table, and they are still that far apart with no colour at all. So the glyphs carry all of the information and colour carries none: strip the escapes and the board reads exactly the same.
Colour is off unless the output is a terminal, and off regardless if NO_COLOR is set to anything non-empty. An explicit ansi option overrides the terminal check but not NO_COLOR, because somebody who set that meant it.
METHODS
new
in, out, level, variant, colour, seed, quiet, ansi, ascii.
ansi
Whether colour will be used.
in, out, game
The handles, and the game once start has built one. All three are read and write: a test hands this a tied handle and reads the game back out of it.
The ansi option is held privately, because "ansi" is a question about the handle and the environment as well as the option, and there should be one public answer to it rather than two.
level, variant, colour, seed, quiet, ascii
The options, as properties. colour is the side the person is playing.
glyph
The disc a colour is drawn as, which ascii decides.
board_text
The board as lines of text: a ruled grid, files across the top and the bottom, ranks down both sides, and the legal moves for a colour marked with *. Marking them is not decoration: working out which squares outflank something is the engine's job, and a person doing it by hand every turn will get it wrong.
start
Plays a game and returns its Game::Reversi::Result.
AUTHOR
LNATION <email@lnation.org>
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.
1 POD Error
The following errors were encountered while parsing the POD:
- Around line 381:
Non-ASCII character seen before =encoding in '┌───┬───┬───┬───┬───┬───┬───┬───┐'. Assuming UTF-8