NAME

Game::Reversi::Board - the 64 squares, the eight rays, and what outflanks what

VERSION

Version 0.01

SYNOPSIS

use Game::Reversi::Board;

my $board = Game::Reversi::Board->empty;
$board->[ Game::Reversi::Board->square_of('d', 5) ] = 'b';

my @flips = Game::Reversi::Board->flips_for($board, $square, 'b');
my @moves = Game::Reversi::Board->legal_moves($board, 'b');
my $after = Game::Reversi::Board->apply($board, $square, 'b');

DESCRIPTION

The position and nothing above it. Whose turn it is, when a turn is forfeited and when the game ends are rules, and they live in Game::Reversi::Rules.

A board is an arrayref, not an object

Sixty four cells, index row * 8 + col, row 0 being rank 8 and column 0 being file a. So a8 is 0, h1 is 63, and the index reads in the order the board is drawn.

These are class methods taking a board rather than methods on a board object, because the bot's search calls "flips_for" millions of times and an object allocation per node is the difference between a level that searches and a level that gives up. A board holds no invariants, so there is nothing for an object to protect.

A cell holds undef, 'b' or 'w'

Not 0, 1 and 2. Index 0 is a real square and 0 is a plausible spelling of a colour, so a numeric encoding lets if ($board->[$sq]) mean "not black" by accident.

The rays are walked by row and column

Both bounds checked, never by adding a stride to a flat index. A strided walk stays within 0 .. 63 while stepping off the right hand edge of one rank and onto the left hand edge of the next, so a range check on the index cannot see it, and the result is a move that flips discs along a line no player can see.

METHODS

empty

A new board with all 64 cells empty.

clone

A copy of a board.

rays

The eight directions as [drow, dcol] pairs, in the order "flips_for" returns its squares.

other

The opposite colour.

square_of

An index from a file letter and a rank digit. undef for anything off the board.

name_of

The algebraic name of an index, d5 and so on.

flips_for

The squares a disc played at $square by $colour would turn, in ray order. Empty for a square that is already occupied and for a move that outflanks nothing.

Every square where "flips_for" returns something.

has_move

True if legal_moves would return anything, without building the list. The forced pass asks this once per turn, so it is worth not paying for the list.

apply

A new board with the disc placed and every outflanked disc turned. Dies on a move that outflanks nothing, which is a fault in the caller rather than a player's mistake.

count

A hashref of b and w counts. This is not the final score: a game that ends with squares still empty awards them to the winner, which is Game::Reversi::Scoring.

empties

How many squares are still empty.

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.