Royal Ur library

Core functionality for classical ROGOUR

The player are named Green (to move) and Red. Each board square is assigned a character.

D C B A     Z Y
1 2 3 4 5 6 7 8         
d c b a     z y

Green pieces move through abcd12345678yz, while Red pieces move ABCD12345678YZ. Internally the board is represented as an array of length 22, indexed thus,

18 17 16 15   [21] 20 19
 4  5  6  7  8  9  10 11
 3  2  1  0   [14] 13 12

Positions 14 and 21 respectively store the number of Green/Red pieces out of play (born-off). The number of pieces at home is implicit (total must sum to 7). You may find it easier to picture the game and internal representation like that:

Red   15 16 17 18+                    19 20+ 21
                   4 5 6 7& 8 9 10 11 
Green  0  1  2  3+                    12 13+ 14

The plus sign indicates the square bestows an extra roll. The ampersand provide protection from hits as well.

The board can be encoded as either a code or an index. Codes are printable strings (of length 5) intended for “human interaction” and light usage, i.e. copy/paste for sharing or when the number of boards is relativly small. The index is tighter representation mapping the board to an unique integer in the range [0,137913936), the total number of Ur positions. Indices are computationally slower than codes, but enable storing per-board values in one contiguous memory block, indexed by the board index, for the full game space. Given the ridiculous overhead of Python lists, and even the supposedly efficient arrays, the only viable option is to work with low-level bytearrays indexed by the board index.

royalur.urcore.startPosition()

Staring position.

royalur.urcore.allActualMoves(board, pips, froms=None)

Return a list of all actual moves by Green given the dice.

actual here means omitting the cases where Green can’t move. Each returned move is a (b,e) pair, where e is True when Green has an extra turn (and thus the board has not been flipped), or False and thus this is Red turn and the board is flipped.

royalur.urcore.allMoves(board, pips, froms=None)

Return a list of all moves by Green given the dice.

Same format as allActualMoves(), but including the “no-move” board from 0 pips.

royalur.urcore.getPips()

Get a “dice” roll.

royalur.urcore.reverseBoard(board)

Reverse roles of Red and Green.

royalur.urcore.homes(board)

Helper returning a (numberOfGreenMenAtHome, numberOfRedMenAtHome) pair.

royalur.urcore.gameOver(board)

True if game on board is over, False otherwise.

royalur.urcore.typeBearOff(board)

True if board is in bear-off mode. (i.e. no more contact possible).

royalur.urcore.getIndex(board)

Get board index from either a board, code, or index (convenience)

royalur.urcore.getBoard(key)

Get internal representation of board from either a board, code, or index (convenience)

royalur.urcore.getCode(board)

Get board code from either a board, code, or index (convenience)

royalur.urcore.boardAsString(board)

Board as a printable string (debug).

royalur.urcore.positionsIterator(gOff=0, rOff=0)

Iterate over all positions with gOff/rOff Green/Red pieces (respectively) off.

royalur.urcore.validBoard(b)

A valid ROGOUR board (debug)

Play and rollout functions

Functions to play ROGOUR games and positions, using different strategies for X and O.

royalur.play.rollout(board, nTrials, playerX=None, playerO=None, evaluator=None)

Play board nTrials times. Report percentage of wins.

Use playerX/playerO to determine X/O moves. If unspecified, use the best human-like player. If evaluator is given, truncate the rollout at the first position with a valid probability.

royalur.play.getDBplayer(db)

Return a player using probabilities from db.

Fall back to default human player if position has no DB entry.

royalur.play.ply1(board, db)

Win probability of board at 1-ply.

royalur.play.prob(board, ply, db)

Win probability of board at ply-ply.

Human-like Players for ROGOUR

Human-like players are built upon the basic core principles of ROGOUR: hitting, not getting hit, extra moves and bearing off. Such core principles (or strategies) are captured in a move filter. A move filter takes a set of positions (all possible moves for some position and dice in our case), and filters out undesirable positions. It may filter none at all or all but one. For example hitAny() keeps the positions with the highest number of pieces in the opponents home, and so effectively prefers positions with hits over positions without hits.

More advanced players are built on top of those core principles by creating a compound filter, which is a list of core filters which are executed in order, from first to last.

Probabilities Database

Per-Position win probabilities for the full game space.

class royalur.probsdb.PositionsWinProbs(fname=None)

Win probability for Green (on play) for each ROGOUR position.

aget(board)

Get the win probability associated with board.

aset(board, pr)

Set the win probability associated with board to pr.

board2key(board)

Return the db internal ‘position’. This happens to be the offset into one humongus byte array.

get(bpos)

Get the win probability associated with position bpos.

key2board(key)

Return the key of the board associated with this position.

keys()

Return the set of valid board positions (will be very slow the first time).

set(bpos, pr)

Set the win probability associated with position bpos to pr.