NAME

Game::Go::SGF - read and write the SGF subset a Go record needs

VERSION

Version 0.01

SYNOPSIS

my $in = Game::Go::SGF::read($text, lenient => 1);
my $game = $in->{game};

print Game::Go::SGF::write($game, black => 'Shusaku', white => 'Gennan');

DESCRIPTION

Smart Game Format, enough of it to consume a public record and to emit one.

Only the main line is read: the first child at every branch. A record's variations are somebody's analysis, and a game is what was played.

The reader is lenient on request, and strict by default

The shipped ruleset forbids suicide and enforces positional superko. Real records contain moves that neither permits, and a reader that could not be told to relax would refuse the corpus that is meant to be testing us.

The SGF specification's own execution model permits suicide outright:

When a B (resp. W) property is encountered, a stone of that color is placed
on the given position (no matter what was there before). Then the
application should check any W (resp. B) groups that are adjacent to the
stone just placed. If they have no liberties they should be removed and the
prisoner count increased accordingly. Lastly, the B (resp. W) group that the
newest stone belongs to should be checked for liberties, and if it has no
liberties, it should be removed (suicide) and the prisoner count increased
accordingly.

Note also "no matter what was there before", which is the spec saying a record may place a stone on an occupied point.

So:

lenient => 0    (default)  the shipped rules. A record that breaks them is
                           refused, naming the move number and the reason
lenient => 1               simple ko only, so a repetition a real game
                           contained is not refused

Making the default lenient would have been the easy mistake: the site would then accept an imported game that its own rules refuse. A leniently read game is a record of what somebody played, not a game this engine would have allowed.

HA places nothing

The spec:

Defines the number of handicap stones (>=2). If there is a handicap, the
position should be set up with AB within the same node. HA itself doesn't
add any stones to the board, nor does it imply any particular way of placing
the handicap stones.

So the stones come from AB and HA is a cross-check: a count that disagrees with AB is refused. A reader that placed stones from HA would put them on this distribution's star points rather than the ones the game was played with, which for a free-placement record is a different game.

A pass has two spellings

A pass move is shown as '[]' or alternatively as '[tt]' (only for boards
<= 19x19), i.e. applications should be able to deal with both
representations. '[tt]' is kept for compatibility with FF[3].

Both are read. Only [] is written. tt works as a sentinel precisely because it is column 20, off any board this distribution offers, which is also why the spec limits it to boards of 19 and under.

FUNCTIONS

read

Game::Go::SGF::read($text, lenient => 0)

A hashref: the game, and the record's size, komi, handicap, moves, first, result, players, ranks, date, rules, territory and setup.

territory is the TB and TW properties, which is the agreed territory a server wrote when the game was counted. It is the only external oracle this distribution's territory scorer will ever have.

write

Game::Go::SGF::write($game, black => $name, white => $name, date => $when)

The game as SGF. TB and TW are emitted only for a game that was actually counted, because until the players agreed there is no agreed territory.

parse_result, format_result

RE in its five forms and back again:

B+3.5   a score           B+R   a resignation
0       a jigo            B+T   a timeout
Void    no result

parse_result returns a hashref with a kind, and unknown rather than undef for anything it does not recognise, so a caller can tell "no result recorded" from "a result I could not read".

SEE ALSO

Game::Go::Notation, which owns the two coordinate alphabets and the gap between them.

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)