NAME
Game::Oware::Scoring - what has been captured, and what the result was
VERSION
Version 0.02
SYNOPSIS
use Game::Oware::Scoring;
Game::Oware::Scoring->captured($board); # { p1 => 8, p2 => 3 }
Game::Oware::Scoring->score($board, 'active'); # undef
Game::Oware::Scoring->target_reached($board); # 'p1', or undef
DESCRIPTION
captured and score are two names on purpose, and not for the reason you would expect
In Game::Reversi::Scoring the two names exist because the arithmetic differs: a disc count is not the official score, which awards the empty squares to the winner, and an engine that reported one for the other would disagree with every published record.
In Oware the arithmetic is identical. A seat's score is the seeds in its store, during the game and at the end of it, and nothing is redistributed when the game stops. The plan for this distribution claimed the two would diverge at the end and that claim is wrong: the sweeps move seeds into the stores before the game is over, not after, so a store is a store throughout.
What differs is when the number is valid, which is why there are still two names. captured is a running total and is always available. score is a result, needs to be told the game has finished, and is undef otherwise. A caller that reaches for score in the middle of a game gets nothing rather than a plausible number it can publish by accident, and that is the whole of the protection this pair buys.
There are two sweeps, and they are two functions on purpose
Two sentences in the source, three paragraphs apart, describe the same physical gesture with two different owners:
the failed feed: "the current player captures all seeds in their own
territory"
the cycle: "each player captures the seeds on their side of the
board"
The first is one-sided, to the seat that could not feed, and it takes every seed on the board. The second is a split: each row goes to its own store.
One sweep($board, $seat_or_undef) is how the failed-feed ending quietly starts splitting, or the cycle ending quietly starts handing everything to whoever moved last. Neither shows up as an error. Both produce a finished game with a plausible score, both leave the total at forty-eight, and both look right in a log. The only test that catches the confusion is one that sweeps the same board both ways and asserts the two results differ, which is why t/14-endings.t has one.
Seeds on the board belong to nobody
A player with forty seeds sitting in their row has captured nothing and is very often losing, so there is no function here that counts them. That is "seeds_on_side" in Game::Oware::Board, which says the same thing in its own POD, and it is a position feature rather than a score.
Twenty-five wins and twenty-four all draws
"The game is over when one player has captured 25 or more seeds, or each player has taken 24 seeds (draw)." Twenty-five or more: a single capture can carry a store from twenty-three to twenty-six.
FUNCTIONS
TO_WIN
Twenty-five, the seeds that win a game.
DRAW_AT
Twenty-four, the seeds each that draw one.
captured
The running totals as { p1 => N, p2 => N }.
taken
How many seeds a capture chain holds, without applying it. Takes the board the chain was found on.
leader
Who has captured more, or undef if they are level. Not a prediction.
target_reached
The seat with twenty-five or more, or undef.
is_draw
True when both stores hold exactly twenty-four.
score
Game::Oware::Scoring->score($board, $status);
The official result, or undef unless $status is finished.
sweep_to
Every seed on the board into one seat's store. The failed-feed ending. Returns a new board.
sweep_split
Each seat's own row into its own store. The cycle ending. Returns a new board.
SEE ALSO
Game::Oware, Game::Oware::Board
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.