ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/Games-Go-SimpleBoard/SimpleBoard.pm
(Generate patch)

Comparing Games-Go-SimpleBoard/SimpleBoard.pm (file contents):
Revision 1.6 by root, Tue Jun 24 23:19:24 2008 UTC vs.
Revision 1.14 by elmex, Thu Jul 24 08:58:02 2008 UTC

29 MARK_HOSHI # this is a hoshi point (not used much) 29 MARK_HOSHI # this is a hoshi point (not used much)
30 MARK_MOVE # this is a regular move 30 MARK_MOVE # this is a regular move
31 MARK_KO # this is a ko position 31 MARK_KO # this is a ko position
32 MARK_REDRAW # ignored, can be used for your own purposes 32 MARK_REDRAW # ignored, can be used for your own purposes
33 33
34 COLOUR_BLACK # used for $board->{last} 34 COLOUR_WHITE # guarenteed to be 0
35 COLOUR_WHITE # to mark the colour of the last move 35 COLOUR_BLACK # guarenteed to be 1
36 36
37 MOVE_HANDICAP # used as "x-coordinate" for handicap moves 37 MOVE_HANDICAP # used as "x-coordinate" for handicap moves
38 MOVE_PASS # can be used as "x-coordinate" for handicap moves 38 MOVE_PASS # can be used as "x-coordinate" for pass moves
39 39
40=head2 METHODS 40=head2 METHODS
41 41
42=over 4 42=over 4
43 43
52 52
53our $VERSION = '1.0'; 53our $VERSION = '1.0';
54 54
55our @EXPORT = qw( 55our @EXPORT = qw(
56 MARK_TRIANGLE MARK_SQUARE MARK_CIRCLE MARK_SMALL_B MARK_SMALL_W MARK_B 56 MARK_TRIANGLE MARK_SQUARE MARK_CIRCLE MARK_SMALL_B MARK_SMALL_W MARK_B
57 MARK_W MARK_GRAYED MARK_MOVE MARK_LABEL MARK_HOSHI MARK_KO 57 MARK_W MARK_GRAYED MARK_MOVE MARK_LABEL MARK_HOSHI MARK_KO MARK_CROSS
58 MARK_REDRAW 58 MARK_REDRAW
59 COLOUR_BLACK COLOUR_WHITE 59 COLOUR_BLACK COLOUR_WHITE
60 MOVE_HANDICAP MOVE_PASS 60 MOVE_HANDICAP MOVE_PASS
61); 61);
62 62
72sub MARK_GRAYED (){ 0x0080 } # in conjunction with MARK_[BW], grays the stone 72sub MARK_GRAYED (){ 0x0080 } # in conjunction with MARK_[BW], grays the stone
73sub MARK_LABEL (){ 0x0100 } 73sub MARK_LABEL (){ 0x0100 }
74sub MARK_HOSHI (){ 0x0200 } # this is a hoshi point (not used much) 74sub MARK_HOSHI (){ 0x0200 } # this is a hoshi point (not used much)
75sub MARK_MOVE (){ 0x0400 } # this is a regular move 75sub MARK_MOVE (){ 0x0400 } # this is a regular move
76sub MARK_KO (){ 0x0800 } # this is a ko position 76sub MARK_KO (){ 0x0800 } # this is a ko position
77sub MARK_CIRCLE (){ 0x1000 } 77sub MARK_CROSS (){ 0x1000 }
78sub MARK_REDRAW (){ 0x8000 } 78sub MARK_REDRAW (){ 0x8000 }
79 79
80sub COLOUR_BLACK (){ 0 }
81sub COLOUR_WHITE (){ 1 } 80sub COLOUR_WHITE (){ 0 }
81sub COLOUR_BLACK (){ 1 }
82 82
83sub MOVE_PASS (){ undef } 83sub MOVE_PASS (){ undef }
84sub MOVE_HANDICAP (){ -2 } 84sub MOVE_HANDICAP (){ -2 }
85 85
86=item my $board = new $size 86=item my $board = new $size
89 89
90C<< $board->{size} >> stores the board size. 90C<< $board->{size} >> stores the board size.
91 91
92C<< $board->{max} >> stores the maximum board coordinate (size-1). 92C<< $board->{max} >> stores the maximum board coordinate (size-1).
93 93
94C<< $board->{captures}[COLOUR] >> stores the number of captured stones for 94C<< $board->{captures}[COLOUR_xxx] >> stores the number of captured stones for
95the given colour. 95the given colour.
96
97C<< $board->{last} >> stores the colour of the last move that was played.
98 96
99C<< $board->{board} >> stores a two-dimensional array with board contents. 97C<< $board->{board} >> stores a two-dimensional array with board contents.
100 98
101=cut 99=cut
102 100
103sub new { 101sub new {
104 my $class = shift; 102 my $class = shift;
105 my $size = shift; 103 my $size = shift;
104
105 unless ($size > 0) {
106 Carp::croak ("no board size given!");
107 }
108
106 bless { 109 bless {
107 max => $size - 1, 110 max => $size - 1,
108 size => $size, 111 size => $size,
109 board => [map [(0) x $size], 1 .. $size], 112 board => [map [(0) x $size], 1 .. $size],
110 captures => [0, 0], # captures 113 captures => [0, 0], # captures
111 #timer => [], 114 #timer => [],
112 #score => [], 115 #score => [],
113 #last => COLOUR_...,
114 @_
115 }, 116 @_,
116 $class; 117 }, $class
117} 118}
118 119
119# inefficient and primitive, I hear you say? 120# inefficient and primitive, I hear you say?
120# well... you are right :) 121# well... you are right :)
121# use an extremely dumb floodfill algorithm to get rid of captured stones 122# use an extremely dumb floodfill algorithm to get rid of captured stones
129 130
130 my $max = $self->{max}; 131 my $max = $self->{max};
131 132
132 while (@nodes) { 133 while (@nodes) {
133 my ($x, $y) = @{pop @nodes}; 134 my ($x, $y) = @{pop @nodes};
135
134 unless ($seen{$x,$y}++) { 136 unless ($seen{$x,$y}++) {
135 if ($board->[$x][$y] & $mark) { 137 if ($board->[$x][$y] & $mark) {
136 push @found, [$x, $y]; 138 push @found, [$x, $y];
137 139
138 push @nodes, [$x-1, $y] unless $seen{$x-1, $y} || $x <= 0; 140 push @nodes, [$x-1, $y] unless $seen{$x-1, $y} || $x <= 0;
161specified in C<$clr>, then setting bits specified in C<$set>. 163specified in C<$clr>, then setting bits specified in C<$set>.
162 164
163If C<$set> includes C<MARK_LABEL>, the label text must be given in 165If C<$set> includes C<MARK_LABEL>, the label text must be given in
164C<$label>. 166C<$label>.
165 167
166If C<$set> contains C<MARK_MOVE>, then a circle symbol will be placed 168If C<$set> contains C<MARK_MOVE> then surrounded stones will be removed
167at this coordinate. Also, surrounded stones will be removed from the
168board and (simple) Kos are detected and marked with square symbols and 169from the board and (simple) Kos are detected and marked with square
169C<MARK_KO>. The circle and square markings are removed with the next 170symbols and C<MARK_KO>, after removing other marking symbols. The
171markings are also removed with the next next update structure that uses
170update that uses C<MARK_MOVE>, so this flag is suited well for marking, 172C<MARK_MOVE>, so this flag is suited well for marking, well, moves. Note
171well, moves. Note that you can make invalid "moves" (such as suicide) and 173that you can make invalid "moves" (such as suicide) and C<update> will
172C<update> will try to cope with it. You can use C<is_valid_move> to avoid 174try to cope with it. You can use C<is_valid_move> to avoid making illegal
173making illegal moves. 175moves.
174 176
175For handicap "moves", currently only board sizes 9, 13 and 19 are 177For handicap "moves", currently only board sizes 9, 13 and 19 are
176supported and only handicap values from 2 to 9. The placement follows the 178supported and only handicap values from 2 to 9. The placement follows the
177IGS rules, if you want other placements, you have to set it up yourself. 179IGS rules, if you want other placements, you have to set it up yourself.
178 180
211 7 => [qw(0,2 2,0 0,0 2,2 0,1 2,1 1,1)], 213 7 => [qw(0,2 2,0 0,0 2,2 0,1 2,1 1,1)],
212 8 => [qw(0,2 2,0 0,0 2,2 0,1 2,1 1,0 1,2 )], 214 8 => [qw(0,2 2,0 0,0 2,2 0,1 2,1 1,0 1,2 )],
213 9 => [qw(0,2 2,0 0,0 2,2 0,1 2,1 1,0 1,2 1,1)], 215 9 => [qw(0,2 2,0 0,0 2,2 0,1 2,1 1,0 1,2 1,1)],
214); 216);
215 217
218our $mark_symbols = MARK_CIRCLE | MARK_SQUARE | MARK_TRIANGLE | MARK_CROSS | MARK_KO;
219
216sub update { 220sub update {
217 my ($self, $path) = @_; 221 my ($self, $path) = @_;
218 222
219 my $board = $self->{board}; 223 my $board = $self->{board};
220 224
221 for (@$path) { 225 for (@$path) {
222 my ($x, $y, $clr, $set, $label) = @$_; 226 my ($x, $y, $clr, $set, $label) = @$_;
223 227
224 my $nodemask =
225 $_ == $path->[-1]
226 ? ~0
227 : ~(MARK_SQUARE | MARK_TRIANGLE | MARK_CIRCLE | MARK_LABEL | MARK_KO);
228
229 if (!defined $x) { 228 if (!defined $x) {
229 $$_ &= ~$mark_symbols for @{ delete $self->{unmark} || [] };
230 # pass 230 # pass
231 $self->{last} = $set & MARK_B ? COLOUR_BLACK : COLOUR_WHITE;
232 231
233 } elsif ($x == MOVE_HANDICAP) { 232 } elsif ($x == MOVE_HANDICAP) {
233 $$_ &= ~$mark_symbols for @{ delete $self->{unmark} || [] };
234
234 # $y = #handicap stones 235 # $y = #handicap stones
235 my $c = $HANDICAP_COORD{$self->{size}} 236 my $c = $HANDICAP_COORD{$self->{size}}
236 or Carp::croak "$self->{size}: illegal board size for handicap"; 237 or Carp::croak "$self->{size}: illegal board size for handicap";
237 my $h = $HANDICAP_XY{$y} 238 my $h = $HANDICAP_XY{$y}
238 or Carp::croak "$y: illegal number of handicap stones"; 239 or Carp::croak "$y: illegal number of handicap stones";
241 my ($x, $y) = map $c->[$_], split /,/; 242 my ($x, $y) = map $c->[$_], split /,/;
242 $board->[$x][$y] = MARK_B | MARK_MOVE; 243 $board->[$x][$y] = MARK_B | MARK_MOVE;
243 } 244 }
244 245
245 } else { 246 } else {
246 $board->[$x][$y] = 247 my $space = \$board->[$x][$y];
247 $board->[$x][$y] 248
248 & ~$clr 249 $$space = $$space & ~$clr | $set;
249 | $set
250 & $nodemask;
251 250
252 $self->{label}[$x][$y] = $label if $set & MARK_LABEL; 251 $self->{label}[$x][$y] = $label if $set & MARK_LABEL;
253 252
254 if ($set & MARK_MOVE) { 253 if ($set & MARK_MOVE) {
255 $self->{last} = $set & MARK_B ? COLOUR_BLACK : COLOUR_WHITE; 254 $$_ &= ~$mark_symbols for @{ $self->{unmark} || [] };
255 @{ $self->{unmark} } = $space;
256 256
257 unless (${ $_->[5] ||= \my $hint }) { 257 unless (${ $_->[5] ||= \my $hint }) {
258 my ($own, $opp) = 258 my ($own, $opp) =
259 $set & MARK_B 259 $set & MARK_B
260 ? (MARK_B, MARK_W) 260 ? (MARK_B, MARK_W)
261 : (MARK_W, MARK_B); 261 : (MARK_W, MARK_B);
262 262
263 my (@capture, $suicide); 263 my (@capture, @suicide);
264 264
265 push @capture, $self->capture ($opp, $x-1, $y) if $x > 0 && $board->[$x-1][$y] & $opp; 265 push @capture, $self->capture ($opp, $x-1, $y) if $x > 0 && $board->[$x-1][$y] & $opp;
266 push @capture, $self->capture ($opp, $x+1, $y) if $x < $self->{max} && $board->[$x+1][$y] & $opp; 266 push @capture, $self->capture ($opp, $x+1, $y) if $x < $self->{max} && $board->[$x+1][$y] & $opp;
267 push @capture, $self->capture ($opp, $x, $y-1) if $y > 0 && $board->[$x][$y-1] & $opp; 267 push @capture, $self->capture ($opp, $x, $y-1) if $y > 0 && $board->[$x][$y-1] & $opp;
268 push @capture, $self->capture ($opp, $x, $y+1) if $y < $self->{max} && $board->[$x][$y+1] & $opp; 268 push @capture, $self->capture ($opp, $x, $y+1) if $y < $self->{max} && $board->[$x][$y+1] & $opp;
269 269
270 # keep only unique coordinates 270 # keep only unique coordinates
271 @capture = do { my %seen; grep !$seen{"$_->[0],$_->[1]"}++, @capture }; 271 @capture = do { my %seen; grep !$seen{"$_->[0],$_->[1]"}++, @capture };
272 272
273 # remove captured stones 273 # remove captured stones
274 $self->{captures}[$self->{last}] += @capture; 274 $self->{captures}[$own == MARK_B ? COLOUR_BLACK : COLOUR_WHITE] += @capture;
275 $self->{board}[$_->[0]][$_->[1]] &= ~(MARK_B | MARK_W | MARK_MOVE) 275 $self->{board}[$_->[0]][$_->[1]] = 0
276 for @capture; 276 for @capture;
277 277
278 $suicide += $self->capture ($own, $x, $y); 278 push @suicide, $self->capture ($own, $x, $y);
279 279
280 ${ $_->[5] } ||= !(@capture || $suicide); 280 ${ $_->[5] } ||= !(@capture || @suicide);
281 281
282 if (@suicide) {
283 $self->{board}[$_->[0]][$_->[1]] = 0
284 for @suicide;
285
282 if (!$suicide && @capture == 1) { 286 } elsif (!@suicide && @capture == 1) {
283 # possible ko. now check liberties on placed stone 287 # possible ko. now check liberties on placed stone
284 288
285 my $libs; 289 my $libs;
286 290
287 $libs++ if $x > 0 && !($board->[$x-1][$y] & $opp); 291 $libs++ if $x > 0 && !($board->[$x-1][$y] & $opp);
288 $libs++ if $x < $self->{max} && !($board->[$x+1][$y] & $opp); 292 $libs++ if $x < $self->{max} && !($board->[$x+1][$y] & $opp);
289 $libs++ if $y > 0 && !($board->[$x][$y-1] & $opp); 293 $libs++ if $y > 0 && !($board->[$x][$y-1] & $opp);
290 $libs++ if $y < $self->{max} && !($board->[$x][$y+1] & $opp); 294 $libs++ if $y < $self->{max} && !($board->[$x][$y+1] & $opp);
291 295
292 if ($libs == 1) { 296 if ($libs == 1) {
293 $board->[$x][$y] = $board->[$x][$y] & ~MARK_CIRCLE | (MARK_KO & $nodemask); 297 $$space = $$space & ~$mark_symbols | MARK_KO;
298
294 ($x, $y) = @{$capture[0]}; 299 ($x, $y) = @{$capture[0]};
295 $board->[$x][$y] |= MARK_KO & $nodemask; 300 $board->[$x][$y] |= MARK_KO;
301
302 push @{ $self->{unmark} }, \$board->[$x][$y];
296 } 303 }
297 } 304 }
298 } 305 }
299 } 306 }
300 } 307 }
313sub is_valid_move { 320sub is_valid_move {
314 my ($self, $colour, $x, $y, $may_suicide) = @_; 321 my ($self, $colour, $x, $y, $may_suicide) = @_;
315 322
316 my $board = $self->{board}; 323 my $board = $self->{board};
317 324
318 return if $board->[$x][$y] & (MARK_B | MARK_W | MARK_KO); 325 return if $board->[$x][$y] & (MARK_B | MARK_W | MARK_KO)
326 && !($board->[$x][$y] & MARK_GRAYED);
319 327
320 if ($may_suicide) { 328 if ($may_suicide) {
321 return 1; 329 return 1;
322 } else { 330 } else {
323 my ($own, $opp) = $colour == COLOUR_BLACK 331 my ($own, $opp) = $colour == COLOUR_BLACK

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines