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.13 by elmex, Thu Jul 24 08:50:53 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
161specified in C<$clr>, then setting bits specified in C<$set>. 162specified in C<$clr>, then setting bits specified in C<$set>.
162 163
163If C<$set> includes C<MARK_LABEL>, the label text must be given in 164If C<$set> includes C<MARK_LABEL>, the label text must be given in
164C<$label>. 165C<$label>.
165 166
166If C<$set> contains C<MARK_MOVE>, then a circle symbol will be placed 167If 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 168from the board and (simple) Kos are detected and marked with square
169C<MARK_KO>. The circle and square markings are removed with the next 169symbols and C<MARK_KO>, after removing other marking symbols. The
170markings 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, 171C<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 172that 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 173try to cope with it. You can use C<is_valid_move> to avoid making illegal
173making illegal moves. 174moves.
174 175
175For handicap "moves", currently only board sizes 9, 13 and 19 are 176For handicap "moves", currently only board sizes 9, 13 and 19 are
176supported and only handicap values from 2 to 9. The placement follows the 177supported 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. 178IGS rules, if you want other placements, you have to set it up yourself.
178 179
211 7 => [qw(0,2 2,0 0,0 2,2 0,1 2,1 1,1)], 212 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 )], 213 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)], 214 9 => [qw(0,2 2,0 0,0 2,2 0,1 2,1 1,0 1,2 1,1)],
214); 215);
215 216
217our $mark_symbols = MARK_CIRCLE | MARK_SQUARE | MARK_TRIANGLE | MARK_CROSS | MARK_KO;
218
216sub update { 219sub update {
217 my ($self, $path) = @_; 220 my ($self, $path) = @_;
218 221
219 my $board = $self->{board}; 222 my $board = $self->{board};
220 223
221 for (@$path) { 224 for (@$path) {
222 my ($x, $y, $clr, $set, $label) = @$_; 225 my ($x, $y, $clr, $set, $label) = @$_;
223 226
224 my $nodemask =
225 $_ == $path->[-1]
226 ? ~0
227 : ~(MARK_SQUARE | MARK_TRIANGLE | MARK_CIRCLE | MARK_LABEL | MARK_KO);
228
229 if (!defined $x) { 227 if (!defined $x) {
228 $$_ &= ~$mark_symbols for @{ delete $self->{unmark} || [] };
230 # pass 229 # pass
231 $self->{last} = $set & MARK_B ? COLOUR_BLACK : COLOUR_WHITE;
232 230
233 } elsif ($x == MOVE_HANDICAP) { 231 } elsif ($x == MOVE_HANDICAP) {
232 $$_ &= ~$mark_symbols for @{ delete $self->{unmark} || [] };
233
234 # $y = #handicap stones 234 # $y = #handicap stones
235 my $c = $HANDICAP_COORD{$self->{size}} 235 my $c = $HANDICAP_COORD{$self->{size}}
236 or Carp::croak "$self->{size}: illegal board size for handicap"; 236 or Carp::croak "$self->{size}: illegal board size for handicap";
237 my $h = $HANDICAP_XY{$y} 237 my $h = $HANDICAP_XY{$y}
238 or Carp::croak "$y: illegal number of handicap stones"; 238 or Carp::croak "$y: illegal number of handicap stones";
241 my ($x, $y) = map $c->[$_], split /,/; 241 my ($x, $y) = map $c->[$_], split /,/;
242 $board->[$x][$y] = MARK_B | MARK_MOVE; 242 $board->[$x][$y] = MARK_B | MARK_MOVE;
243 } 243 }
244 244
245 } else { 245 } else {
246 $board->[$x][$y] = 246 my $space = \$board->[$x][$y];
247 $board->[$x][$y] 247
248 & ~$clr 248 $$space = $$space & ~$clr | $set;
249 | $set
250 & $nodemask;
251 249
252 $self->{label}[$x][$y] = $label if $set & MARK_LABEL; 250 $self->{label}[$x][$y] = $label if $set & MARK_LABEL;
253 251
254 if ($set & MARK_MOVE) { 252 if ($set & MARK_MOVE) {
255 $self->{last} = $set & MARK_B ? COLOUR_BLACK : COLOUR_WHITE; 253 $$_ &= ~$mark_symbols for @{ $self->{unmark} || [] };
254 @{ $self->{unmark} } = $space;
256 255
257 unless (${ $_->[5] ||= \my $hint }) { 256 unless (${ $_->[5] ||= \my $hint }) {
258 my ($own, $opp) = 257 my ($own, $opp) =
259 $set & MARK_B 258 $set & MARK_B
260 ? (MARK_B, MARK_W) 259 ? (MARK_B, MARK_W)
269 268
270 # keep only unique coordinates 269 # keep only unique coordinates
271 @capture = do { my %seen; grep !$seen{"$_->[0],$_->[1]"}++, @capture }; 270 @capture = do { my %seen; grep !$seen{"$_->[0],$_->[1]"}++, @capture };
272 271
273 # remove captured stones 272 # remove captured stones
274 $self->{captures}[$self->{last}] += @capture; 273 $self->{captures}[$own == MARK_B ? COLOUR_BLACK : COLOUR_WHITE] += @capture;
275 $self->{board}[$_->[0]][$_->[1]] &= ~(MARK_B | MARK_W | MARK_MOVE) 274 $self->{board}[$_->[0]][$_->[1]] = 0
276 for @capture; 275 for @capture;
277 276
278 $suicide += $self->capture ($own, $x, $y); 277 $suicide += $self->capture ($own, $x, $y);
279 278
280 ${ $_->[5] } ||= !(@capture || $suicide); 279 ${ $_->[5] } ||= !(@capture || $suicide);
281 280
281 if ($suicide) {
282 $self->{board}[$x][$y] = 0
283
282 if (!$suicide && @capture == 1) { 284 } elsif (!$suicide && @capture == 1) {
283 # possible ko. now check liberties on placed stone 285 # possible ko. now check liberties on placed stone
284 286
285 my $libs; 287 my $libs;
286 288
287 $libs++ if $x > 0 && !($board->[$x-1][$y] & $opp); 289 $libs++ if $x > 0 && !($board->[$x-1][$y] & $opp);
288 $libs++ if $x < $self->{max} && !($board->[$x+1][$y] & $opp); 290 $libs++ if $x < $self->{max} && !($board->[$x+1][$y] & $opp);
289 $libs++ if $y > 0 && !($board->[$x][$y-1] & $opp); 291 $libs++ if $y > 0 && !($board->[$x][$y-1] & $opp);
290 $libs++ if $y < $self->{max} && !($board->[$x][$y+1] & $opp); 292 $libs++ if $y < $self->{max} && !($board->[$x][$y+1] & $opp);
291 293
292 if ($libs == 1) { 294 if ($libs == 1) {
293 $board->[$x][$y] = $board->[$x][$y] & ~MARK_CIRCLE | (MARK_KO & $nodemask); 295 $$space = $$space & ~$mark_symbols | MARK_KO;
296
294 ($x, $y) = @{$capture[0]}; 297 ($x, $y) = @{$capture[0]};
295 $board->[$x][$y] |= MARK_KO & $nodemask; 298 $board->[$x][$y] |= MARK_KO;
299
300 push @{ $self->{unmark} }, \$board->[$x][$y];
296 } 301 }
297 } 302 }
298 } 303 }
299 } 304 }
300 } 305 }
313sub is_valid_move { 318sub is_valid_move {
314 my ($self, $colour, $x, $y, $may_suicide) = @_; 319 my ($self, $colour, $x, $y, $may_suicide) = @_;
315 320
316 my $board = $self->{board}; 321 my $board = $self->{board};
317 322
318 return if $board->[$x][$y] & (MARK_B | MARK_W | MARK_KO); 323 return if $board->[$x][$y] & (MARK_B | MARK_W | MARK_KO)
324 && !($board->[$x][$y] & MARK_GRAYED);
319 325
320 if ($may_suicide) { 326 if ($may_suicide) {
321 return 1; 327 return 1;
322 } else { 328 } else {
323 my ($own, $opp) = $colour == COLOUR_BLACK 329 my ($own, $opp) = $colour == COLOUR_BLACK

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines