NAME

Term::Chess::Game - a game of chess over a Chess::Plisco position

VERSION

Version 0.03

SYNOPSIS

use Term::Chess::Game;

my $g = Term::Chess::Game->new(seed => $thirty_two_bytes, white => 'p1');
$g->turn;                     # 'p1'
$g->legal;                    # [ 'a2a3', 'a2a4', ... ] as coordinates
my $why = $g->play('e4');     # 0, or a Term::Chess::Error
$g->play('e7e5');
$g->san_log;                  # '1. e4 e5'
$g->status;                   # 'active' | 'finished'
$g->result;                   # { winner, reason }

my ($replayed, $why, $at) = Term::Chess::Game->replay(
    seed => $seed, white => 'p1', moves => $g->log);

DESCRIPTION

A game between two seats, p1 and p2, over one Chess::Plisco::Engine::Position. Plisco holds the position, generates the legal moves and judges checkmate, stalemate, the fifty-move rule and insufficient material; this class knows which seat is which colour, what a move is called, why a move that is not on Plisco's list was refused, when the same position has come round three times, and how to replay a log. No rule of chess is written here.

The seat named white plays White and moves first.

A move is coordinates in, coordinates stored, SAN beside

play takes coordinates (e2e4, e7e8q) or standard algebraic notation (Nf3, exd5, O-O, a8=Q). Whatever was typed, the log holds the coordinates, because SAN depends on the position and is ambiguous to read back years later; san_log gives the same moves as numbered SAN for a person.

A refusal is returned, never thrown

play returns 0 when the move was played and a Term::Chess::Error when it was not. Plisco itself dies on a string it cannot read; that die is caught here and becomes bad_move, so a caller never needs an eval.

A move that parses but is not on the legal list is named from the near misses: an empty from-square is no_piece; a piece of the other colour is not_your_turn; a pawn move to the last rank with no piece named, or the wrong one, is bad_promotion; a move the piece can make that only the king forbids is in_check; everything else is not_legal. The last two carry the legal list.

How a game ends

After every move Plisco is asked whether the game is over: checkmate wins for the seat holding the mating colour; stalemate, fifty moves without a capture or a pawn move, and insufficient material are draws. Then the position's signature is counted through every signature since the start, and a third occurrence is a draw by repetition. A seat may also resign. A game built from a position that is already over is finished at once.

METHODS

new

Term::Chess::Game->new(seed => $bytes, white => 'p1', position => $fen);

seed is exactly thirty-two bytes and is what the bot draws its rung from; anything else is a programmer error and croaks. white is the seat that plays White, p1 by default. position is a FEN to start from instead of the opening; one Plisco refuses croaks with Plisco's reason.

white

black

The seat playing each colour.

status

active or finished.

turn

The seat to move, or undef once the game is finished.

side

white or black: the colour to move.

The legal moves as coordinates, in the order Plisco yields them. Empty once finished.

play

my $why = $g->play('Nf3');

0, or a Term::Chess::Error.

resign

$g->resign('p1');

Finishes the game for the other seat with the reason resignation. Returns 0, or game_over if it was already finished.

log

The moves played, as coordinates, a copy.

san_log

The same moves as numbered SAN: 1. e4 e5 2. Nf3. A game that began with Black to move starts 1... e5.

result

{ winner => 'p2', reason => 'checkmate' }

winner is a seat, or undef for a draw or while the game is active. reason is one of checkmate, stalemate, fifty_moves, insufficient_material, repetition, resignation, or undef while active.

winner

The seat that won, or undef.

position

The Chess::Plisco::Engine::Position itself, for drawing and for the bot. It is the live object: a caller that moves on it has moved in the game.

signatures

A copy of the position's signature at the start and after every move, in order. The bot's search takes this list so it can see a repetition coming.

seed

The seed, but only once the game is finished; undef while it is active.

bot

The name of the bot class, loaded on demand.

replay

my ($game, $why, $at) = Term::Chess::Game->replay(
    seed => $seed, white => 'p1', position => $fen, moves => \@coordinates);

my ($again, $why, $at) = $game->replay(\@coordinates);

Builds a fresh game and plays the moves into it. In list context returns the game, the first refusal or 0, and the index of the move that was refused (or the count of moves when none was). In scalar context returns the game, or undef if any move was refused. The instance form replays from this game's own seed, colours and starting position.

to_text

my $text = $g->to_text;

The game as a string, from which "from_text" builds the same game again: the seed in hex, the seat that plays White, the starting position as FEN, every move as coordinates, the seat that resigned or -, and the status. Two games with the same history write the same string.

from_text

my $again = Term::Chess::Game->from_text($text);

A new game built from a string "to_text" wrote, by replaying its moves from its starting position, so the signatures behind a repetition, the SAN log and Plisco's own counters all come back as they were, and a resignation is made again after the last move. The new game shares nothing with the one that was written, and writes the same string.

A string that is not one to_text writes croaks with a sentence naming what is wrong: a missing or unknown line, a seed that is not sixty-four hex digits, a starting position Plisco refuses, or a move or resignation the replay refuses, by its number.

other

Term::Chess::Game::other('p1');    # 'p2'

The other seat. A function, not a method.