File Coverage

lib/Games/Checkers/Board.pm
Criterion Covered Total %
statement 15 174 8.6
branch 0 78 0.0
condition 0 26 0.0
subroutine 5 25 20.0
pod 0 20 0.0
total 20 323 6.1


line stmt bran cond sub pod time code
1             # Games::Checkers, Copyright (C) 1996-2012 Mikhael Goikhman, migo@cpan.org
2             #
3             # This program is free software: you can redistribute it and/or modify
4             # it under the terms of the GNU General Public License as published by
5             # the Free Software Foundation, either version 3 of the License, or
6             # (at your option) any later version.
7             #
8             # This program is distributed in the hope that it will be useful,
9             # but WITHOUT ANY WARRANTY; without even the implied warranty of
10             # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11             # GNU General Public License for more details.
12             #
13             # You should have received a copy of the GNU General Public License
14             # along with this program. If not, see .
15              
16 1     1   915 use strict;
  1         2  
  1         37  
17 1     1   4 use warnings;
  1         2  
  1         27  
18              
19             package Games::Checkers::Board;
20              
21 1     1   447 use Games::Checkers::BoardConstants;
  1         2  
  1         6  
22 1     1   630 use Games::Checkers::Constants;
  1         2  
  1         6  
23 1     1   626 use Games::Checkers::IteratorConstants;
  1         3  
  1         6  
24              
25             sub new ($;$) {
26 0     0 0   my $class = shift;
27 0           my $board = shift;
28              
29 0           my $self = {
30             occup_map => 0xFFF00FFF,
31             color_map => 0xFFFF0000,
32             piece_map => 0x00000000,
33             };
34 0           bless $self, $class;
35 0 0         $self->copy($board) if defined $board;
36 0           return $self;
37             }
38              
39             sub get_size ($) {
40 0     0 0   return 8;
41             }
42              
43             sub occup ($$) {
44 0     0 0   my $self = shift;
45 0           my $loc = shift;
46 0 0         return $self->{occup_map} & (1 << $loc) ? 1 : 0;
47             }
48              
49             sub color ($$) {
50 0     0 0   my $self = shift;
51 0           my $loc = shift;
52 0 0         return $self->{color_map} & (1 << $loc) ? Black : White;
53             }
54              
55             sub piece ($$) {
56 0     0 0   my $self = shift;
57 0           my $loc = shift;
58 0 0         return $self->{piece_map} & (1 << $loc) ? King : Pawn;
59             }
60              
61             sub white ($$) {
62 0     0 0   my $self = shift;
63 0           my $loc = shift;
64 0   0       return $self->occup($loc) && $self->color($loc) == White;
65             }
66              
67             sub black ($$) {
68 0     0 0   my $self = shift;
69 0           my $loc = shift;
70 0   0       return $self->occup($loc) && $self->color($loc) == Black;
71             }
72              
73             sub copy ($$) {
74 0     0 0   my $self = shift;
75 0           my $board = shift;
76              
77 0           $self->{$_} = $board->{$_} for qw(occup_map color_map piece_map);
78 0           return $self;
79             }
80              
81             sub clr_all ($) {
82 0     0 0   my $self = shift;
83 0           $self->{occup_map} = 0;
84             }
85              
86             sub clr ($$) {
87 0     0 0   my $self = shift;
88 0           my $loc = shift;
89 0           $self->{occup_map} &= ~(1 << $loc);
90             }
91              
92             sub set ($$$$) {
93 0     0 0   my $self = shift;
94 0           my ($loc, $color, $type) = @_;
95 0           $self->{occup_map} |= (1 << $loc);
96 0           ($self->{color_map} &= ~(1 << $loc)) |= ((1 << $loc) * $color);
97 0           ($self->{piece_map} &= ~(1 << $loc)) |= ((1 << $loc) * $type);
98             }
99              
100              
101             sub get_cost ($$) {
102 0     0 0   my $self = shift;
103 0           my $turn = shift;
104              
105             # Count white & black figures
106 0           my ($white_pawns, $white_kings, $black_pawns, $black_kings) = (0) x 4;
107              
108 0           my $whites_iterator = new Games::Checkers::FigureIterator($self, White);
109 0           while ($whites_iterator->left) {
110 0           my $loc = $whites_iterator->next;
111 0 0         $self->piece($loc) == Pawn ? $white_pawns++ : $white_kings++;
112             }
113              
114 0           my $blacks_iterator = new Games::Checkers::FigureIterator($self, Black);
115 0           while ($blacks_iterator->left) {
116 0           my $loc = $blacks_iterator->next;
117 0 0         $self->piece($loc) == Pawn ? $black_pawns++ : $black_kings++;
118             }
119              
120 0 0         return -1e8 if $white_pawns + $white_kings == 0;
121 0 0         return +1e8 if $black_pawns + $black_kings == 0;
122              
123             return
124 0 0         + $white_pawns * 100
125             + $white_kings * 600
126             - $black_pawns * 100
127             - $black_kings * 600
128             + ($turn == White ? 1 : -1);
129             }
130              
131             sub transform ($) {
132 0     0 0   my $self = shift;
133 0           my $move = shift;
134              
135 0           my $src = $move->source;
136 0           my $dst = $move->destin(0);
137 0           my $beat = $move->is_beat;
138 0           my $color = $self->color($src);
139 0           my $piece = $self->piece($src);
140 0           for (my $n = 0; $dst != NL; $src = $dst, $dst = $move->destin(++$n)) {
141 0           $self->clr($src);
142 0           $self->set($dst, $color, $piece);
143 0 0         $self->clr($self->figure_between($src, $dst)) if $beat;
144             # convert to king if needed
145 0 0         if (convert_type->[$color][$piece] & (1 << $dst)) {
146 0           $self->{piece_map} ^= (1 << $dst);
147 0           $piece ^= 1;
148             }
149             }
150             }
151              
152             sub can_piece_step ($$;$) {
153 0     0 0   my $self = shift;
154 0           my $loc = shift;
155 0           my $locd = shift;
156 0 0         $locd = NL unless defined $locd;
157              
158 0 0         if (!$self->occup($loc)) {
159 0           warn("Internal error in can_piece_step, loc=$loc is not occupied");
160 0           &DIE_WITH_STACK();
161 0           return No;
162             }
163 0           my $color = $self->color($loc);
164 0 0         my $step_dst = $self->piece($loc) == Pawn
165             ? pawn_step_iterator
166             : king_step_iterator;
167 0           $step_dst->init($loc, $color);
168 0           while ($step_dst->left) {
169 0           my $loc2 = $step_dst->next;
170 0 0 0       next if $locd != NL && $locd != $loc2;
171 0 0         next if $self->figure_between($loc, $loc2) != NL;
172 0 0         return Yes unless $self->occup($loc2);
173             }
174 0           return No;
175             }
176              
177             sub can_piece_beat ($$;$) {
178 0     0 0   my $self = shift;
179 0           my $loc = shift;
180 0           my $locd = shift;
181 0 0         $locd = NL unless defined $locd;
182              
183 0 0         if (!$self->occup($loc)) {
184 0           warn("Internal error in can_piece_beat, loc=$loc is not occupied");
185 0           &DIE_WITH_STACK();
186 0           return No;
187             }
188 0           my $color = $self->color($loc);
189 0 0         my $beat_dst = $self->piece($loc) == Pawn
190             ? pawn_beat_iterator
191             : king_beat_iterator;
192 0           $beat_dst->init($loc, $color);
193 0           while ($beat_dst->left) {
194 0           my $loc2 = $beat_dst->next;
195 0 0 0       next if $locd != NL && $locd != $loc2;
196 0           my $loc1 = $self->figure_between($loc, $loc2);
197 0 0 0       next if $loc1 == NL || $loc1 == ML;
198 0 0 0       return Yes unless $self->occup($loc2) ||
      0        
199             !$self->occup($loc1) || $self->color($loc1) == $color;
200             }
201 0           return No;
202             }
203              
204             sub can_color_step ($$) {
205 0     0 0   my $self = shift;
206 0           my $color = shift;
207 0           my $iterator = Games::Checkers::FigureIterator->new($self, $color);
208 0           while ($iterator->left) {
209 0 0         return Yes if $self->can_piece_step($iterator->next);
210             }
211 0           return No;
212             }
213              
214             sub can_color_beat ($$) {
215 0     0 0   my $self = shift;
216 0           my $color = shift;
217 0           my $iterator = Games::Checkers::FigureIterator->new($self, $color);
218 0           while ($iterator->left) {
219 0 0         return Yes if $self->can_piece_beat($iterator->next);
220             }
221 0           return No;
222             }
223              
224             sub can_color_move ($$) {
225 0     0 0   my $self = shift;
226 0           my $color = shift;
227 0   0       return $self->can_color_beat($color) || $self->can_color_step($color);
228             }
229              
230             sub figure_between ($$$) {
231 0     0 0   my $self = shift;
232 0           my $src = shift;
233 0           my $dst = shift;
234              
235 0           for (my $drc = 0; $drc < DIRECTION_NUM; $drc++) {
236 0           my $figures = 0;
237 0           my $figure = NL;
238 0           for (my $loc = loc_directions->[$src][$drc]; $loc != NL; $loc = loc_directions->[$loc][$drc]) {
239 0 0         if ($loc == $dst) {
240 0 0         return $figures > 1 ? ML : $figures == 1 ? $figure : NL;
    0          
241             }
242 0 0         if ($self->occup($loc)) {
243 0           $figure = $loc;
244 0           $figures++;
245             }
246             }
247             }
248 0           return NL;
249             }
250              
251             #
252             # +-------------------------------+
253             # 8 |###| @ |###| @ |###| @ |###| @ |
254             # |---+---+---+---+---+---+---+---|
255             # 7 | @ |###| @ |###| @ |###| @ |###|
256             # |---+---+---+---+---+---+---+---|
257             # 6 |###| @ |###| @ |###| @ |###| @ |
258             # |---+---+---+---+---+---+---+---|
259             # 5 | |###| |###| |###| |###|
260             # |---+---+---+---+---+---+---+---|
261             # 4 |###| |###| |###| |###| |
262             # |---+---+---+---+---+---+---+---|
263             # 3 | O |###| O |###| O |###| O |###|
264             # |---+---+---+---+---+---+---+---|
265             # 2 |###| O |###| O |###| O |###| O |
266             # |---+---+---+---+---+---+---+---|
267             # 1 | O |###| O |###| O |###| O |###|
268             # +-------------------------------+
269             # a b c d e f g h
270             #
271              
272             sub dump ($;$) {
273 0     0 0   my $self = shift;
274 0   0       my $prefix = shift || "";
275 0 0         $prefix = " " x $prefix if $prefix =~ /^\d+$/;
276              
277 0           my $char_sets = [
278             {
279             tlc => "+",
280             trc => "+",
281             blc => "+",
282             brc => "+",
283             vcl => "|",
284             vll => "|",
285             vrl => "|",
286             hcl => "-",
287             htl => "-",
288             hbl => "-",
289             ccl => "+",
290             bcs => "",
291             bce => "",
292             bcf => " ",
293             wcs => "",
294             wce => "",
295             wcf => "#",
296             },
297             {
298             tlc => "\e)0\016l\017",
299             trc => "\016k\017",
300             blc => "\016m\017",
301             brc => "\016j\017",
302             vcl => "\016x\017",
303             vll => "\016t\017",
304             vrl => "\016u\017",
305             hcl => "\016q\017",
306             htl => "\016w\017",
307             hbl => "\016v\017",
308             ccl => "\016n\017",
309             bcs => "\e[0;7m",
310             bce => "\e[0m",
311             bcs => "",
312             bce => "",
313             bcf => " ",
314             wcs => "",
315             wce => "",
316             wcs => "\e[0;7m",
317             wce => "\e[0m",
318             wcf => " ",
319             },
320             ];
321 0 0         my %ch = %{$char_sets->[$ENV{DUMB_CHARS} ? 0 : 1]};
  0            
322              
323 0           my $size = $self->get_size;
324 0           my $size_1 = $size - 1;
325 0           my $size_2 = $size / 2;
326              
327 0           my $str = "";
328 0           $str .= "\n";
329 0           $str .= " " . $ch{tlc} . ("$ch{hcl}$ch{hcl}$ch{hcl}$ch{htl}" x $size_1) . "$ch{hcl}$ch{hcl}$ch{hcl}$ch{trc}\n";
330 0           for (my $i = 0; $i < $size; $i++) {
331 0           $str .= ($size - $i) . " $ch{vcl}";
332 0           for (my $j = 0; $j < $size; $j++) {
333 0           my $is_used = ($i + $j) % 2;
334 0 0         if (($i + $j) % 2) {
335 0           my $loc = ($size_1 - $i) * $size_2 + int($j / 2);
336 0           my $ch0 = $ch{bcf};
337 0           my $is_king = $self->piece($loc) == King;
338 0 0         $ch0 = $self->white($loc) ? $is_king ? "8" : "O" : $is_king ? "&" : "@"
    0          
    0          
    0          
339             if $self->occup($loc);
340 0 0         $ch0 = $self->white($loc) ? "\e[1m$ch0\e[0m" : "\e[4m$ch0\e[0m"
    0          
341             if $self->occup($loc);
342 0           $str .= "$ch{bcs}$ch{bcf}$ch0$ch{bcs}$ch{bcf}$ch{bce}";
343             } else {
344 0           $str .= "$ch{wcs}$ch{wcf}$ch{wcf}$ch{wcf}$ch{wce}";
345             }
346 0           $str .= $ch{vcl};
347             }
348 0           $str .= "\n";
349 0 0         $str .= " " . $ch{vll} . ("$ch{hcl}$ch{hcl}$ch{hcl}$ch{ccl}" x $size_1) . "$ch{hcl}$ch{hcl}$ch{hcl}$ch{vrl}\n" if $i != $size_1;
350             }
351 0           $str .= " " . $ch{blc} . ("$ch{hcl}$ch{hcl}$ch{hcl}$ch{hbl}" x $size_1) . "$ch{hcl}$ch{hcl}$ch{hcl}$ch{brc}\n";
352 0           $str .= " a b c d e f g h \n";
353 0           $str .= "\n";
354              
355 0           $str =~ s/^/$prefix/gm;
356              
357 0           return $str;
358             }
359              
360             1;