package Games::SGF::Util;
use warnings;
use strict;
use Games::SGF;
no warnings 'redefine';
=head1 NAME
Games::SGF::Util - Utility pack for Games::SGF objects
=head1 VERSION
Version 0.993
=cut
our $VERSION = 0.993;
=head1 SYNOPSIS
Quick summary of what the module does.
Perhaps a little code snippet.
use Games::SGF::Util;
my $util = new Games::SGF::Util($sgf);
$util->filter( "C", undef ); # removes all comments from SGF
=head1 DISCRIPTION
This is a collection of useful methods for manipulating a Games::SGF object.
All Util methods in this module will not call any game movement methods. This
means in order to work with files with multiple games you must move to the
game of choice then pass it into a util object.
=head1 METHODS
=head2 new
$util = new Games::SGF::Util($sgf);
This initializes a new Games::SGF::Util object. Will return C<undef> if C<$sgf>
is no supplied.
=cut
sub new {
my $inv = shift;
my $class = ref $inv || $inv;
my $sgf = shift;
if($sgf) {
# $sgf = $sgf->clone(); # So we are not working with the actual sgf file
} else {
return undef;
}
return bless \$sgf, $class;
}
=head2 touch
$util->touch(\&sub);
This will call C<&sub> for every node in $sgf. C<&sub> will be passed the
C<$sgf> object. any subroutines which manipulate the C<$sgf> tree will lead
to undefined behavior. The safe methods to use are:
=over
=item L<Games::SGF/property>
=item L<Games::SGF/getProperty>
=item L<Games::SGF/setProperty>
=item L<Games::SGF/isCompose>
=item L<Games::SGF/isStone>
=item L<Games::SGF/isMove>
=item L<Games::SGF/isPoint>
=item L<Games::SGF/compose>
=item L<Games::SGF/stone>
=item L<Games::SGF/move>
=item L<Games::SGF/point>
=item L<Games::SGF/err>
=back
=cut
sub touch {
my $self = shift;
my $callback = shift;
my $sgf = $$self;
my( @branches ) = (-1); # Stores the branch stack
$sgf->gotoRoot;
{
my $last = pop @branches;
&$callback($sgf) if $last == -1; # callback on current node
if( $last < $sgf->branches and $sgf->gotoBranch(++$last)) {
push @branches, $last,-1;
} elsif(@branches > 0 ) {
$sgf->prev;
pop @branches;
} else {
last;
}
redo;
}
}
=head2 filter
$util->fiter( $tag, \&sub);
Will call C<&sub> for every $tag which is in C<$sgf>. C<&sub> will be passed
the tag value. The value then be reset to the return of C<&sub>. If the return
is "" the tag will be unset.
If the tag has a value list each value will be sent to $callback.
If the $callback returns undef it will not be set.
Example:
# removes all comments that don't match m/Keep/
$util->filter( "C", sub { return $_[0] =~ m/Keep/ ? $_[0] : ""; );
=cut
sub filter {
my $self = shift;
my $tag = shift;
my $callback = shift;
return $self->touch(
sub {
my $sgf = shift;
my $values = $sgf->property($tag);
my @set;
if( $values ) {
if( $callback ) {
foreach( @$values ) {
my $ret = &$callback($_);
if( defined $ret ) {
push @set, $ret
}
}
} # else unset tag
$sgf->setProperty($tag,@set);
}
}
);
}
=head2 gameInfo
my(@games) = $util->gameInfo;
foreach my $game (@games) {
print "New Game\n";
foreach my $tag (keys %$game) {
print "\t$tag -> $game->{$tag}\n";
}
}
Will return the game-info tags for all games represented in the current
game tree. The return order is the closest to the root, and then the closest
to the main line branch.
UNWRITTEN
=cut
sub gameInfo {
my $self = shift;
my $isRec = shift; # set if a recursive call
my $sgf = $$self;
my( @games );
# if this is first run
$sgf->gotoRoot unless $isRec;
# touch all nodes in this branch
{
# check for games and add to @games
my(@tags) = $sgf->property;
my $game = {};
foreach my $t (@tags) {
if( $sgf->getTagType($t) & $sgf->T_GAME_INFO ) {
$game->{$t} = $sgf->getProperty($t);
}
}
if( keys %$game ) {
$games[@games] = $game;
}
redo if $sgf->next;
}
# touch all variations
for( my $i = 0; $i < $sgf->branches; $i++ ) {
#add game info of branch onto our list
$sgf->gotoBranch($i);
push @games, $self->gameInfo( 1 );
$sgf->gotoParent;
}
return @games;
}
=head2 sgf
$sgf = $util->sgf;
$sgf = $util->sgf($sgf)
This returns a clone of the C<$sgf> object associated with C<$util>, or sets the
C<$sgf> object to a clone of object supplied.
=cut
sub sgf {
my $self = shift;
my $sgf = shift;
if($sgf) {
$$self = $sgf;#->clone();
return $sgf;
}
$sgf = $$self;
return $sgf;#->clone();
}
1;
__END__
=head1 ALSO SEE
L<Games::SGF>
=head1 AUTHOR
David Whitcomb, C<< <whitcode at gmail.com> >>
=head1 BUGS
Please report any bugs or feature requests to C<bug-games-sgf at rt.cpan.org>, or through
the web interface at L<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.
=head1 SUPPORT
You can find documentation for this module with the perldoc command.
perldoc Games::SGF::Util
You can also look for information at:
=over 4
=item * RT: CPAN's request tracker
L<http://rt.cpan.org/NoAuth/Bugs.html?Dist=Games-SGF>
=item * AnnoCPAN: Annotated CPAN documentation
L<http://annocpan.org/dist/Games-SGF>
=item * CPAN Ratings
L<http://cpanratings.perl.org/d/Games-SGF>
=item * Search CPAN
L<http://search.cpan.org/dist/Games-SGF>
=back
=head1 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.