NAME

Game::Checkers - English draughts as an engine, with a terminal game on top

VERSION

Version 0.01

SYNOPSIS

use Game::Checkers;

my $game = Game::Checkers->new;

$game->turn;                    # 'black'
$game->legal_moves;             # the seven opening moves
$game->move('11-15');           # a Game::Checkers::Move

my $bad = $game->move('11-15');
$bad->message if ref $bad eq 'Game::Checkers::Error';

$game->status;                  # 'active' until somebody wins
$game->result->stringify;       # 'Black wins: White has no move'

DESCRIPTION

English draughts, also called American checkers: an eight by eight board, twelve pieces a side, men moving and capturing forward only, kings one square in any direction, capture compulsory, and the crown ending the turn. "The rules implemented" in Game::Checkers::Rules states them exactly.

It is the engine behind the checkers at https://peer2peergames.com.

The engine does no input and no output. It never prints, never reads a handle, never sleeps and never calls rand, so a game is a pure function of its moves and replays anywhere. The terminal game lives in Game::Checkers::Terminal and the checkers script, and nothing in the engine loads either.

A player's mistake is returned as a Game::Checkers::Error, never thrown. Only a programmer error dies: a square outside 1 to 32, a colour that is not black or white, a variant that is not English.

The modules

Game::Checkers::Board

The 32 playing squares and what stands on them.

Game::Checkers::Squares

The numbering, and the step and jump tables built from it.

Game::Checkers::Piece

One man or king.

Game::Checkers::Move

One move, with its whole jump path and everything it captured.

Game::Checkers::Rules

Move generation over a raw position, and the interface the search uses.

Game::Checkers::Notation

Moves, positions and games as text: numeric notation, FEN and PDN.

Game::Checkers::Error, Game::Checkers::Result

Why a move was refused, and how a game ended.

Game::Checkers::Bot

An opponent at five strengths, bounded by a node budget and never by a clock.

Game::Checkers::Terminal

The game at a prompt, and the only module here that reads or writes a handle. The checkers script is a few lines of option parsing on top of it.

Draws

Three of them, and the first two are counted by the game object as it goes:

  • Threefold repetition. Every position the game has been in is counted by its FEN, which includes the side to move. The third occurrence ends the game.

  • No progress. Forty moves by each side, counted here as eighty plies, with no capture and no man moved. Any capture or man move resets it.

  • Agreement. "offer_draw" then "accept_draw" by the other side. An offer stands until the other side answers it with a move.

When a move both blocks the opponent and completes a draw counter, the block wins: a player with no move has lost.

PROPERTIES

board

Read and write Game::Checkers::Board. Pass one to start from a position without a FEN.

$game->board;

turn

Read and write string, black or white. Black moves first.

$game->turn;

variant

Read and write string. english is the only value, and anything else dies at construction. It is here so a later variant is an addition rather than a rewrite.

$game->variant;

fen

Read and write string: the position the game STARTED from, filled in at construction. Pass it to start from a position. The current position is "to_fen".

Game::Checkers->new(fen => 'W:WK5:BK28');

result

Read and write Game::Checkers::Result, undef while the game is active.

$game->result;

history

Read and write arrayref of the Game::Checkers::Move objects played, in order.

$game->history;

no_progress

Read and write integer: plies since the last capture or man move.

$game->no_progress;

repetition

Read and write hashref counting how often each position has occurred, keyed by FEN.

$game->repetition;

draw_offered_by

Read and write black, white or undef.

$game->draw_offered_by;

FUNCTIONS

status

active or finished.

$game->status;

ply

How many moves have been played.

$game->ply;

to_fen

The FEN of the current position, including the side to move.

$game->to_fen;

An arrayref of the Game::Checkers::Move objects the side to move may play, empty once the game is finished. The order is stable, so a client may show them numbered.

$game->legal_moves;

The legal moves starting on one square.

$game->legal_moves_for(11);

must_capture

True when a jump is available, which makes the legal list jumps only.

$game->must_capture;

move

Plays one move and returns it, or returns a Game::Checkers::Error saying why not. Accepts a Game::Checkers::Move, a notation string such as 11-15 or 23x14x7, the same move written as the squares on the board, f6-e5 and e3xc5xe7, or a hashref of from and to (or squares).

The short form of a jump is resolved against the legal list: 23x7 is one sequence in most positions, and where it is two the error is ambiguous rather than a guess. A multi jump stopped part way is not_legal, never completed on the player's behalf, because a position with two continuations would then be chosen for them.

my $played = $game->move('11-15');

undo

Takes back the last move and returns it, restoring the position exactly, including a crown the move gave and a king it captured. It also lifts a finish, so a resigned game can be taken back. Returns an error when nothing has been played.

$game->undo;

resign

Ends the game, the other side winning. Defaults to the side to move.

$game->resign('white');

offer_draw

Offers a draw on behalf of a side and returns it. The offer stands until the other side answers it with a move.

$game->offer_draw('black');

accept_draw

Accepts an offer made by the other side, ending the game as a draw by agreement.

$game->accept_draw('white');

decline_draw

Clears an offer made by the other side.

$game->decline_draw('white');

to_pdn

The game as PDN. Any arguments are tags, and a game that did not start from the opening position carries its FEN and SetUp tags automatically.

print $game->to_pdn(Event => 'Kitchen table', Black => 'Me');

from_pdn

Class method replaying a PDN game and returning it. Dies naming the move number when one of them is illegal, because a game record that does not replay is not a game record.

my $game = Game::Checkers->from_pdn($text);

clone

A copy of the whole game, position, history and counters, sharing nothing that matters.

my $copy = $game->clone;

CONSTANTS

NO_PROGRESS_PLIES is 80, the forty move rule counted in plies.

AUTHOR

LNATION, <email at lnation.org>

BUGS

Please report any bugs or feature requests to bug-game-checkers at rt.cpan.org, or through the web interface at https://rt.cpan.org/NoAuth/ReportBug.html?Queue=Game-Checkers. I will be notified, and then you'll automatically be notified of progress on your bug as I make changes.

SUPPORT

You can find documentation for this module with the perldoc command.

perldoc Game::Checkers

You can also look for information at:

ACKNOWLEDGEMENTS

LICENSE AND COPYRIGHT

This software is Copyright (c) 2026 by LNATION.

This is free software, licensed under:

The Artistic License 2.0 (GPL Compatible)