File Coverage

blib/lib/Chess/Piece.pm
Criterion Covered Total %
statement 117 118 99.1
branch 40 68 58.8
condition 5 15 33.3
subroutine 24 25 96.0
pod 15 16 93.7
total 201 242 83.0


line stmt bran cond sub pod time code
1             =head1 NAME
2              
3             Chess::Piece - a base class for chess pieces
4              
5             =head1 SYNOPSIS
6              
7             $piece = Chess::Piece->new("e2", "white", "White King's pawn");
8             $piece->set_current_square("e4");
9             $e4 = $piece->get_current_square();
10             $piece->set_description("My Piece");
11             $description = $piece->get_description();
12             $color = $piece->get_color();
13             if (!$piece->moved()) {
14             # do something with the unmoved piece
15             }
16             $piece->set_moved(1);
17             if ($piece->threatened()) {
18             # do something with the threatened piece
19             }
20             $piece->set_threatened(1);
21             if ($piece->captured()) {
22             # do something with the captured piece
23             }
24             $piece->set_captured(1);
25              
26             =head1 DESCRIPTION
27              
28             The Chess module provides a framework for writing chess programs with Perl.
29              
30             This class represents the parent class for all Chess pieces, and contains
31             accessors and mutators for all the common properties of chess pieces.
32             The following is an exhaustive list of the properties of a Chess::Piece:
33              
34             * initial square (read-only, specified at construction)
35             * color (read-only, specified at construction)
36             * current square
37             * description
38             * a flag indicating whether or not the piece has moved
39             * a flag indicating whether or not the piece is threatened
40             * a flag indicating whether or not the piece was captured
41              
42             See L for details of the methods which manipulate and return these
43             properties.
44              
45             =head1 METHODS
46              
47             =head2 Construction
48              
49             =over 4
50              
51             =item new()
52              
53             Constructs a new Chess::Piece. Requires a two scalar arguments containing the
54             initial square this piece is on and the color of the piece. If the program
55             will use colors other than 'black' and 'white', then subclasses of
56             Chess::Piece will need to override the L method to take these
57             colors into account. Optionally takes a third argument containing a text
58             description of the piece. Returns a blessed Chess::Piece object reference
59             that can be used to call any of the methods listed in L.
60             The square is not tested for validity, so the program must validate the
61             square before calling new().
62              
63             $piece = Chess::Piece->new("e2", "white");
64             $piece = Chess::Piece->new("e2", "white", "White King's pawn");
65              
66             See also L to construct a new Chess::Piece from an existing one.
67              
68             =head2 Class methods
69              
70             There are no class methods for this class.
71              
72             =head2 Object methods
73              
74             =item clone()
75              
76             Clones an existing Chess::Piece. Requires no arguments. Returns a blessed
77             Chess::Piece object reference which has data identical to the cloned piece,
78             but can be manipulated separately.
79              
80             $clone = $piece->clone();
81             $clone->set_description("Cloned piece");
82              
83             =item get_initial_square()
84              
85             Takes no parameters. Returns the initial square property that the piece was
86             constructed with.
87              
88             =item get_current_square()
89              
90             Takes no parameters. Returns the value of the current square property.
91              
92             =item set_current_square()
93              
94             Takes a single scalar parameter containing the current square of this piece.
95             Sets the current square property to this value. Like L, this square
96             is not tested for validity and should be tested before calling the function.
97              
98             =item get_description()
99              
100             Takes no parameters. Returns the value of the description property.
101              
102             =item set_description()
103              
104             Takes a single scalar parameter containing a description for the piece.
105             Sets the description property to this value.
106              
107             =item get_color()
108              
109             Takes no parameters. Returns the color property the piece was constructed with.
110              
111             =item moved()
112              
113             Takes no parameters. Returns true iff the piece has not been moved (as
114             determined by a call to L).
115              
116             =item set_moved()
117              
118             Takes a single scalar parameter containing true or false. Sets the moved flag
119             if the parameter is true.
120              
121             =item threatened()
122              
123             Takes no parameters. Returns true iff the piece is not threatened (as
124             determined by a call to L).
125              
126             =item set_threatened()
127              
128             Takes a single scalar parameter containing true or false. Sets the threatened
129             flag if the parameter is true.
130              
131             =item captured()
132              
133             Takes no parameters. Returns true iff the piece is not captured (as
134             determined by a call to L
135              
136             =item set_captured()
137              
138             Takes a single scalar parameter containing true or false. Sets the captured
139             flag, and also sets the current square property to C, if the parameter
140             is true.
141              
142             =item can_reach()
143              
144             Takes a single scalar parameter containing the square to be tested. Returns
145             true if the piece can reach the given square from its current location, as
146             determined by a call to the abstract method L.
147              
148             =item reachable_squares()
149              
150             This is an abstract method and must be overridden in all subclasses of
151             Chess::Piece. Returns a list of squares (in lower-case) that the piece can
152             reach. This list is used by L and various methods of
153             L to determine legality of moves and other high-level analyses.
154             Thus, subclasses of Chess::Piece not provided by this framework must return
155             all squares that B reached, regardless of the current state of the
156             board. The L method will then determine if all
157             conditions for a particular move have been met.
158              
159             =back
160              
161             =head1 DIAGNOSTICS
162              
163             =over 4
164              
165             =item Missing argument to Chess::Piece::new()
166              
167             The initial square argument is required. See L for details on how
168             to call this method.
169              
170             =item Invalid Chess::Piece reference
171              
172             The program uses a reference which is undefined, or was obtained without
173             using L or L. Ensure that the program only obtains
174             its references from new() or clone() and that the reference refers to a
175             defined value.
176              
177             =item Call to abstract method Chess::Piece::reachable_squares()
178              
179             The L function is abstract. Any class which subclasses
180             Chess::Piece must provide its own implementation of this method.
181              
182             =back
183              
184             =head1 BUGS
185              
186             Please report any bugs to the author.
187              
188             =head1 AUTHOR
189              
190             Brian Richardson
191              
192             =head1 COPYRIGHT
193              
194             Copyright (c) 2002, 2005 Brian Richardson. All rights reserved. This module
195             is Free Software. It may be modified and redistributed under the same terms
196             as Perl itself.
197              
198             =cut
199             package Chess::Piece;
200              
201 12     12   20685 use strict;
  12         23  
  12         475  
202 12     12   71 use Carp;
  12         25  
  12         1328  
203              
204 12         976 use constant OBJECT_FIELDS => (
205             _firstmoved => undef,
206             init_sq => '',
207             curr_sq => '',
208             player => '',
209             description => '',
210             flags => 0x0
211 12     12   75 );
  12         20  
212              
213 12     12   77 use constant PIECE_MOVED => 0x01;
  12         20  
  12         597  
214 12     12   83 use constant PIECE_THREATENED => 0x02;
  12         28  
  12         509  
215 12     12   59 use constant PIECE_CAPTURED => 0x04;
  12         28  
  12         19878  
216              
217             {
218             my @_pieces = ( );
219             my %object_fields = OBJECT_FIELDS;
220              
221             sub _get_piece_ref {
222 49382     49382   56724 my ($i) = @_;
223 49382         83575 return $_pieces[$i];
224             }
225              
226             sub new {
227 139     139 1 253 my ($caller, $init_sq, $color, $desc) = @_;
228 139   33     503 my $class = ref($caller) || $caller;
229 139         815 my $obj_data = { %object_fields };
230 139 50 33     674 croak "Missing argument to Chess::Piece::new()" unless ($init_sq && $color);
231 139         240 $obj_data->{init_sq} = $init_sq;
232 139         225 $obj_data->{curr_sq} = $init_sq;
233 139         263 $obj_data->{player} = lc $color;
234 139 100       883 $obj_data->{description} = $desc if ($desc);
235 139         228 push @_pieces, $obj_data;
236 139         203 my $i = $#_pieces;
237 139         663 return bless \$i, $class;
238             }
239              
240             sub clone {
241 14707     14707 1 17668 my ($clonee) = @_;
242 14707   33     32881 my $class = ref($clonee) || croak "Invalid Chess::Piece reference";
243 14707         24309 my $r_piece = _get_piece_ref($$clonee);
244 14707 50       30344 croak "Invalid Chess::Piece reference" unless $r_piece;
245 14707         88724 my $new_piece = { %$r_piece };
246 14707         27117 push @_pieces, $new_piece;
247 14707         17450 my $i = $#_pieces;
248 14707         54656 return bless \$i, $class;
249             }
250              
251             sub _firstmoved {
252 3     3   7 my ($self) = @_;
253 3   33     13 my $class = ref($self) || croak "Invalid Chess::Piece reference";
254 3         8 my $r_piece = _get_piece_ref($$self);
255 3 50       12 croak "Invalid Chess::Piece reference" unless $r_piece;
256 3         28 return $r_piece->{_firstmoved};
257             }
258              
259             sub _set_firstmoved {
260 165     165   311 my ($self, $movenum) = @_;
261 165   33     594 my $class = ref($self) || croak "Invalid Chess::Piece reference";
262 165         323 my $r_piece = _get_piece_ref($$self);
263 165 50       391 croak "Invalid Chess::Piece reference" unless $r_piece;
264 165         601 $r_piece->{_firstmoved} = $movenum;
265             }
266             }
267              
268             sub get_initial_square {
269 1     1 1 5 my ($self) = @_;
270 1 50       5 croak "Invalid Chess::Piece reference" unless (ref($self));
271 1         4 my $r_piece = _get_piece_ref($$self);
272 1 50       8 croak "Invalid Chess::Piece reference" unless ($r_piece);
273 1         5 return $r_piece->{init_sq};
274             }
275              
276             sub get_current_square {
277 17942     17942 1 29897 my ($self) = @_;
278 17942 50       36680 croak "Invalid Chess::Piece reference" unless (ref($self));
279 17942         28270 my $r_piece = _get_piece_ref($$self);
280 17942 50       39624 croak "Invalid Chess::Piece reference" unless ($r_piece);
281 17942         54823 return $r_piece->{curr_sq};
282             }
283              
284             sub set_current_square {
285 202     202 1 1124 my ($self, $sq) = @_;
286 202 50       629 croak "Invalid Chess::Piece reference" unless (ref($self));
287 202         416 my $r_piece = _get_piece_ref($$self);
288 202 50       518 croak "Invalid Chess::Piece reference" unless ($r_piece);
289 202         648 $r_piece->{curr_sq} = $sq;
290             }
291              
292             sub get_description {
293 4     4 1 14 my ($self) = @_;
294 4 50       36 croak "Invalid Chess::Piece reference" unless (ref($self));
295 4         10 my $r_piece = _get_piece_ref($$self);
296 4 50       11 croak "Invalid Chess::Piece reference" unless ($r_piece);
297 4         15 return $r_piece->{description};
298             }
299              
300             sub set_description {
301 2     2 1 12 my ($self, $desc) = @_;
302 2 50       10 croak "Invalid Chess::Piece reference" unless (ref($self));
303 2         8 my $r_piece = _get_piece_ref($$self);
304 2 50       7 croak "Invalid Chess::Piece reference" unless ($r_piece);
305 2         7 $r_piece->{description} = $desc;
306             }
307              
308             sub get_player {
309 2808     2808 0 3568 my ($self) = @_;
310 2808 50       5677 croak "Invalid Chess::Piece reference" unless (ref($self));
311 2808         4604 my $r_piece = _get_piece_ref($$self);
312 2808 50       10274 croak "Invalid Chess::Piece reference" unless $r_piece;
313 2808         9301 return $r_piece->{player};
314             }
315              
316             sub moved {
317 2103     2103 1 2586 my ($self) = @_;
318 2103 50       4358 croak "Invalid Chess::Piece reference" unless (ref($self));
319 2103         3766 my $r_piece = _get_piece_ref($$self);
320 2103 50       4341 croak "Invalid Chess::Piece reference" unless ($r_piece);
321 2103         11922 return $r_piece->{flags} & PIECE_MOVED;
322             }
323              
324             sub set_moved {
325 204     204 1 323 my ($self, $set) = @_;
326 204 50       523 croak "Invalid Chess::Piece reference" unless (ref($self));
327 204         425 my $r_piece = _get_piece_ref($$self);
328 204 50       513 croak "Invalid Chess::Piece reference" unless ($r_piece);
329 204 100       641 $r_piece->{flags} |= PIECE_MOVED if ($set);
330 204 100       796 $r_piece->{flags} &= ~PIECE_MOVED if (!$set);
331             }
332              
333             sub threatened {
334 135     135 1 232 my ($self) = @_;
335 135 50       357 croak "Invalid Chess::Piece reference" unless (ref($self));
336 135         286 my $r_piece = _get_piece_ref($$self);
337 135 50       699 croak "Invalid Chess::Piece reference" unless ($r_piece);
338 135         675 return $r_piece->{flags} & PIECE_THREATENED;
339             }
340              
341             sub set_threatened {
342 318     318 1 520 my ($self, $set) = @_;
343 318 50       898 croak "Invalid Chess::Piece reference" unless (ref($self));
344 318         681 my $r_piece = _get_piece_ref($$self);
345 318 50       770 croak "Invalid Chess::Piece reference" unless ($r_piece);
346 318 100       732 $r_piece->{flags} |= PIECE_THREATENED if ($set);
347 318 100       1522 $r_piece->{flags} &= ~PIECE_THREATENED if (!$set);
348             }
349              
350             sub captured {
351 10753     10753 1 14152 my ($self) = @_;
352 10753 50       22434 croak "Invalid Chess::Piece reference" unless (ref($self));
353 10753         18266 my $r_piece = _get_piece_ref($$self);
354 10753 50       21730 croak "Invalid Chess::Piece reference" unless ($r_piece);
355 10753         51054 return $r_piece->{flags} & PIECE_CAPTURED;
356             }
357              
358             sub set_captured {
359 35     35 1 71 my ($self, $set) = @_;
360 35 50       117 croak "Invalid Chess::Piece reference" unless (ref($self));
361 35         85 my $r_piece = _get_piece_ref($$self);
362 35 50       106 croak "Invalid Chess::Piece reference" unless ($r_piece);
363 35 100       86 if ($set) {
364 34         85 $r_piece->{curr_sq} = undef;
365 34         107 $r_piece->{flags} |= PIECE_CAPTURED;
366             }
367             else {
368 1         2 $r_piece->{flags} &= ~PIECE_CAPTURED;
369             }
370             }
371              
372             sub can_reach {
373 4002     4002 1 10017 my ($self, $sq) = @_;
374 4002         7974 my $lsq = lc $sq;
375 4002         12815 return grep /^$sq$/, $self->reachable_squares();
376             }
377              
378             sub reachable_squares {
379 0     0 1   croak "Call to abstract method Chess::Piece::reachable_squares()";
380             }