File Coverage

blib/lib/Chess/Game/MoveList.pm
Criterion Covered Total %
statement 108 120 90.0
branch 35 58 60.3
condition 9 18 50.0
subroutine 13 15 86.6
pod 8 9 88.8
total 173 220 78.6


line stmt bran cond sub pod time code
1             =head1 NAME
2              
3             Chess::Game::MoveList - a specialized list class for recording the moves of a
4             L
5              
6             =head1 SYNOPSIS
7              
8             $movelist = Chess::Game::MoveList->new("white", "black");
9             $wpawn = Chess::Game::Pawn->new("e2", "white");
10             $entry = $movelist->add_move($wpawn, "e2", "e4");
11             $true = $entry->get_piece() eq $entry;
12             $bpawn = Chess::Game::Pawn->new("e7", "black");
13             $entry = $movelist->add_move($bpawn, "e7", "e6");
14             $entry = $movelist->add_move($wpawn, "e4", "e5");
15             @del_entries = $movelist->delete_move(1, "white"); # delete the list
16             $true = $entries[0]->get_piece() eq $wpawn;
17             $true = $entries[0]->get_dest_square() eq "e4";
18             $true = $entries[1]->get_piece() eq $bpawn;
19             $true = $entries[1]->get_dest_square() eq "e6";
20              
21             =head1 DESCRIPTION
22              
23             The Chess module provides a framework for writing chess programs with Perl.
24             This class forms part of that framework, recording a log of all moves during
25             a L in such a fashion that the list can be used to undo moves
26             that have been made.
27              
28             =head1 METHODS
29              
30             =head2 Construction
31              
32             =item new()
33              
34             Creates a new Chess::Game::MoveList. Takes two scalar parameters containing the
35             names of the two players. These names will be used as a key for calls to
36             L and L.
37              
38             $movelist = Chess::Game::MoveList("white", "black");
39              
40             =head2 Class methods
41              
42             =head2 Object methods
43              
44             =item clone()
45              
46             Creates a new Chess::MoveList based on an existing one. Returns a new list
47             with identical contents, but can be manipulated separately of the original.
48              
49             $clone = $movelist->clone();
50              
51             =item get_move_num()
52              
53             Takes no parameters. Returns the current move number of the game. Numbering
54             is identical to numbering in a regular chess game. The move number does not
55             increment until the first player's next turn.
56              
57             $move_num = $movelist->get_move_num();
58              
59             =item get_last_moved()
60              
61             Takes no parameters. Returns the name of the player who last moved. It will
62             be one of the values passed to L and can be used as a key to
63             L and L.
64              
65             $last_moved = $movelist->get_last_moved();
66              
67             =item get_move()
68              
69             Takes two scalar parameters containing the move number and the name of the
70             player to get the move for. Returns a blessed L
71             with the particulars for that move, or C if that move wasn't found.
72              
73             $entry = $movelist->get_move(1, "white"); # pawn to king's four, perhaps?
74              
75             =item get_all_moves()
76              
77             Takes an optional scalar parameter specifying which player to return a list
78             of moves for. Returns an array of all the entries for moves made by that
79             player. If the player is not specified, returns a two-element array containing
80             references to the first player's and second player's lists respectively.
81              
82             @wmoves = $movelist->get_all_moves("white");
83             @bmoves = $movelist->get_all_moves("black");
84             ($wmoves, $bmoves) = $movelist->get_all_moves();
85              
86             =item add_move()
87              
88             Takes three scalar parameters containing a reference to the piece being moved,
89             the square it is being moved from, and square it is being moved to. Returns
90             a blessed L containing the particulars for that
91             move.
92              
93             $entry = $movelist->add_move($pawn, "e2", "e4");
94              
95             =item delete_move()
96              
97             Takes no parameters. Returns the last move to be made, if there is one, and
98             then deletes it. The MoveList is now in exactly the same state as prior to
99             the last move being made.
100              
101             $entry = $movelist->delete_move();
102              
103             =head1 DIAGNOSTICS
104              
105             =item Invalid Chess::Game::MoveList reference
106              
107             The program contains a reference to a Chess::Game::MoveList object not
108             obtained through L or L. Ensure that all such references
109             were obtained properly, and that the reference refers to a defined value.
110              
111             =item Chess::Game::MoveList player entries must be unique keys
112              
113             L requires that the two arguments can be used as hash keys. Ensure
114             that the call to new contains two defined, unique keys as player names.
115              
116             =item Invalid move number
117              
118             The program contains a call to a method requiring a move number, and passes in
119             a move number of 0 or less. Move numbering starts at 1 to be consistent with
120             a standard chess game.
121              
122             =head1 BUGS
123              
124             Please report any bugs to the author.
125              
126             =head1 AUTHOR
127              
128             Brian Richardson
129              
130             =head1 COPYRIGHT
131              
132             Copyright (c) 2002, 2005 Brian Richardson. All rights reserved. This module is
133             Free Software. It may be modified and redistributed under the same terms as
134             Perl itself.
135              
136             =cut
137             package Chess::Game::MoveList;
138              
139 4     4   31152 use Chess::Game::MoveListEntry;
  4         9  
  4         103  
140 4     4   24 use Carp;
  4         7  
  4         213  
141 4     4   22 use strict;
  4         7  
  4         152  
142              
143 4         16176 use constant OBJECT_DATA => (
144             move_num => 0,
145             players => undef,
146             last_moved => undef,
147             list => undef
148 4     4   24 );
  4         5  
149              
150             {
151             my %_object_data = OBJECT_DATA;
152             my @_move_lists = ( );
153             my $_last_moved = undef;
154              
155             sub _get_move_list {
156 3568     3568   4391 my ($i) = @_;
157 3568         6011 return $_move_lists[$i];
158             }
159              
160             sub new {
161 230     230 1 611 my ($caller, $player1, $player2) = @_;
162 230   33     1043 my $class = ref($caller) || $caller;
163 230 50 33     1737 if (!defined($player1 && $player2) or $player1 eq $player2) {
      33        
164 0         0 croak "Chess::Game::MoveList player labels must be unique keys";
165             }
166 230         1523 my $obj_data = { %_object_data };
167 230         761 $obj_data->{players} = [ $player1, $player2 ];
168 230         1184 $obj_data->{list} = { $player1 => [ ], $player2 => [ ] };
169 230         512 push @_move_lists, $obj_data;
170 230         440 my $i = $#_move_lists;
171 230         1271 return bless \$i, $class;
172             }
173              
174             sub clone {
175 1     1 1 3 my ($self) = @_;
176 1   33     6 my $class = ref($self) || croak "Invalid Chess::Game::MoveList reference";
177 1         4 my $r_move_list = $_move_lists[$$self];
178 1 50       3 croak "Invalid Chess::Game::MoveList reference" unless ($r_move_list);
179 1         8 my $obj_data = { %_object_data };
180 1         3 $obj_data->{players} = [ @{$r_move_list->{players}} ];
  1         5  
181 1         3 my $player1 = $obj_data->{players}[0];
182 1         3 my $player2 = $obj_data->{players}[1];
183 1         3 foreach my $entry (@{$r_move_list->{list}{$player1}}) {
  1         5  
184 2         4 push @{$obj_data->{list}{$player1}}, $entry->clone();
  2         12  
185             }
186 1         3 foreach my $entry (@{$r_move_list->{$player2}}) {
  1         4  
187 0         0 push @{$obj_data->{list}{$player2}}, $entry->clone();
  0         0  
188             }
189 1         3 push @_move_lists, $obj_data;
190 1         2 my $i = $#_move_lists;
191 1         5 return bless \$i, $class;
192             }
193              
194             sub DESTROY {
195 0     0   0 my ($self) = @_;
196 0 0       0 $_move_lists[$$self] = undef if (ref($self));
197             }
198             }
199              
200             sub get_move_num {
201 62     62 1 119 my ($self) = @_;
202 62 50       234 croak "Invalid Chess::Game::MoveList reference" unless (ref($self));
203 62         207 my $obj_data = _get_move_list($$self);
204 62 50       166 croak "Invalid Chess::Game::MoveList reference" unless ($obj_data);
205 62         254 return $obj_data->{move_num} + 1;
206             }
207              
208             sub get_last_moved {
209 431     431 1 643 my ($self) = @_;
210 431 50       1012 croak "Invalid Chess::Game::MoveList reference" unless (ref($self));
211 431         901 my $obj_data = _get_move_list($$self);
212 431 50       890 croak "Invalid Chess::Game::MoveList reference" unless ($obj_data);
213 431         742 my $last_moved = $obj_data->{last_moved};
214 431 100       999 return undef unless defined($last_moved);
215 424         1514 return $obj_data->{players}[$last_moved];
216             }
217              
218             sub get_players {
219 0     0 0 0 my ($self) = @_;
220 0 0       0 croak "Invalid Chess::Game::MoveList reference" unless (ref($self));
221 0         0 my $obj_data = _get_move_list_ref($$self);
222 0 0       0 croak "Invalid Chess::Game::MoveList reference" unless ($obj_data);
223 0         0 return @{$obj_data->{players}};
  0         0  
224             }
225              
226             sub get_move {
227 51     51 1 121 my ($self, $move_num, $player) = @_;
228 51 50       143 croak "Invalid Chess::Game::MoveList reference" unless (ref($self));
229 51         104 my $obj_data = _get_move_list($$self);
230 51 50       126 croak "Invalid Chess::Game::MoveList reference" unless ($obj_data);
231 51 100       137 return undef unless (defined($player));
232 48 50       74 return undef unless (grep /^$player$/, @{$obj_data->{players}});
  48         701  
233 48         246 return $obj_data->{list}{$player}[$move_num - 1];
234             }
235              
236             sub get_all_moves {
237 228     228 1 399 my ($self, $player) = @_;
238 228 50       706 croak "Invalid Chess::Game::MoveList reference" unless (ref($self));
239 228         595 my $obj_data = _get_move_list($$self);
240 228 50       548 croak "Invalid Chess::Game::MoveList reference" unless ($obj_data);
241 228 50 66     746 return undef if (defined($player) and !grep /^$player$/, @{$obj_data->{players}});
  2         39  
242 228 100       489 if (defined($player)) {
243 2         3 return @{$obj_data->{list}{$player}};
  2         11  
244             }
245             else {
246 226         614 my $key1 = $obj_data->{players}[0];
247 226         432 my $key2 = $obj_data->{players}[1];
248 226         350 my @moves = ([ @{$obj_data->{list}{$key1}} ], [ @{$obj_data->{list}{$key2}} ]);
  226         1262  
  226         1327  
249 226         1031 return @moves;
250             }
251             }
252              
253             sub add_move {
254 2791     2791 1 5700 my ($self, $piece, $sq1, $sq2, $flags) = @_;
255 2791 50       5766 croak "Invalid Chess::Game::MoveList reference" unless (ref($self));
256 2791         4780 my $obj_data = _get_move_list($$self);
257 2791 50       5779 croak "Invalid Chess::Game::MoveList reference" unless ($obj_data);
258 2791         4163 my $move_num = $obj_data->{move_num};
259 2791         3475 my $last_moved = $obj_data->{last_moved};
260 2791 100 100     38694 my $turn = (defined($last_moved) && ($last_moved == 0)) ? 1 : 0;
261 2791 100       7840 if (defined($last_moved)) {
262 2560 100       5558 $move_num++ if ($turn == 0);
263             }
264             else {
265 231         421 $move_num = 0;
266             }
267 2791         4560 my $player = $obj_data->{players}[$turn];
268 2791         10402 my $entry = Chess::Game::MoveListEntry->new($move_num + 1, $piece, $sq1, $sq2, $flags);
269 2791         5374 my $move_list_ref = $obj_data->{list}{$player};
270 2791         4551 $move_list_ref->[$move_num] = $entry;
271 2791         3626 $obj_data->{last_moved} = $turn;
272 2791         3479 $obj_data->{move_num} = $move_num;
273 2791         10379 return $entry;
274             }
275              
276             sub delete_move {
277 5     5 1 1081 my ($self) = @_;
278 5 50       20 croak "Invalid Chess::Game::MoveList reference" unless (ref($self));
279 5         12 my $obj_data = _get_move_list($$self);
280 5 50       22 croak "Invalid Chess::Game::MoveList reference" unless ($obj_data);
281 5         12 my $last_moved = $obj_data->{last_moved};
282 5 100       19 return undef unless (defined($last_moved));
283 4         10 my $curr_move = $obj_data->{move_num};
284 4         12 my $player = $obj_data->{players}[$last_moved];
285 4         12 my $entry = $obj_data->{list}{$player}[$curr_move];
286 4         12 delete $obj_data->{list}{$player}[$curr_move];
287 4 100       19 $obj_data->{last_moved} = $last_moved ? 0 : 1;
288 4 100       147 if ($last_moved == 0) {
289 2 50       7 if ($curr_move == 0) {
290 0         0 $obj_data->{last_moved} = undef;
291             }
292             else {
293 2         5 $obj_data->{move_num}--;
294             }
295             }
296 4         13 return $entry;
297             }
298              
299             1;