The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

NAME

Games::SGF - A general SGF parser

VERSION

Version 0.991

SYNOPSIS

  use Games::SGF;

  my $sgf = new Games::SGF();

  $sgf->setStoneRead( sub { "something useful"} );
  $sgf->setMoveRead( sub { "something useful"} );
  $sgf->setPointRead( sub { "something useful"} );

  $sgf->addTag('KM', $sgf->T_GAME_INFO, $sgf->V_REAL );
  $sgf->readFile("015-01.sgf");
  $sgf->setProperty( "AP", $sgf->compose("MyApp", "Version 1.0") );

DISCRIPTION

Games::SGF is a general Smart Game Format Parser. It parses the file, and checks the properties against the file format 4 standard. No game specific features are implemented, but can be added on in inheriting classes.

It is designed so that the user can tell the parser how to handle new tags. It also allows the user to set callbacks to parse Stone, Point, and Move types. These are game specific types.

SGF Structure

SGF file contains 1 or more game trees. Each game tree consists of a sequence of nodes followed by a sequence of variations. Each variation also consists a sequence of nodes followed by a sequence of variations.

Each node contains a set of properties. Each property has a "Type", "Value Type", "Flags", and an "Attribute".

Interface

The interface is broken into 3 conceptal parts

SGF Format

This is the straight SGF Format which is saved and read using "IO" methods.

User Format

This is the format that the Games::SGF user will come in contact with. Various methods will convert the Uwer Format into the Internal Format which Games::SGF actually deals with.

These can take the form of Constants:

Double Values: DBL_NORM and DBL_EMPH
Color Values: C_BLACK and C_WHITE

Or with converstion methods:

"compose"
"move"
"stone"
"point"
Internal Format

If this format differs from the others, you don't need to know.

Also see: http://www.red-bean.com/sgf

METHODS

new

  new Games::SGF(%options);

Creates a SGF object.

Options that new will look at.

Fatal
Warn
Debug

These options operate in the same fashion. There are 3 value cases that it will check. If the value is a code reference it will ccall that subroutine when the event occurs with the event strings passed to it. If the value is true then it croak on Fatal, and carp on Warn or Debug. If the value is false it will be silent. You will still be able to get the error strings by calling "Fatal", "Warn", or "Debug".

clone

  $sgf_copy = $sgf->clone;

This will create a completely independent copy of the $sgf object.

IO

readText

  $sgf->readText($text);

This takes in a SGF formated string and parses it.

readFile

  $sgf->readFile($file);

This will open the passed file, read it in then parse it.

writeText

  $sgf->writeText;

Will return the current collection in SGF form;

writeFile

  $sgf->writeFile($filename);

Will write the current game collection to $filename.

Property Manipulation

addTag

  $sgf->addTag($tagname, $type, $value_type, $flags, $attribute);

This add a new tag to the parsing engine. This needs to called before the read or write commands are called. This tag will not override the FF[4] standard properties, or already defined properties.

The $tagname is the name of the tag which will be read in, thus if you want to be able to read AAA[...] from an SGF file the tagname needs to be "AAA".

The $type needs to be choosen from the "Type" list below. Defaults to T_NONE.

The $value_type needs to be choosen from the "Type" list below. Defaults to V_TEXT.

The $flags are from the "Flags" List. Defaults to VF_EMPTY | VF_LIST.

The $attribute is from the "Attribute" List. Defaults to A_NONE.

redefineTag

  $sgf->redefineTag($tag, $type, $value, $value_flags, $attribute);

This will overwrite the flags set for $tagname. If one of the args is unset, it will be unaltered. For example:

  $sgf->redefineTag($tag, , , $flags);

Will reset $tag's $flags leaving all other properties untouched.

The property fields are the same defined the same as "addTag".

setPointRead

setMoveRead

setStoneRead

  $sgf->setPointRead(\&coderef);
  $sgf->setMoveRead(\&coderef);
  $sgf->setStoneRead(\&coderef);

These call backs are called when a properties value needs to be parsed. It takes in a string, and returns a structure of some type. Here is a possible example for a Go point callback:

  sub parsepoint {
     my $value = shift;
     my( $x, $y) = split //, $value;
     return [ ord($x) - ord('a'), ord($y) - ord('a') ];
  }
  # then somewhere else
  $sgf->setPointParse( \&parsepoint );

Note: that you should do more then this in practice, but it gets the across.

If the value is an empty string and VF_RMPTY is set then the call back will not be called but return an empty string.

setPointCheck

setMoveCheck

setStoneCheck

  $sgf->setPointCheck(\&coderef);
  $sgf->setMoveCheck(\&coderef);
  $sgf->setStoneCheck(\&coderef);

This callback is called when a parameter is stored. The callback takes the structure passed to setProperty, or component if composed, and returns true if it is a valid structure.

An Example of a stone check for go is as follows:

  sub stoneCheck {
     my $stone = shift;
     if( ref $stone eq 'ARRAY' and @$stone == 2
            and $stone->[0] > 0 and $stone->[1] > 0 ) {
         return 1;
     } else {
         return 0;
     }
  }

If the value is an empty string it will be passed to the check callback only if VF_EMPTY is not set.

setPointWrite

setMoveWrite

setStoneWrite

  $sgf->setPointWrite(\&coderef);
  $sgf->setMoveWrite(\&coderef);
  $sgf->setStoneWrite(\&coderef);

This callback is called when a parameter is written in text format. The callback takes the structure passed to setProperty, or component if composed, and returns the text string which will be stored.

An Example of a stone check for go is as follows:

  sub stoneWrite {
     my $stone = shift;
     my @list = ('a'..'Z','A'..'Z');
     return $list[$stone->[0] - 1] . $list[$stone->[1] - 1];
  }

If the tag value is an empty string it will not be sent to the write callback, but immedeitely be returned as an empty string.

nextGame

  $sgf->nextGame;

Sets the node pointer to the next game in the Collection. If the current game is the last game then returns 0 otherwise 1.

prevGame;

  $sgf->prevGame;

Sets the node pointer to the prevoius game in the Collection. If the current game is the first game then returns 0 otherwise 1.

gotoRoot

  $sgf->gotoRoot;

This will move the pointer to the root node of the game tree.

next

  $sgf->next;

Moves the node pointer ahead one node.

Returns 0 if it is the last node in the branch, otherwise 1

prev

  $sgf->prev;

Moves the node pointer back one node.

Returns 0 if first node in the branch and 1 otherwise

variations

  $sgf->variations;

Returns the number of variations on this branch.

gotoVariation

  $sgf->gotoVariation($n);

Goes to the first node of the specified Variation. If it returns 4 that means that there is variations 0..3,

Returns 1 on success and 0 on Failure.

gotoParent

  $sgf->gotoParent;

Will move the node pointer to the last node of the parent branch. This will fail if you already on the root branch for the current game.

Returns 1 on success or 0 on failure.

SGF Manipulation

addGame

  $self->addGame;

This will add a new game to the collection with the root node added. The current node pointer will be set to the root node of the new game.

Returns true on success.

addNode

  $sgf->addNode;

Adds node end of the current branch. It will fail if there is any variations on this branch.

Returns 1 on success and 0 on Failure.

addVariation

  $sgf->addVariation;

Adds a new variation onto this branch. The current branch will be changed to this new variation. It will then add the first node for this variation.

Returns 1 on sucess 0 on Failure.

removeNode

  $sgf->removeNode;

Removes last node from the current Branch, will fail if there is any variations on this branch

Returns 1 on success and 0 on Failure.

removeVariation

  $sgf->removeVariation($n);

This will remove the $n variation from the branch. If you have variations 0..4 and ask it to remove variation 1 then the indexs will be 0..3.

Returns 1 on sucess 0 on Failure.

splitBranch

  $sgf->splitBranch($n);

This will split the current branch into 2 branches, so that the last part of the branch will be a variation of the first portion. $n will be the first node in the next variation.

Your node pointer will be the last node first branch. For Example say the branch you are on has nodes 0..9 and you want to split it on node 5 will give the first branch having nodes 0..4 having one variation containing nodes 5..9 and the variations of the original branch. Your node pointer will point to node 4

This is used for adding a variation of move $n:

  $sgf->splitBranch($n);
  $sgf->addVariation;
  # set some node properties

The above code will add a variation on the a node in the middle of a node sequence in a branch.

Returns 1 on success and 0 on Failure.

flatten

  $sgf->flatten;

If the current branch has only one variation then moves nodes and variations from that one variation into current branch, and removing the old branch.

property

  my( @tags ) = $sgf->property;
  my $array_ref = $sgf->property( $value );
  my $didSave = $sgf->property( $value , @values );

This is used to read and set properties on the current node. Will prevent T_MOVE and T_SETUP types from mixing. Will prevent writing T_ROOT tags to any location other then the root node. Will Lists from being stored in non list tags. Will prevent invalid structures from being stored.

If no options are given it will return all the tags set on this node. Inherited tags will only be returned if they were set on this node.

getProperty

  my $array_ref = $sgf->getProperty($tag, $isStrict);
  if( $array_ref ) {
      # sucess
      foreach my $value( @$array_ref ) {
          # do something
      }
  } else {
      # failure
  }

Will fetch the the $tag value stored in the current node.

$isStrict is for fetching inherited tags, if set it will only return an inherited tag if it is actually set on that node.

setProperty

  fail() unless $sgf->setProperty($tag,@values);

Sets the the $tag value of the current node to @values. This method does a series of sanity checks before attempting to write. It will fail if any of the following are true:

@values > 0 and is not a list
$tag is of type T_ROOT but the current node is not the root node
$tag is a T_MOVE or T_SETUP and the other type is already present in the node
@values are invalid type values
unseting a value that is not set.

If @values is not passed then it will remove the property from the node. This is not the same as setting to a empty value.

  $sgf->setProperty($tag); # will unset the $tag
  $sgf->setProperty($tag, "" ); # will set to an empty value

Value Type Functions

compose

  ($pt1, $pt2) = $sgf->compose($compose);
  $compose = $sgf->compose($pt1,$pt2);

Used for creating and breaking apart composed values. If you will be setting or fetching a composed value you will be needing this function to breack it apart.

isComposed

  if( $sgf->isComposed($compose) ) {
     ($val1, $val2) = $sgf->compose($compose);
  }

This returns true if the value passed in is a composed value, otherwise false.

isPoint

isStone

isMove

isEmpty

  $self->isPoint($val);

Returns true if $val is a point, move or stone.

The determination for this is if it is blessing class matches m/^Games::SGF::.*type$/ where type is point, stone, or move. So as long as read,write,check methods work with it there is no need for these methods to be overwritten.

isEmpty will detect an empty tag.

point

stone

move

  $struct = $sgf->move(@cord);
  @cord = $sgf->move($struct);

If a point, stone, or move is passed in, it will be broken into it's parts and returned. If the parts are passed in it will construct the internal structure which the parser uses.

Will treat the outside format the same as the SGF value format. Thus will use the read and write callbacks for point,stone, and move.

If the SGF representation is not what you desire then override these.

empty

Will return a empty value, which can be tested with isEmpty.

Error and Diagnostic Methods

Fatal

Warn

Debug

  $self->Fatal( 'Failed to Parse Something');
  @errors = $self->Fatal;

  $self->Warn( 'Some recoverable Error Occured');
  @warnings = $self->Warn;

  $self->Debug('I am doing something here');
  @debug = $self->Debug;

These methods are used for storing human readable error messages, and testing if an error has occured.

Fatal messages are set when there is a failure which can not be corrected, such as trying to move passed the last node in a branch, or parsing a bad SGF file.

Warn messages are set when a failure occurs and it can give a good guess as to how to proceed. For example, a node can not have more then one a given property set, but if the tag is for a list it will assume that you ment to add that element onto the end of the list and spit out a warning.

Debug messages are saved at various points in the program, these are mainly finding problems in module code (what is helpful for me to fix a bug).

If called with no arguments it will return a list of all event strings currently on the stack.

Otherwise it will push the arguments onto the event stack.

Clear

  $self->Clear;

This will empty all events in the stack. This is only needed by extension modules, which need to clear the stack.

Each time the public methods are called (outside of Games::SGF) the event stacks will be cleared.

CONSTANTS

Type

These are the defined property types. They tell the engine where the tag is allowed to be.

T_MOVE

This is used for properties discribing a move. T_MOVE and T_SETUP tags may not be present in the same node.

T_SETUP

These properties are used for setting up a position on the board. Such as placing stones on the board.

T_ROOT

These properties must be in the root node. This is the root of the collection, not the root of a variation tree.

T_GAME_INFO

These are used for discribing the game. They should be on the earliest node, that the game is evident. For example if the SGF file is a fuseki, the Game_info should be when the game becomes unique in the collection.

T_NONE

There is no placement restrictions placed on tags of this type.

These can be in any node. There is no resrictions placed on these nodes.

Value Type

These discribe the types of data contained in a tag.

V_NONE

These properties have no tag content.

V_NUMBER

This is a number which satifisies the regex: [+-]?[0-9]+

V_REAL

This is a number which satifisies the regex: [+-]?[0-9]+(\.[0-9]+)?

V_DOUBLE

This is used for emphasies. For example GB move the good for black property. GB[1] would mean "Good for Black" GB[2] would mean "Very Good for Black."

DBL_NORM

Used for normal emphasis. When 1 is passed into a V_DOUBLE.

DBL_EMPH

Used for emphasis. When 2 is passed into a V_DOUBLE.

V_COLOR

This is used to specify a color, such as which color starts.

C_BLACK

Used when B is passed into a V_COLOR tag.

C_WHITE

Used when W is passed into a V_COLOR tag.

V_TEXT

Can take pretty much any text.

V_SIMPLE_TEXT

Same as V_TEXT except all spaces are reduced down to a single space.

V_POINT

This is used to specify a point on the board. Used for marking positions. This is a Game Specific property type and will be handled as V_TEXT unless a parsing callback is specified.

V_STONE

This is used to specify a stone or placement of a stone on the board on the board. Used for stone placement. This is a Game Specific property type and will be handled as V_TEXT unless a parsing callback is specified.

V_MOVE

This is used to specify a move on the board. Used making moves on the board. This is a Game Specific property type and will be handled as V_TEXT unless a parsing callback is specified.

Flags

These are various flags that can be given to a property tag. Since these are bit flags, in order to set more then one flag use the bitwise | operator. For example to set both the VF_EMPTY and VF_LIST flag use VF_EMPTY | VF_LIST.

VF_NONE

Used to specify that no flags are set.

VF_EMPTY

This also's the property to the tag to be empty. For Example MA uses this flag:

  MA[]

  or

  MA[ff][gg]
VF_LIST

This allows you to list properties together. The second example above demstrates this behavior. Used in conjunction with VF_EMPTY allows you to have a empty list, otherwise it must have at least one property given.

VF_OPT_COMPOSE

This tag allows a property to be composed with itself. For example in the specification any List of Points can be used as a List of Point composed with point, in order to specify a rectangular region of points. As an Example:

  MA[aa][ab][ba][bb]

  is equavalent to:

  MA[aa:bb]

Attribute

A_NONE

Used to specify no Attribute is set.

A_INHERIT

Currently the only Attribute defined in the specs. This property value will be passed down to all subsequient nodes, untill a new value is set.

EXTENDING Games::SGF

This is done by inheritance. You use the engine, but override the game specific features related to point, stone, and move.

A Simple template is shown below:

  package MySGFGame;
  require Games::SGF;
  no warnings 'redefine';

  our( @ISA ) = ('Games::SGF');

  sub new {
     my $inv = shift;
     my $class = ref $inv || $inv;
     my $self = $class->SUPER::new(@_);

     # Add Tags
     $self->addTag('TB', $self->T_NONE, $self->V_POINT,
         $self->VF_EMPTY | $self->VF_LIST | $self->VF_OPT_COMPOSE);

     # more tags
     
     # Add Callbacks

     $self->addPointRead(\&pointRead);
     $self->addStoneRead(\&stoneRead);
     $self->addMoveRead(\&moveRead);


     $self->addPointCheck(\&pointCheck);
     $self->addStoneCheck(\&stoneCheck);
     $self->addMoveCheck(\&moveCheck);

     $self->addPointWrite(\&pointWrite);
     $self->addStoneWrite(\&stoneWrite);
     $self->addMoveWrite(\&moveWrite);

     return bless $self, $class; # Makes $self your class
  }
  # define the Callbacks
  # ...
  #
  # define move, point, stone
  # ...

ASSUMPTIONS

All Inherited properties are T_NONE

This holds true for standard FF4 and I believe it would cause a conflict if it was not true.

KNOWN PROBLEMS

Documentation

The Documentation needs to be reviewed for accuracy

Some Errors not handled

The internal methods _get*(branch,node) do not currently check any errors, and the methods using them don't check for errors. When this is fixed a few error handling portions of the _read method can be removed.

ALSO SEE

http://www.red-bean.com/sgf

Games::Goban

Games::Go::SGF

AUTHOR

David Whitcomb, <whitcode at gmail.com>

ACKNOWLEDGEMENTS

Robin Redeker

For pointing out that AW[aa]AW[ab] should be read in and corrected to AW[aa][ab].

BUGS

Please report any bugs or feature requests to bug-games-sgf at rt.cpan.org, or through the web interface at http://rt.cpan.org/NoAuth/ReportBug.html?Queue=Games-SGF. I will be notified, and then you'll automatically be notified of progress on your bug as I make changes.

SUPPORT

You can find documentation for this module with the perldoc command.

    perldoc Games::SGF

You can also look for information at:

ACKNOWLEDGEMENTS

COPYRIGHT & LICENSE

Copyright 2008 David Whitcomb, all rights reserved.

This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.