NAME

Game::Sternhalma::Notation - names, moves, positions and games as text, with no compiled code

SYNOPSIS

use Game::Sternhalma::Notation ':all';

my $hole = hole_of('i5');                 # 60, the centre
my $name = name_of(0);                    # 'a1', the top tip

my $move = parse_move('e5:g5:g7');        # { kind => 'chain', holes => [...] }
my $text = format_move($move);

my ($p, $code) = parse_position(SETUP_TWO);
print format_position($p), "\n";

DESCRIPTION

Everything a program needs to read and write sternhalma as text, in plain Perl. It loads no compiled code, so a saved game can be read, and a move told from a typo, on a machine where the engine was never built. It knows no rule: whether a move is legal is the engine's business.

Hole names

There is no standard notation for the star, so this is this distribution's own. The rows are a (the top tip) to q (the bottom tip), and a hole is its row and its place in the row counted from the left from 1. The two rows of thirteen are e and m; the centre is i5.

Moves

A step is two names joined by -: e5-f5. A chain of hops is every hole it lands on, from its start, joined by :: e5:g5:g7. Then pass, and resign and a seat: resign 2.

Positions

The seventeen rows from the top, / between them; in a row a digit 0 to 5 is that seat's piece and [n] a run of n empty holes. Then the seat to move, the seat count and the piece count. Then, only once the game has moved on from its set-up, a ; and the game: f= the seats home in order, r= the seats that resigned in order, p= the ply, k= passes in a row, c= the cap if it is not 1,000 a seat, o= how the game ended (1 home, 2 the cap, 3 nobody could move, 4 resigning).

FUNCTIONS

name_of

name_of($hole)

The name of a hole, 0 to 120, or undef.

hole_of

hole_of($name)

The hole a name names, or undef.

row_len

row_len($row)

How many holes row $row (0 to 16) has.

parse_move

my $m = parse_move($text);

A move as a hash: kind (step, chain, pass or resign), holes for a step or a chain, seat for a resignation. undef for text that is not a move. It checks the text, not the board.

format_move

The text of a move hash.

parse_position

my ($p, $code) = parse_position($string);

The position as a hash (cells, 121 of a seat or undef; to_move, seats, pieces; finished and resigned, lists of seats; ply, pass_run, cap, how) and 0; or undef and the refusal code, the same codes and the same order as "reason" in Game::Sternhalma::Engine.

format_position

The string of a position hash.

format_game

my $text = format_game({ seats => 3, pieces => 10, cap => 3000, first => 1, moves => [ ... ] });

A game as text: a header, then one move a line. first, the seat that moved first, is written only when it is not seat 0; start, a position the game began from instead of its set-up, only when there is one.

parse_game

my ($game, $why) = parse_game($text);

The hash format_game takes, or undef and what is wrong with the text.

SETUP_TWO

The position string of the two-seat set-up.

HOLES

121.

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)