NAME

Game::RoyalUr::Dice - the dice of the Royal Game of Ur, as a pure function of a seed

VERSION

Version 0.01

SYNOPSIS

use Game::RoyalUr::Dice qw(throw_for marked roll_of roll_for opening_for);

my $throw = throw_for($seed, 0, 4);        # [1, 0, 1, 1], the same always
my $count = marked($throw);                # 3
my $roll  = roll_of($count, { zero_rolls => 0 });

my $again = roll_for($seed, 0, { dice => 4, zero_rolls => 0 });

my ($first, $used, $throws) = opening_for($seed, 4);

DESCRIPTION

The game is played with three or four dice, each of which lands marked or unmarked with equal chance. This module says how they fell, for a given seed and a given throw of the game, and what that is worth.

Every function here is pure. The same seed and the same throw number give the same dice in every process, on every machine, at any time, whoever asks. There is no state, and nothing here reads a clock or a random number generator.

A throw is not a roll

A throw is the dice as they fell: which are marked. A roll is what that is worth in steps on the board.

Usually they are the same number, the count of marked dice. They part company in the rule set where a throw with nothing marked is worth four steps and not none. roll_of is the one place the difference is decided, and every name in this distribution says which of the two it holds.

The seed is bytes

A seed is a string of bytes and is used as it is given: not decoded, not trimmed, not turned into hexadecimal. A string holding a character above 255 is not bytes, and is refused.

A throw number belongs to the game, not to a player

Throws are numbered from 0 through the whole game: an opening throw, a throw that could not be played, an extra throw earned on a rosette each take the next number. Which side a throw belongs to is a fact of the game and not of the dice, so a game picked up again at the same number gets the same throw.

Not the rolls of another game

The throws of a seed here have nothing to do with what Game::Backgammon makes of the same seed. Two games sharing a seed do not share their dice.

A RULE SET

roll_of and roll_for take a rule set: a hash reference, or any object, that answers zero_rolls (0 or 4: what a throw with nothing marked is worth) and, for roll_for, dice (3 or 4).

FUNCTIONS

None is exported unless asked for. :all exports all five.

throw_for

my $throw = throw_for($seed, $n, $dice);

Throw number $n of the game whose seed is $seed, as a reference to an array of $dice values, each 1 for a marked die and 0 for an unmarked one. The faces are returned and not only their sum, so that a caller can draw them.

Croaks without a seed, on a throw number that is not a whole number from 0 up, and on a number of dice that is not 3 or 4.

marked

my $count = marked($throw);

How many dice of a throw are marked.

roll_of

my $roll = roll_of($count, $rules);

What a count of marked dice is worth in steps: the count itself, except that a count of 0 is worth the rule set's zero_rolls.

roll_for

my $roll = roll_for($seed, $n, $rules);

The three above in one call: the roll that throw $n is worth under a rule set.

opening_for

my ($first, $used, $throws) = opening_for($seed, $dice);

The throw that decides who moves first. Light throws, then dark, and the side with more dice marked is $first, 'light' or 'dark'. On a tie both throw again.

$used is how many throws that took, always an even number, so that the game's own throws carry on from there. $throws is every throw made, in order.

The count of marked dice is compared, and not what the throw would be worth as a move. In the rule set where nothing marked is worth four steps, nothing marked still loses the opening to one.

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 (GPL Compatible)