--- Games-Go-SimpleBoard/SimpleBoard.pm 2008/06/24 23:19:38 1.7 +++ Games-Go-SimpleBoard/SimpleBoard.pm 2008/07/29 10:07:08 1.17 @@ -16,26 +16,30 @@ Marker types for each board position (ORed together): - MARK_TRIANGLE # triangle mark - MARK_SQUARE # square mark - MARK_CIRCLE # circle mark - MARK_CROSS # cross mark - MARK_SMALL_B # small stone, used for scoring or marking - MARK_SMALL_W # small stone, used for scoring or marking - MARK_B # normal black stone - MARK_W # normal whit stone - MARK_GRAYED # in conjunction with MARK_[BW], grays the stone - MARK_LABEL # a text label - MARK_HOSHI # this is a hoshi point (not used much) - MARK_MOVE # this is a regular move - MARK_KO # this is a ko position - MARK_REDRAW # ignored, can be used for your own purposes + MARK_B # normal black stone + MARK_W # normal whit stone + MARK_GRAYED # in conjunction with MARK_[BW], grays the stone + + MARK_SMALL_B # small stone, used for scoring or marking + MARK_SMALL_W # small stone, used for scoring or marking + MARK_SMALL_GRAYED # in conjunction with MARK_SMALL_[BW], grays the stone + + MARK_TRIANGLE # triangle mark + MARK_SQUARE # square mark + MARK_CIRCLE # circle mark + MARK_CROSS # cross mark + + MARK_LABEL # a text label + MARK_HOSHI # this is a hoshi point (not used much) + MARK_MOVE # this is a regular move + MARK_KO # this is a ko position + MARK_REDRAW # ignored, can be used for your own purposes - COLOUR_BLACK # used for $board->{last} - COLOUR_WHITE # to mark the colour of the last move + COLOUR_WHITE # guaranteed to be 0 + COLOUR_BLACK # guaranteed to be 1 - MOVE_HANDICAP # used as "x-coordinate" for handicap moves - MOVE_PASS # can be used as "x-coordinate" for handicap moves + MOVE_HANDICAP # used as "x-coordinate" for handicap moves + MOVE_PASS # can be used as "x-coordinate" for pass moves =head2 METHODS @@ -54,7 +58,7 @@ our @EXPORT = qw( MARK_TRIANGLE MARK_SQUARE MARK_CIRCLE MARK_SMALL_B MARK_SMALL_W MARK_B - MARK_W MARK_GRAYED MARK_MOVE MARK_LABEL MARK_HOSHI MARK_KO MARK_CROSS + MARK_W MARK_GRAYED MARK_SMALL_GRAYED MARK_MOVE MARK_LABEL MARK_HOSHI MARK_KO MARK_CROSS MARK_REDRAW COLOUR_BLACK COLOUR_WHITE MOVE_HANDICAP MOVE_PASS @@ -62,26 +66,30 @@ # marker types for each board position (ORed together) -sub MARK_TRIANGLE (){ 0x0001 } -sub MARK_SQUARE (){ 0x0002 } -sub MARK_CIRCLE (){ 0x0004 } -sub MARK_SMALL_B (){ 0x0008 } # small stone, used for scoring or marking -sub MARK_SMALL_W (){ 0x0010 } # small stone, used for scoring or marking -sub MARK_B (){ 0x0020 } # normal black stone -sub MARK_W (){ 0x0040 } # normal whit stone -sub MARK_GRAYED (){ 0x0080 } # in conjunction with MARK_[BW], grays the stone -sub MARK_LABEL (){ 0x0100 } -sub MARK_HOSHI (){ 0x0200 } # this is a hoshi point (not used much) -sub MARK_MOVE (){ 0x0400 } # this is a regular move -sub MARK_KO (){ 0x0800 } # this is a ko position -sub MARK_CIRCLE (){ 0x1000 } -sub MARK_REDRAW (){ 0x8000 } +sub MARK_TRIANGLE (){ 0x0001 } +sub MARK_SQUARE (){ 0x0002 } +sub MARK_CIRCLE (){ 0x0004 } +sub MARK_CROSS (){ 0x0008 } + +sub MARK_SMALL_B (){ 0x0010 } # small stone, used for scoring or marking +sub MARK_SMALL_W (){ 0x0020 } # small stone, used for scoring or marking +sub MARK_SMALL_GRAYED (){ 0x0040 } + +sub MARK_B (){ 0x0080 } # normal black stone +sub MARK_W (){ 0x0100 } # normal whit stone +sub MARK_GRAYED (){ 0x0200 } # in conjunction with MARK_[BW], grays the stone + +sub MARK_LABEL (){ 0x0400 } +sub MARK_HOSHI (){ 0x0800 } # this is a hoshi point (not used much) +sub MARK_MOVE (){ 0x1000 } # this is a regular move +sub MARK_KO (){ 0x2000 } # this is a ko position +sub MARK_REDRAW (){ 0x8000 } -sub COLOUR_BLACK (){ 0 } -sub COLOUR_WHITE (){ 1 } +sub COLOUR_WHITE (){ 0 } +sub COLOUR_BLACK (){ 1 } -sub MOVE_PASS (){ undef } -sub MOVE_HANDICAP (){ -2 } +sub MOVE_PASS (){ undef } +sub MOVE_HANDICAP (){ -2 } =item my $board = new $size @@ -91,11 +99,9 @@ C<< $board->{max} >> stores the maximum board coordinate (size-1). -C<< $board->{captures}[COLOUR] >> stores the number of captured stones for +C<< $board->{captures}[COLOUR_xxx] >> stores the number of captured stones for the given colour. -C<< $board->{last} >> stores the colour of the last move that was played. - C<< $board->{board} >> stores a two-dimensional array with board contents. =cut @@ -103,17 +109,20 @@ sub new { my $class = shift; my $size = shift; + + unless ($size > 0) { + Carp::croak ("no board size given!"); + } + bless { - max => $size - 1, - size => $size, - board => [map [(0) x $size], 1 .. $size], - captures => [0, 0], # captures - #timer => [], - #score => [], - #last => COLOUR_..., - @_ - }, - $class; + max => $size - 1, + size => $size, + board => [map [(0) x $size], 1 .. $size], + captures => [0, 0], # captures + #timer => [], + #score => [], + @_, + }, $class } # inefficient and primitive, I hear you say? @@ -131,6 +140,7 @@ while (@nodes) { my ($x, $y) = @{pop @nodes}; + unless ($seen{$x,$y}++) { if ($board->[$x][$y] & $mark) { push @found, [$x, $y]; @@ -163,14 +173,14 @@ If C<$set> includes C, the label text must be given in C<$label>. -If C<$set> contains C, then a circle symbol will be placed -at this coordinate. Also, surrounded stones will be removed from the -board and (simple) Kos are detected and marked with square symbols and -C. The circle and square markings are removed with the next -update that uses C, so this flag is suited well for marking, -well, moves. Note that you can make invalid "moves" (such as suicide) and -C will try to cope with it. You can use C to avoid -making illegal moves. +If C<$set> contains C then surrounded stones will be removed +from the board and (simple) Kos are detected and marked with square +symbols and C, after removing other marking symbols. The +markings are also removed with the next next update structure that uses +C, so this flag is suited well for marking, well, moves. Note +that you can make invalid "moves" (such as suicide) and C will +try to cope with it. You can use C to avoid making illegal +moves. For handicap "moves", currently only board sizes 9, 13 and 19 are supported and only handicap values from 2 to 9. The placement follows the @@ -213,6 +223,8 @@ 9 => [qw(0,2 2,0 0,0 2,2 0,1 2,1 1,0 1,2 1,1)], ); +our $mark_symbols = MARK_CIRCLE | MARK_SQUARE | MARK_TRIANGLE | MARK_CROSS | MARK_KO; + sub update { my ($self, $path) = @_; @@ -221,16 +233,13 @@ for (@$path) { my ($x, $y, $clr, $set, $label) = @$_; - my $nodemask = - $_ == $path->[-1] - ? ~0 - : ~(MARK_SQUARE | MARK_TRIANGLE | MARK_CIRCLE | MARK_LABEL | MARK_KO); - if (!defined $x) { + $$_ &= ~$mark_symbols for @{ delete $self->{unmark} || [] }; # pass - $self->{last} = $set & MARK_B ? COLOUR_BLACK : COLOUR_WHITE; } elsif ($x == MOVE_HANDICAP) { + $$_ &= ~$mark_symbols for @{ delete $self->{unmark} || [] }; + # $y = #handicap stones my $c = $HANDICAP_COORD{$self->{size}} or Carp::croak "$self->{size}: illegal board size for handicap"; @@ -243,16 +252,19 @@ } } else { - $board->[$x][$y] = - $board->[$x][$y] - & ~$clr - | $set - & $nodemask; + my $space = \$board->[$x][$y]; + + $$space = $$space & ~$clr | $set; $self->{label}[$x][$y] = $label if $set & MARK_LABEL; if ($set & MARK_MOVE) { - $self->{last} = $set & MARK_B ? COLOUR_BLACK : COLOUR_WHITE; + $$_ &= ~$mark_symbols for @{ $self->{unmark} || [] }; + @{ $self->{unmark} } = $space; + + # remark the space, in case the move was on the same spot as the + # old mark + $$space |= $set; unless (${ $_->[5] ||= \my $hint }) { my ($own, $opp) = @@ -260,7 +272,7 @@ ? (MARK_B, MARK_W) : (MARK_W, MARK_B); - my (@capture, $suicide); + my (@capture, @suicide); push @capture, $self->capture ($opp, $x-1, $y) if $x > 0 && $board->[$x-1][$y] & $opp; push @capture, $self->capture ($opp, $x+1, $y) if $x < $self->{max} && $board->[$x+1][$y] & $opp; @@ -271,15 +283,21 @@ @capture = do { my %seen; grep !$seen{"$_->[0],$_->[1]"}++, @capture }; # remove captured stones - $self->{captures}[$self->{last}] += @capture; - $self->{board}[$_->[0]][$_->[1]] &= ~(MARK_B | MARK_W | MARK_MOVE) + $self->{captures}[$own == MARK_B ? COLOUR_BLACK : COLOUR_WHITE] += @capture; + $self->{board}[$_->[0]][$_->[1]] = 0 for @capture; - $suicide += $self->capture ($own, $x, $y); + push @suicide, $self->capture ($own, $x, $y); - ${ $_->[5] } ||= !(@capture || $suicide); + ${ $_->[5] } ||= !(@capture || @suicide); - if (!$suicide && @capture == 1) { + if (@suicide) { + $self->{board}[$_->[0]][$_->[1]] = 0 + for @suicide; + # count suicides as other sides stones + $self->{captures}[$opp == MARK_B ? COLOUR_BLACK : COLOUR_WHITE] += @suicide; + + } elsif (!@suicide && @capture == 1) { # possible ko. now check liberties on placed stone my $libs; @@ -290,9 +308,12 @@ $libs++ if $y < $self->{max} && !($board->[$x][$y+1] & $opp); if ($libs == 1) { - $board->[$x][$y] = $board->[$x][$y] & ~MARK_CIRCLE | (MARK_KO & $nodemask); + $$space = $$space & ~$mark_symbols | MARK_KO; + ($x, $y) = @{$capture[0]}; - $board->[$x][$y] |= MARK_KO & $nodemask; + $board->[$x][$y] |= MARK_KO; + + push @{ $self->{unmark} }, \$board->[$x][$y]; } } } @@ -315,7 +336,8 @@ my $board = $self->{board}; - return if $board->[$x][$y] & (MARK_B | MARK_W | MARK_KO); + return if $board->[$x][$y] & (MARK_B | MARK_W | MARK_KO) + && !($board->[$x][$y] & MARK_GRAYED); if ($may_suicide) { return 1;