NAME
Games::Go::SGF2misc Reads SGF files and produces usable output in many formats
SYNOPSIS
use Games::Go::SGF2misc;
my $sgf = new Games::Go::SGF2misc;
for my $file (<*.sgf>) {
$sgf->parse($file);
# do things with the parsed sgf:
# the output options are listed below
}
as_perl
my @gametree = $sgf->as_perl; # smart enough to return an arrayref or array as appropriate
my $gametree = $sgf->as_perl; # the arrayref is probably a better choice, since it's not a copy.
# This will probably be the most useful as_ function (if you're doing
# your own custom thing). It returns all the nodes, beautifully
# parsed, with the board positions as a big matrix, kibitzen as an
# array of strings, the list of moves, the list of captures, and the
# rules, etc.
#
# Could you ask for anything more?
#
# Less memory usage perhaps? Because of the huge amount of
# semi-duplicate data in the tree, these take up (very roughly) around
# a meg of ram per 300 move game.
The Layout (Pretty Much)
# The game tree is actually an array(ref) of games.
$gametree = [ $game1, $game2, $game3, $game4 ];
# The games are hashes (refs) of game data with the Collections of
# Nodes stuffed into a kids=>[] array(ref).
$game = { variations=>6,
game_properties={
'AP' => 'CGoban:2', 'FF' => 3, 'PB' => 'Myself', 'GM' => 1, 'KM' => '0', 'SZ' => 5,
'RU' => 'Japanese', 'CA' => 'UTF-8', 'PW' => 'Me'
},
kids => [ $node1, $node2, $node3 ],
};
# And, lastly, the nodes are hashes with their own kids array.
$node = {
parent=>$game, # this is actually a pointer (ref) to the hashref
# who's kids=>[] array points to this node.
'variation' => 1,
'other' => { 'ST' => '2' }, # cgoban 1 and 2 kick this out. *shrug*
# the other properties are just values
# SGF2misc doesn't handle
'move_no' => 1, # The root node is move #0.
# It is technically possible to have more than one
# move in a given node. If so, the move counter is
# bumped up to count the last move of the node.
'kids' => [ $node1, $node2, $etc ],
# This should be pretty clear. SGF2misc leaves the sgf
# co-ordinates intact, but also presents some numerical ones for
# your enjoyment. CR is a circle, btw. The numerical co-ordinates
# are for the board matrix and start in the upper left corner
# (unlike the letter-number co-ordinates we use IRL).
'marks' => [ [ 'CR', 1, 3, 'bd' ] ],
'moves' => [ [ 'B', 1, 3, 'bd' ] ],
# This is from the root node of a handicap game... They're the handicap stones.
'edits' => [ [ 'B', 15, 3, 'pd' ], [ 'B', 3, 15, 'dp' ] ],
# The board is a matrix of descriptive characters. This is a 3x3
# board, and clearly, the edits from above do not fit on it.
'board' => [ [' ', ' ', ' '], [' ', ' ', ' '], [' ', ' ', ' '] ],
# This speaks for itself I hope.
'captures' => { B=>3, W=>4 },
# I generally like to say something friendly at the start of all my games, you?
'comments' => [ 'jettero [15k]: hi ' ],
}
Lemme See It
# Rather than explain the layout in detail, it's better to simply see
# it. WARNING: This can be gigantically huge! In memory, a complete
# game is probably around a meg. When you print them though, the
# indenting get's out of control!
use Data::Dumper;
$Data::Dumper::Maxdepth = 10; # set this to 0 to see the whole show...
$Data::Dumper::Indent = 1;
$Data::Dumper::Purity = 1 if $Data::Dumper::Maxdepth == 0;
# Purity makes some of the crazy-refs show up at the end instead
# crazy refs? EG: 'parent' =>
# $VAR1->[0]{'kids'}[0]{'kids'}[0]{'kids'}[0]{'kids'}[0]{'kids'}[0]{'kids'}[0]{'kids'}[0],
open OUT, ">darray.pl" or die $!;
print OUT Dumper( $gametree );
clsoe OUT;
# One last thing. You can check the memory usage by using the DEBUG
# environment variable (see below). Anything over a 1 will show how
# big the structures get at both parse phasen.
node_list
my $node_list = $sgf->node_list;
# Parsing the game tree from as_perl() could be pretty tedius.
# Fortunately, you don't have to. This returns a special list
# of node-id's (human readable $variation-$move_no node descriptors).
# example:
$node_list = { 'game #1' => [
[ '1-root', '1-1', '1-2', '1-3', '1-4', '1-5', ],
[ undef, undef, undef, undef, undef, '2-5', '2-6', '2-7', ],
[ undef, undef, undef, undef, undef, undef, undef, '3-7', '3-8' ],
] };
# As confusing as you found the as_perl() above, you'll probably like this.
# as_perl() understands these node identifiers!
my $game_info = $sgf->as_perl('game #1');
my $root_node = $sgf->as_perl('1-root');
my $move5_v2 = $sgf->as_perl('2-5');
my $s = $game_info->{game_properties}{SZ};
print "The board size is: ${s}x${s}!\n";
print my $c (@{ $move5_v2->{comments} }) {
print "Comment from 2-5: $c\n";
}
# Tada!!
as_text
# This is pretty much just an example
use strict;
use Games::Go::SGF2misc;
my $sgf = new Games::Go::SGF2misc;
$sgf->parse("sgf/jettero-sixrusses-2004-03-18.sgf");
my $nl = $sgf->node_list;
my $end = $nl->{'game #1'}[0][ $#{$nl->{'game #1'}[0]} ];
# 1st game 1st variation last node
my $caps = $sgf->as_perl( $end )->{captures};
print $sgf->as_text($end), "Captures: Black-$caps->{B} / White-$caps->{W}\n";
# Result:
# X O . . . O . . . . . . . . . O O O .
# . O O . O O X . X X O O X O O O X X X
# X O . . . O X . X O . O O . O X . . .
# O O . O O X O O O O O . . O X . X . .
# O O O O O X X X X O . O O X . . . . .
# O X X X O X . X . X O O X . X . . . .
# X X X O O . X . X . X X . . . . . . .
# X . . X O . . X . . . . . . . . X . .
# X . X X O O O X . . . . . . . . . . .
# X . X . X X O X O O O X . . . . . . .
# . X . . . X O O X X X X X . . . X . .
# . . . . X O O . O O O X . . . X X X X
# X X X . X X O O . O X X X . . X O O X
# X O O X . . X . O O O X X . X O O . O
# O O . O X X X X X O X X O X X X O . .
# . . O O O X . . X O O X O X O X O X .
# . . . . O O X X O O . O O O O O O X .
# . . . . . O X O . O . . . . O X X O O
# . . . . . O X O O . . . . . . . . . .
# Captures: Black-11 / White-16
parse_hash
my $hash = $sgf->parse_hash;
# You'll find this highly useless. It returns the parse tree as a perl
# hash. Check out as_perl() instead.
head1 Board Postion Character Map
$board = [
[ ' ', ' ', ' ' ],
[ ' ', ' ', ' ' ],
[ ' ', ' ', ' ' ],
];
# ' ' - an empty board position
# 'W' - a white stone
# 'B' - a black stone
# Marks are not placed on the board!
# You'll just have to fetch the marks array from the $node.
BUGS
Besides the lack of documentation? Well, I'm sure there's a bunch.
If you spot any bugs, please tell me.
ENV VARIABLES
DEBUG
Try setting $ENV{DEBUG}=1, $ENV{DEBUG}=2, or $ENV{DEBUG}=3 to see the internals.
Also, from your bash prompt you can 'DEBUG=1 perl ./myprog.pl' to
enable debugging dynamically.
DEBUG of 31 or over will show the lexical trace. That's kinda fun.
AUTHOR
Please contact me with ANY suggestions, no matter how pedantic.
Jettero Heller <japh@voltar-confed.org>