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.