NAME

Game::Merrills::Rules - which moves are legal, and what a move does to a position

VERSION

Version 0.03

SYNOPSIS

use Game::Merrills::Board;
use Game::Merrills::Rules;

my $position = Game::Merrills::Rules::position(Game::Merrills::Board->new);

my $moves = Game::Merrills::Rules::generate($position, 'white');     # 24 of them

Game::Merrills::Rules::apply($position, 'white', $moves->[0]);
Game::Merrills::Rules::unapply($position, 'white', $moves->[0]);

DESCRIPTION

The rules of Nine Men's Morris as plain functions over a position. Nothing here keeps a game: there is no turn, no history and no result. Give the functions a position and a side and they say what that side may do; give them a move and they do it or undo it.

A position

An arrayref of 26 values. The first 24 are the points in point order, 1 for a white man, -1 for a black one and 0 for an empty point, exactly as Game::Merrills::Board holds them. The last two are the men white and then black have yet to place. "position" makes one from a board and "board" makes a board from one.

A move

A raw move, the arrayref Game::Merrills::Move describes. A move is always the whole turn. One that closes a mill names the man it takes, so a position is never left waiting for a capture.

The placing rules

While a side has men in hand, its turn is to put one on any empty point.

A man that lands as the third of its side in a row closes a mill, and the move takes one enemy man off the board.

A man in a mill is safe while any enemy man stands outside one. When every enemy man on the board is in a mill, any of them may be taken.

A man that completes two mills at once, one across and one down, still takes one man.

Only men on the board can be taken. Men in hand are out of reach.

The moving rules

Once a side has placed every man, its turn is to move one of them along a line to a neighbouring empty point. A man may not pass over another. Mills are closed and men taken exactly as while placing, and a man may step out of a mill and back in to close it again.

Flying

A side with exactly three men on the board and none in hand may move a man to any empty point, neighbour or not. The two sides are judged apart, so one may be flying while the other still moves along the lines.

Flying is the usual rule and is on unless a function is told otherwise. Each function that depends on it takes a last argument for it: leave it out or pass a true value for flying, pass 0 for a game without it.

A side that cannot move

A side whose every man is hemmed in has no move, and "generate" returns an empty list for it. A side still placing always has a move, since eighteen men cannot fill 24 points, and so does a side that is flying.

FUNCTIONS

None is exported. A side is white or black; anything else dies, as does a position that is not 26 values.

position

The position of a Game::Merrills::Board, as a fresh arrayref.

my $position = Game::Merrills::Rules::position($board);

board

A new Game::Merrills::Board holding a position.

my $board = Game::Merrills::Rules::board($position);

phase

What a side does on its turn: placing while it has men in hand, flying when it has none in hand and exactly three on the board, and moving otherwise. Takes the flying argument.

Game::Merrills::Rules::phase($position, 'white');
Game::Merrills::Rules::phase($position, 'white', 0);     # never 'flying'

has_move

True when a side has at least one legal move. Takes the flying argument.

Game::Merrills::Rules::has_move($position, 'black');

generate

Every legal move for a side, as an arrayref of raw moves. A move that closes a mill appears once for each man it could take, and never without one. Takes the flying argument.

The order is fixed: by the point landed on, then the point left, then the man taken, each ascending. The same position always gives the same list.

my $moves = Game::Merrills::Rules::generate($position, 'white');
my $moves = Game::Merrills::Rules::generate($position, 'white', 0);

closes

How many mills a man of a side would complete by landing on a point: 0, 1 or 2. Give the point it leaves, or undef when it comes from the hand. The point left counts as empty, so a man stepping along a row it already stood in closes nothing.

Game::Merrills::Rules::closes($position, 'white', undef, $to);
Game::Merrills::Rules::closes($position, 'white', $from, $to);

removable

The enemy men a side may take on closing a mill, as a list of points in order: the enemy men outside any mill, or every enemy man when there are none outside.

my @points = Game::Merrills::Rules::removable($position, 'white');

apply

Plays a raw move for a side, changing the position, and returns it. The move is taken on trust: hand this only moves that "generate" returned.

Game::Merrills::Rules::apply($position, 'white', $raw);

unapply

Takes back a raw move a side played, changing the position, and returns it. The move must be the last one applied.

Game::Merrills::Rules::unapply($position, 'white', $raw);

CONSTANTS

HAND_WHITE

24, where a position holds the men white has yet to place.

HAND_BLACK

25, where a position holds the men black has yet to place.

RM_FROM
RM_TO
RM_REMOVE
RM_CLOSES
RM_FLEW

The indexes of a raw move, the same as in Game::Merrills::Move.

AUTHOR

LNATION, <email at lnation.org>

LICENSE AND COPYRIGHT

This software is Copyright (c) 2026 by LNATION.

This is free software, licensed under:

The Artistic License 2.0 (GPL Compatible)