NAME

Game::Xiangqi::Terminal - the board on a terminal, and UCCI

SYNOPSIS

my $t = Game::Xiangqi::Terminal->new(level => 8000);
my $status = $t->start;          # RETURNS the exit status; never exits

Game::Xiangqi::Terminal->new(ascii => 1)->draw;

Game::Xiangqi::Terminal->new->ucci;    # speak UCCI, draw nothing

DESCRIPTION

Everything that knows about a screen, and nothing that knows about the rules. in and out are read-write properties defaulting to STDIN and STDOUT, so a test can hand it a pair of in-memory handles and read back exactly what it drew.

start returns and never calls exit. Its return value is the exit status bin/xiangqi should use: 0 if you won or the game drew, 1 if you lost.

The pieces are the characters, and the two sides do not share them

A xiangqi set uses different characters for the two sides for five of the seven pieces, which is the opposite of chess. Red's general is 帥 and Black's is 將.

The table is pinned by codepoint and never by a pasted glyph, and t/19 asserts all fourteen numbers against it, because a glyph read in a diff is exactly the check that does not work: the red general's simplified form U+5E05 is not U+5E25, U+4EF5 is not the advisor U+4ED5 and U+50CC is not the horse U+508C, and a board drawn with any of them is perfectly legible and wrong.

ascii => 1 falls back to the seven Latin letters, uppercase for Red, and this is the only place in the distribution that uses them: elsewhere a chariot is a chariot and never a rook, because four of the seven pieces move differently from the chess piece whose name they would borrow.

Colour, and NO_COLOR beating everything

If NO_COLOR is present in the environment, output is uncoloured whatever else was asked for, including an explicit colour => 1. Its presence is what counts and not its value, so NO_COLOR=0 still means no colour.

The output handle gets a UTF-8 layer

Unless ascii is set, out is given an :encoding(UTF-8) layer when it is set. Without one, every glyph raises "Wide character in print", which is a warning and not an error: the board still appears, mangled, and the test that goes red is some later one watching for warnings.

The board is drawn in fixed cells

Every point occupies two display columns and every gap between points one, so a rank is always 26 columns wide whatever is standing on it. Nothing measures a string to decide how to pad it.

METHODS

new

Game::Xiangqi::Terminal->new(in => $fh, out => $fh, game => $g,
                             ascii => 0, wxf => 0, colour => undef,
                             level => 6000, seat => 'p1');

in and out default to STDIN and STDOUT. colour left undefined means colour unless NO_COLOR is set; level is a node budget for the opponent, and undef lets the game's seed draw a rung.

in

out

Read-write properties. Setting out also gives it a UTF-8 layer unless ascii is on, and does so at most once per handle.

game

Read-write. The Game::Xiangqi being played; start makes one if there is none.

ascii

colour

Read-write. colour can be turned on and off, but NO_COLOR in the environment has already won by the time new returns.

wxf

Read-write. Whether a move is shown in WXF beside its coordinates.

level

The node budget handed to the opponent, or undef to let the game's seed draw a rung. Read-only: it is a property of the sitting, not of a turn.

seat

The seat the person at the keyboard plays, 'p1' or 'p2'. Read-only, and it is also which way up the board is drawn.

board_lines

my @lines = $t->board_lines(position => $pos, flip => 1);

The board as a list of lines, without printing anything. Red is at the bottom unless flip. Every line is the same number of display columns.

draw

The same, printed to out. Returns the terminal.

start

Plays a game against the bot on in and out until somebody quits or the game ends. Returns the exit status and never calls exit: 0 if you won, drew or quit, 1 if you lost, 2 if a game could not be built.

ucci

Speaks UCCI on in and out and draws nothing. Returns 0.

UCCI

ucci reads UCCI on in and writes it to out. Moves are ICCS coordinates, which is what this distribution stores anyway, so a log feeds an external engine without translation.

Implemented: ucci, isready, position startpos|fen ... [moves ...], go nodes N, go depth N, stop, quit, and bestmove in reply. An unknown command is ignored rather than fatal.

Not implemented, listed so nobody has to find out by trying: ponder, time controls (go time, go movetime), banmoves, setoption, and info lines during a search.