NAME
Game::Oware::Rules - what a move is allowed to do, and what it does
VERSION
Version 0.01
SYNOPSIS
use Game::Oware::Rules;
my @legal = Game::Oware::Rules->legal_moves($board, 'p1', 'abapa');
my ($next, $move) = Game::Oware::Rules->resolve($board, 4, 'p1', 'abapa');
DESCRIPTION
The legal move list is not "the houses with seeds in them"
The feeding obligation prunes it, and that is the whole of this module's reason to exist. A bot built on the unpruned list offers moves the rules then refuse, which surfaces as a fault in whatever is consuming the engine rather than in the bot, because a terminal only ever offers what legal_moves returned and an adapter is the only thing that ever sees the two disagree.
A grand slam is decided on the resulting board, not on the chain's length
A chain of six houses is not automatically a grand slam: the opponent may hold seeds in a house the chain stopped short of. A chain of one house is a grand slam if that house held their last seeds.
So the question is "would applying this leave them with nothing", asked of the board, and it is asked before the chain is applied. That is why "capture_chain" in Game::Oware::Board finds a chain without taking it.
Under no_capture, feeding is the same question as reaching
"If an opponent's houses are all empty, the current player must make a move that gives the opponent seeds." That reads as though it needs a simulation - sow, capture, then see whether anything survived - and under the shipped policy it does not. The derivation, because a reader will otherwise assume the code is cutting a corner:
the premise is that the opponent holds zero seeds;
so the only seeds that can be on their side after the move are the ones this sowing just put there;
so a chain that captures all of them is capturing all of the opponent's seeds, which is a grand slam by definition;
so under
no_capturethat capture is forfeited and the seeds stay;therefore any move that reaches the opponent's row feeds them, and no move that fails to reach it can.
Which reduces the test to arithmetic: a house needs at least 6 - position seeds, where position is 0 to 5 within its own row. reaches returns that number.
The code simulates anyway, and the suite asserts the two agree. Two independent derivations of one predicate is cheap and real, and the arithmetic is the half that silently stops being true if the policy is ever changed: under illegal_unless_only the argument above does not apply at all, because a slam is allowed and what it leaves behind depends on the capture. That is one more mark against the variations this distribution did not take, and it is worth more than the line of code it saves.
A seat on turn always has seeds, so there is no pass
Under no_capture a seat can never be reduced to nothing: a capture that would do it is a grand slam and is forfeited. So a player is always able to move, and the empty list legal_moves can return means one thing only - the opponent is starved and nothing this seat can play reaches them. That is the failed-feed ending, not a turn forfeit, and any code that treats it as a pass has broken the claim Game::Oware::Notation makes about transcripts.
METHODS
starved
True when a seat has no seeds in any of its six houses.
reaches
How many seeds a house needs in order to reach the opponent's row.
feeds
Whether sowing a house leaves the opponent with seeds on their side. By simulation.
sowable
A seat's houses that hold at least one seed, before the feeding filter.
legal_moves
The houses a seat may sow from, in ascending order. Empty only through the failed-feed ending.
resolve
my ($next, $move) = Game::Oware::Rules->resolve($board, $house, $seat, $variant);
Sows, finds the chain, decides the grand slam, and applies whichever of the two outcomes the variant calls for. Returns the new board and a Game::Oware::Move. The board passed in is not modified.
It does not know whose turn it is, whether the game is over, or what the move log says. Those are Game::Oware's.
SEE ALSO
Game::Oware, Game::Oware::Board, Game::Oware::Variant
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.