| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | =head1 NAME | 
| 2 |  |  |  |  |  |  |  | 
| 3 |  |  |  |  |  |  | Chess::Board - an object representation of a chessboard | 
| 4 |  |  |  |  |  |  |  | 
| 5 |  |  |  |  |  |  | =head1 SYNOPSIS | 
| 6 |  |  |  |  |  |  |  | 
| 7 |  |  |  |  |  |  | $light = Chess::Board->get_color('h1'); | 
| 8 |  |  |  |  |  |  | $dark = Chess::Board->get_color('a1'); | 
| 9 |  |  |  |  |  |  | $e3 = Chess::Board->square_down_from('e4'); | 
| 10 |  |  |  |  |  |  | $e5 = Chess::Board->square_up_from('e4'); | 
| 11 |  |  |  |  |  |  | $d4 = Chess::Board->square_left_of('e4'); | 
| 12 |  |  |  |  |  |  | $f4 = Chess::Board->square_right_of('e4'); | 
| 13 |  |  |  |  |  |  | $board = Chess::Board->new(); | 
| 14 |  |  |  |  |  |  | $is_valid = Chess::Board->square_is_valid($sq); | 
| 15 |  |  |  |  |  |  | if ($is_valid) { | 
| 16 |  |  |  |  |  |  | $board->set_piece_at($sq, $piece); | 
| 17 |  |  |  |  |  |  | $clone = $board->clone(); | 
| 18 |  |  |  |  |  |  | $piece = $clone->get_piece_at($sq); | 
| 19 |  |  |  |  |  |  | $clone->set_piece_at($sq, undef); | 
| 20 |  |  |  |  |  |  | $clone->set_piece_at(Chess::Board->square_up_from($sq), $piece); | 
| 21 |  |  |  |  |  |  | } | 
| 22 |  |  |  |  |  |  |  | 
| 23 |  |  |  |  |  |  | =head1 DESCRIPTION | 
| 24 |  |  |  |  |  |  |  | 
| 25 |  |  |  |  |  |  | The Chess module provides a framework for writing Chess programs with Perl. | 
| 26 |  |  |  |  |  |  |  | 
| 27 |  |  |  |  |  |  | This class forms part of the framework, but it could also be used by | 
| 28 |  |  |  |  |  |  | itself, even to put objects that aren't subclasses of L on it. | 
| 29 |  |  |  |  |  |  |  | 
| 30 |  |  |  |  |  |  | =head1 METHODS | 
| 31 |  |  |  |  |  |  |  | 
| 32 |  |  |  |  |  |  | =head2 Construction | 
| 33 |  |  |  |  |  |  |  | 
| 34 |  |  |  |  |  |  | =over 4 | 
| 35 |  |  |  |  |  |  |  | 
| 36 |  |  |  |  |  |  | =item new() | 
| 37 |  |  |  |  |  |  |  | 
| 38 |  |  |  |  |  |  | Takes no arguments. Returns a blessed Chess::Board object reference. This | 
| 39 |  |  |  |  |  |  | reference can be used to call any of the methods listed in L"Object methods">. | 
| 40 |  |  |  |  |  |  |  | 
| 41 |  |  |  |  |  |  | $board = Chess::Board->new(); | 
| 42 |  |  |  |  |  |  |  | 
| 43 |  |  |  |  |  |  | See also L"clone()"> to construct a new Chess::Board from an existing one. | 
| 44 |  |  |  |  |  |  |  | 
| 45 |  |  |  |  |  |  | =back | 
| 46 |  |  |  |  |  |  |  | 
| 47 |  |  |  |  |  |  | =head2 Class methods | 
| 48 |  |  |  |  |  |  |  | 
| 49 |  |  |  |  |  |  | =over 4 | 
| 50 |  |  |  |  |  |  |  | 
| 51 |  |  |  |  |  |  | =item square_is_valid() | 
| 52 |  |  |  |  |  |  |  | 
| 53 |  |  |  |  |  |  | Takes a single scalar parameter with the square to be tested. Returns true if | 
| 54 |  |  |  |  |  |  | the given square falls within the range a1-h8. Returns false otherwise. | 
| 55 |  |  |  |  |  |  | It is case-insensitive, though all functions that return squares will return | 
| 56 |  |  |  |  |  |  | lower-case. | 
| 57 |  |  |  |  |  |  |  | 
| 58 |  |  |  |  |  |  | if (Chess::Board->square_is_valid($sq)) { | 
| 59 |  |  |  |  |  |  | # call method requiring valid square | 
| 60 |  |  |  |  |  |  | } | 
| 61 |  |  |  |  |  |  |  | 
| 62 |  |  |  |  |  |  | =item get_color_of() | 
| 63 |  |  |  |  |  |  |  | 
| 64 |  |  |  |  |  |  | Takes a single scalar parameter containing the square whose color is requested. | 
| 65 |  |  |  |  |  |  | Returns a scalar containing either of the strings 'light' or 'dark'. Returns | 
| 66 |  |  |  |  |  |  | C and prints a warning to STDERR (see L"DIAGNOSTICS">) if the | 
| 67 |  |  |  |  |  |  | square is not valid. | 
| 68 |  |  |  |  |  |  |  | 
| 69 |  |  |  |  |  |  | $light = Chess::Board->get_color_of("h1"); | 
| 70 |  |  |  |  |  |  | $dark = Chess::Board->get_color_of("a1"); | 
| 71 |  |  |  |  |  |  |  | 
| 72 |  |  |  |  |  |  | =item square_left_of() | 
| 73 |  |  |  |  |  |  |  | 
| 74 |  |  |  |  |  |  | Takes a single scalar parameter containing the square right of the requested | 
| 75 |  |  |  |  |  |  | square. Returns a string containing the square left of the parameter. Returns | 
| 76 |  |  |  |  |  |  | C and prints a warning to STDERR (see L"DIAGNOSTICS">) if the | 
| 77 |  |  |  |  |  |  | square is not valid. Returns undef (but doesn't print a warning) if there is | 
| 78 |  |  |  |  |  |  | no square left of the given square. | 
| 79 |  |  |  |  |  |  |  | 
| 80 |  |  |  |  |  |  | $d4 = Chess::Board->square_left_of("e4"); | 
| 81 |  |  |  |  |  |  |  | 
| 82 |  |  |  |  |  |  | =item square_right_of() | 
| 83 |  |  |  |  |  |  |  | 
| 84 |  |  |  |  |  |  | Takes a single scalar parameter containing the square left of the requested | 
| 85 |  |  |  |  |  |  | square. Returns a string containing the square right of the parameter. Returns | 
| 86 |  |  |  |  |  |  | C and prints a warning to STDERR (see L"DIAGNOSTICS">) if the | 
| 87 |  |  |  |  |  |  | square is not valid. Returns undef (but doesn't print a warning) if there is | 
| 88 |  |  |  |  |  |  | no square right of the given square. | 
| 89 |  |  |  |  |  |  |  | 
| 90 |  |  |  |  |  |  | $f4 = Chess::Board->square_left_of("e4"); | 
| 91 |  |  |  |  |  |  |  | 
| 92 |  |  |  |  |  |  | =item square_up_from() | 
| 93 |  |  |  |  |  |  |  | 
| 94 |  |  |  |  |  |  | Takes a single scalar parameter containing the square down from the requested | 
| 95 |  |  |  |  |  |  | square. Returns a string containing the square up from the parameter. Returns | 
| 96 |  |  |  |  |  |  | C and prints a warning to STDERR (see L"DIAGNOSTICS">) if the | 
| 97 |  |  |  |  |  |  | square is not valid. Returns undef (but doesn't print a warning) if there is | 
| 98 |  |  |  |  |  |  | no square up from the given square. | 
| 99 |  |  |  |  |  |  |  | 
| 100 |  |  |  |  |  |  | $e5 = Chess::Board->square_up_from("e4"); | 
| 101 |  |  |  |  |  |  |  | 
| 102 |  |  |  |  |  |  | =item square_down_from() | 
| 103 |  |  |  |  |  |  |  | 
| 104 |  |  |  |  |  |  | Takes a single scalar parameter containing the square up from the requested | 
| 105 |  |  |  |  |  |  | square. Returns a string containing the square down from the parameter. Returns | 
| 106 |  |  |  |  |  |  | C and prints a warning to STDERR (see L"DIAGNOSTICS">) if the | 
| 107 |  |  |  |  |  |  | square is not valid. Returns undef (but doesn't print a warning) if there is | 
| 108 |  |  |  |  |  |  | no square down from the given square. | 
| 109 |  |  |  |  |  |  |  | 
| 110 |  |  |  |  |  |  | $e3 = Chess::Board->square_down_from("e4"); | 
| 111 |  |  |  |  |  |  |  | 
| 112 |  |  |  |  |  |  | =item horz_distance() | 
| 113 |  |  |  |  |  |  |  | 
| 114 |  |  |  |  |  |  | Takes a single scalar parameter containing the square to calculate distance | 
| 115 |  |  |  |  |  |  | from. Returns the horizontal distance in squares between the two points. | 
| 116 |  |  |  |  |  |  |  | 
| 117 |  |  |  |  |  |  | =item vert_distance() | 
| 118 |  |  |  |  |  |  |  | 
| 119 |  |  |  |  |  |  | Takes a single scalar parameter containing the square to calculate distance | 
| 120 |  |  |  |  |  |  | from. Returns the vertical distance in squares between the two points. | 
| 121 |  |  |  |  |  |  |  | 
| 122 |  |  |  |  |  |  | =item squares_in_line() | 
| 123 |  |  |  |  |  |  |  | 
| 124 |  |  |  |  |  |  | Takes two scalar parameters containing two distinct endpoints in a line. | 
| 125 |  |  |  |  |  |  | Returns a list of scalars in lower-case with an entry for each square in that | 
| 126 |  |  |  |  |  |  | line, or C if the two endpoints do not define a line. In the case where | 
| 127 |  |  |  |  |  |  | both squares are the same, will return a list containing that square. | 
| 128 |  |  |  |  |  |  |  | 
| 129 |  |  |  |  |  |  | =back | 
| 130 |  |  |  |  |  |  |  | 
| 131 |  |  |  |  |  |  | =head2 Object methods | 
| 132 |  |  |  |  |  |  |  | 
| 133 |  |  |  |  |  |  | =over 4 | 
| 134 |  |  |  |  |  |  |  | 
| 135 |  |  |  |  |  |  | =item clone() | 
| 136 |  |  |  |  |  |  |  | 
| 137 |  |  |  |  |  |  | Takes no arguments. Returns a blessed Chess::Board object reference which is | 
| 138 |  |  |  |  |  |  | identical to the caller object. However, it is a I which allows | 
| 139 |  |  |  |  |  |  | the clone()'d object to be manipulated separately of the caller object. | 
| 140 |  |  |  |  |  |  |  | 
| 141 |  |  |  |  |  |  | =item line_is_open() | 
| 142 |  |  |  |  |  |  |  | 
| 143 |  |  |  |  |  |  | Takes two scalar arguments, valid squares defining the endpoints of a line | 
| 144 |  |  |  |  |  |  | on the Chess::Board. Returns true if there are no pieces on either of the | 
| 145 |  |  |  |  |  |  | endpoints, or on any of the intervening squares. Returns false if the line | 
| 146 |  |  |  |  |  |  | is blocked by one or more pieces, and C if the two squares do not | 
| 147 |  |  |  |  |  |  | define endpoints of a line. In the case where both squares are equal, will | 
| 148 |  |  |  |  |  |  | return true if the square is empty and false otherwise. | 
| 149 |  |  |  |  |  |  |  | 
| 150 |  |  |  |  |  |  | =item get_piece_at() | 
| 151 |  |  |  |  |  |  |  | 
| 152 |  |  |  |  |  |  | Takes a single scalar argument containing the square to retrieve the piece | 
| 153 |  |  |  |  |  |  | from. Returns a scalar representing the piece on that square, or C if | 
| 154 |  |  |  |  |  |  | there is none. Returns C and prints a warning to STDERR (See | 
| 155 |  |  |  |  |  |  | L"DIAGNOSTICS">) if the provided square is not valid. | 
| 156 |  |  |  |  |  |  |  | 
| 157 |  |  |  |  |  |  | =item set_piece_at() | 
| 158 |  |  |  |  |  |  |  | 
| 159 |  |  |  |  |  |  | Takes two scalar arguments: the square whose piece to set, and a scalar | 
| 160 |  |  |  |  |  |  | representing the piece to place there. Usually this will be a subclass of | 
| 161 |  |  |  |  |  |  | C, but could be something else if the board is being used | 
| 162 |  |  |  |  |  |  | stand-alone. See L for more information on | 
| 163 |  |  |  |  |  |  | using other things as pieces. Sets the piece at that square if the square is | 
| 164 |  |  |  |  |  |  | valid, and prints a warning to STDERR (see L"DIAGNOSTICS">) otherwise. | 
| 165 |  |  |  |  |  |  |  | 
| 166 |  |  |  |  |  |  | =back | 
| 167 |  |  |  |  |  |  |  | 
| 168 |  |  |  |  |  |  | =head1 DIAGNOSTICS | 
| 169 |  |  |  |  |  |  |  | 
| 170 |  |  |  |  |  |  | =over 4 | 
| 171 |  |  |  |  |  |  |  | 
| 172 |  |  |  |  |  |  | =item 'q9' is not a valid square | 
| 173 |  |  |  |  |  |  |  | 
| 174 |  |  |  |  |  |  | The function which generated this message was called with a square outside | 
| 175 |  |  |  |  |  |  | the range a1-h8, causing it to return C. Use the class method | 
| 176 |  |  |  |  |  |  | L"square_is_valid()"> to validate the square before passing it to any | 
| 177 |  |  |  |  |  |  | method requiring a valid square. | 
| 178 |  |  |  |  |  |  |  | 
| 179 |  |  |  |  |  |  | =item Invalid Chess::Board reference | 
| 180 |  |  |  |  |  |  |  | 
| 181 |  |  |  |  |  |  | The function which generated this message was passed an invalid Chess::Board | 
| 182 |  |  |  |  |  |  | reference. Make sure that the function call is passing a reference obtained | 
| 183 |  |  |  |  |  |  | either from a call to L"new()"> or to L"clone()">, and that the reference | 
| 184 |  |  |  |  |  |  | refers to a defined value. | 
| 185 |  |  |  |  |  |  |  | 
| 186 |  |  |  |  |  |  | =item Can't modify this board. Use Chess::Board->new() instead. | 
| 187 |  |  |  |  |  |  |  | 
| 188 |  |  |  |  |  |  | The program contains a reference to a Chess::Board that wasn't obtained through | 
| 189 |  |  |  |  |  |  | a call to L"new()"> or L"clone()">. Make sure that all references have | 
| 190 |  |  |  |  |  |  | been obtained through these methods. | 
| 191 |  |  |  |  |  |  |  | 
| 192 |  |  |  |  |  |  | =back | 
| 193 |  |  |  |  |  |  |  | 
| 194 |  |  |  |  |  |  | =head1 BUGS | 
| 195 |  |  |  |  |  |  |  | 
| 196 |  |  |  |  |  |  | Please report any bugs to the author. | 
| 197 |  |  |  |  |  |  |  | 
| 198 |  |  |  |  |  |  | =head1 AUTHOR | 
| 199 |  |  |  |  |  |  |  | 
| 200 |  |  |  |  |  |  | Brian Richardson | 
| 201 |  |  |  |  |  |  |  | 
| 202 |  |  |  |  |  |  | =head1 COPYRIGHT | 
| 203 |  |  |  |  |  |  |  | 
| 204 |  |  |  |  |  |  | Copyright (c) 2002, 2005 Brian Richardson. All rights reserved. This module is | 
| 205 |  |  |  |  |  |  | Free Software. It may be modified and redistributed under the same terms as | 
| 206 |  |  |  |  |  |  | Perl itself. | 
| 207 |  |  |  |  |  |  |  | 
| 208 |  |  |  |  |  |  | =cut | 
| 209 |  |  |  |  |  |  | package Chess::Board; | 
| 210 |  |  |  |  |  |  |  | 
| 211 | 12 |  |  | 12 |  | 27109 | use Carp; | 
|  | 12 |  |  |  |  | 26 |  | 
|  | 12 |  |  |  |  | 1204 |  | 
| 212 | 12 |  |  | 12 |  | 66 | use strict; | 
|  | 12 |  |  |  |  | 23 |  | 
|  | 12 |  |  |  |  | 518 |  | 
| 213 |  |  |  |  |  |  |  | 
| 214 | 12 |  |  | 12 |  | 78 | use constant IDX_EMPTY_BOARD => -1; | 
|  | 12 |  |  |  |  | 21 |  | 
|  | 12 |  |  |  |  | 43319 |  | 
| 215 |  |  |  |  |  |  |  | 
| 216 |  |  |  |  |  |  | { | 
| 217 |  |  |  |  |  |  | my $_r_empty_board = _init_empty_board(); | 
| 218 |  |  |  |  |  |  | my $_r_empty_board_arr; | 
| 219 |  |  |  |  |  |  | my @_boards = ( ); | 
| 220 |  |  |  |  |  |  |  | 
| 221 |  |  |  |  |  |  | sub _init_empty_board { | 
| 222 | 12 |  |  | 12 |  | 80 | for (my $y = 0; $y < 8; $y++) { | 
| 223 | 96 | 100 |  |  |  | 220 | my $color = $y % 2 ? 'light' : 'dark'; | 
| 224 | 96 |  |  |  |  | 210 | for (my $x = 0; $x < 8; $x += 2) { | 
| 225 | 384 |  |  |  |  | 1118 | $_r_empty_board_arr->[$y][$x] = { color => $color, | 
| 226 |  |  |  |  |  |  | piece => undef }; | 
| 227 | 384 | 100 |  |  |  | 716 | $color = ($color eq 'light' ? 'dark' : 'light'); | 
| 228 | 384 |  |  |  |  | 1203 | $_r_empty_board_arr->[$y][$x+1] = { color => $color, | 
| 229 |  |  |  |  |  |  | piece => undef }; | 
| 230 | 384 | 100 |  |  |  | 1237 | $color = ($color eq 'light' ? 'dark' : 'light'); | 
| 231 |  |  |  |  |  |  | } | 
| 232 |  |  |  |  |  |  | } | 
| 233 | 12 |  |  |  |  | 27 | my $i = IDX_EMPTY_BOARD; | 
| 234 | 12 |  |  |  |  | 58 | return bless \$i, 'Chess::Board'; | 
| 235 |  |  |  |  |  |  | } | 
| 236 |  |  |  |  |  |  |  | 
| 237 |  |  |  |  |  |  | sub _get_board_array_ref { | 
| 238 | 9676 |  |  | 9676 |  | 11823 | my ($i) = @_; | 
| 239 | 9676 | 100 |  |  |  | 22586 | return $_r_empty_board_arr if ($i == IDX_EMPTY_BOARD); | 
| 240 | 9668 |  |  |  |  | 16478 | return $_boards[$i]; | 
| 241 |  |  |  |  |  |  | } | 
| 242 |  |  |  |  |  |  |  | 
| 243 |  |  |  |  |  |  | sub new { | 
| 244 | 5 |  |  | 5 | 1 | 692 | return $_r_empty_board->clone(); | 
| 245 |  |  |  |  |  |  | } | 
| 246 |  |  |  |  |  |  |  | 
| 247 |  |  |  |  |  |  | sub clone { | 
| 248 | 500 |  |  | 500 | 1 | 1360 | my ($clonee) = @_; | 
| 249 | 500 |  | 33 |  |  | 4366 | my $class = ref($clonee) || croak "Invalid Chess::Board reference"; | 
| 250 | 500 |  |  |  |  | 1065 | my $r_board_arr = _get_board_array_ref($$clonee); | 
| 251 | 500 | 50 |  |  |  | 1199 | croak "Invalid Chess::Board reference" unless ($r_board_arr); | 
| 252 | 500 |  |  |  |  | 563 | my $obj_data; | 
| 253 | 500 |  |  |  |  | 1355 | for (my $y = 0; $y < 8; $y++) { | 
| 254 | 4000 |  |  |  |  | 8188 | for (my $x = 0; $x < 8; $x++) { | 
| 255 | 32000 |  |  |  |  | 58398 | my $color = $r_board_arr->[$y][$x]{color}; | 
| 256 | 32000 |  |  |  |  | 44993 | my $piece = $r_board_arr->[$y][$x]{piece}; | 
| 257 | 32000 | 100 | 66 |  |  | 140574 | $piece = $piece->clone() if (defined($piece) && | 
| 258 |  |  |  |  |  |  | $piece->can('clone')); | 
| 259 | 32000 |  |  |  |  | 168165 | $obj_data->[$y][$x] = { color => $color, | 
| 260 |  |  |  |  |  |  | piece => $piece }; | 
| 261 |  |  |  |  |  |  | } | 
| 262 |  |  |  |  |  |  | } | 
| 263 | 500 |  |  |  |  | 928 | push @_boards, $obj_data; | 
| 264 | 500 |  |  |  |  | 753 | my $i = $#_boards; | 
| 265 | 500 |  |  |  |  | 2040 | return bless \$i, $class; | 
| 266 |  |  |  |  |  |  | } | 
| 267 |  |  |  |  |  |  |  | 
| 268 |  |  |  |  |  |  | sub DESTROY { | 
| 269 | 269 |  |  | 269 |  | 441 | my ($caller) = @_; | 
| 270 | 269 | 50 | 33 |  |  | 8715 | $_boards[$$caller] = undef if (defined($caller) && $$caller >= 0); | 
| 271 |  |  |  |  |  |  | } | 
| 272 |  |  |  |  |  |  | } | 
| 273 |  |  |  |  |  |  |  | 
| 274 |  |  |  |  |  |  | sub _get_square_coords { | 
| 275 | 83430 |  |  | 83430 |  | 99220 | my ($sq) = @_; | 
| 276 | 83430 | 50 |  |  |  | 165289 | if (!Chess::Board->square_is_valid($sq)) { | 
| 277 | 0 |  |  |  |  | 0 | carp "'$sq' is not a valid square"; | 
| 278 | 0 |  |  |  |  | 0 | return undef; | 
| 279 |  |  |  |  |  |  | } | 
| 280 | 83430 |  |  |  |  | 186420 | my $x = ord(lc substr($sq, 0, 1)) - ord('a'); | 
| 281 | 83430 |  |  |  |  | 126215 | my $y = substr($sq, 1, 1) - 1; | 
| 282 | 83430 |  |  |  |  | 147208 | return ($x, $y); | 
| 283 |  |  |  |  |  |  | } | 
| 284 |  |  |  |  |  |  |  | 
| 285 |  |  |  |  |  |  | sub _coords_to_square { | 
| 286 | 44077 |  |  | 44077 |  | 52768 | my ($x, $y) = @_; | 
| 287 | 44077 |  |  |  |  | 79818 | my $sq = chr(ord('a') + $x) . ($y + 1); | 
| 288 | 44077 |  |  |  |  | 91401 | return $sq; | 
| 289 |  |  |  |  |  |  | } | 
| 290 |  |  |  |  |  |  |  | 
| 291 |  |  |  |  |  |  | sub square_is_valid { | 
| 292 | 94034 |  |  | 94034 | 1 | 147116 | my (undef, $sq) = @_; | 
| 293 | 94034 |  |  |  |  | 362143 | return $sq =~ /^[A-Ha-h][1-8]$/; | 
| 294 |  |  |  |  |  |  | } | 
| 295 |  |  |  |  |  |  |  | 
| 296 |  |  |  |  |  |  | sub get_color_of { | 
| 297 | 3 |  |  | 3 | 1 | 9 | my (undef, $sq) = @_; | 
| 298 | 3 |  |  |  |  | 6 | my $r_board_arr = _get_board_array_ref(IDX_EMPTY_BOARD); | 
| 299 | 3 |  |  |  |  | 8 | my ($x, $y) = _get_square_coords($sq); | 
| 300 | 3 | 50 | 33 |  |  | 16 | if (defined($x) && defined($y)) { | 
| 301 | 3 |  |  |  |  | 21 | return $r_board_arr->[$y][$x]{color}; | 
| 302 |  |  |  |  |  |  | } | 
| 303 |  |  |  |  |  |  | else { | 
| 304 | 0 |  |  |  |  | 0 | return undef; | 
| 305 |  |  |  |  |  |  | } | 
| 306 |  |  |  |  |  |  | } | 
| 307 |  |  |  |  |  |  |  | 
| 308 |  |  |  |  |  |  | sub add_horz_distance { | 
| 309 | 22965 |  |  | 22965 | 0 | 33441 | my (undef, $sq, $dist) = @_; | 
| 310 | 22965 |  |  |  |  | 37963 | my ($x, $y) = _get_square_coords($sq); | 
| 311 | 22965 | 50 | 33 |  |  | 100759 | return undef unless (defined($x) && defined($y)); | 
| 312 | 22965 |  |  |  |  | 25639 | $x += $dist; | 
| 313 | 22965 | 100 | 100 |  |  | 89706 | return undef unless (($x >= 0) && ($x <= 7)); | 
| 314 | 21985 |  |  |  |  | 36909 | $sq = _coords_to_square($x, $y); | 
| 315 | 21985 |  |  |  |  | 50403 | return $sq; | 
| 316 |  |  |  |  |  |  | } | 
| 317 |  |  |  |  |  |  |  | 
| 318 |  |  |  |  |  |  | sub add_vert_distance { | 
| 319 | 23068 |  |  | 23068 | 0 | 40327 | my (undef, $sq, $dist) = @_; | 
| 320 | 23068 |  |  |  |  | 40253 | my ($x, $y) = _get_square_coords($sq); | 
| 321 | 23068 | 50 | 33 |  |  | 93800 | return undef unless (defined($x) && defined($y)); | 
| 322 | 23068 |  |  |  |  | 25449 | $y += $dist; | 
| 323 | 23068 | 100 | 100 |  |  | 99460 | return undef unless (($y >= 0) && ($y <= 7)); | 
| 324 | 22092 |  |  |  |  | 40994 | $sq = _coords_to_square($x, $y); | 
| 325 | 22092 |  |  |  |  | 64844 | return $sq; | 
| 326 |  |  |  |  |  |  | } | 
| 327 |  |  |  |  |  |  |  | 
| 328 |  |  |  |  |  |  | sub horz_distance { | 
| 329 | 4361 |  |  | 4361 | 1 | 8271 | my (undef, $sq1, $sq2) = @_; | 
| 330 | 4361 |  |  |  |  | 7771 | my ($x1, $y1) = _get_square_coords($sq1); | 
| 331 | 4361 |  |  |  |  | 8388 | my ($x2, $y2) = _get_square_coords($sq2); | 
| 332 | 4361 |  |  |  |  | 13058 | return $x2 - $x1; | 
| 333 |  |  |  |  |  |  | } | 
| 334 |  |  |  |  |  |  |  | 
| 335 |  |  |  |  |  |  | sub vert_distance { | 
| 336 | 4308 |  |  | 4308 | 1 | 7409 | my (undef, $sq1, $sq2) = @_; | 
| 337 | 4308 |  |  |  |  | 7110 | my ($x1, $y1) = _get_square_coords($sq1); | 
| 338 | 4308 |  |  |  |  | 7745 | my ($x2, $y2) = _get_square_coords($sq2); | 
| 339 | 4308 |  |  |  |  | 11769 | return $y2 - $y1; | 
| 340 |  |  |  |  |  |  | } | 
| 341 |  |  |  |  |  |  |  | 
| 342 |  |  |  |  |  |  | sub square_left_of { | 
| 343 | 5708 |  |  | 5708 | 1 | 8921 | my (undef, $sq) = @_; | 
| 344 | 5708 |  |  |  |  | 13064 | return Chess::Board->add_horz_distance($sq, -1); | 
| 345 |  |  |  |  |  |  | } | 
| 346 |  |  |  |  |  |  |  | 
| 347 |  |  |  |  |  |  | sub square_right_of { | 
| 348 | 12892 |  |  | 12892 | 1 | 25621 | my (undef, $sq) = @_; | 
| 349 | 12892 |  |  |  |  | 27340 | return Chess::Board->add_horz_distance($sq, 1); | 
| 350 |  |  |  |  |  |  | } | 
| 351 |  |  |  |  |  |  |  | 
| 352 |  |  |  |  |  |  | sub square_down_from { | 
| 353 | 6393 |  |  | 6393 | 1 | 9345 | my (undef, $sq) = @_; | 
| 354 | 6393 |  |  |  |  | 14877 | return Chess::Board->add_vert_distance($sq, -1); | 
| 355 |  |  |  |  |  |  | } | 
| 356 |  |  |  |  |  |  |  | 
| 357 |  |  |  |  |  |  | sub square_up_from { | 
| 358 | 12388 |  |  | 12388 | 1 | 18429 | my (undef, $sq) = @_; | 
| 359 | 12388 |  |  |  |  | 24069 | return Chess::Board->add_vert_distance($sq, 1); | 
| 360 |  |  |  |  |  |  | } | 
| 361 |  |  |  |  |  |  |  | 
| 362 |  |  |  |  |  |  | sub squares_in_line { | 
| 363 | 5301 |  |  | 5301 | 1 | 10650 | my (undef, $sq1, $sq2) = @_; | 
| 364 | 5301 |  |  |  |  | 9267 | my ($x1, $y1) = _get_square_coords($sq1); | 
| 365 | 5301 |  |  |  |  | 10049 | my ($x2, $y2) = _get_square_coords($sq2); | 
| 366 | 5301 |  |  |  |  | 7615 | my $hdist = abs($x2 - $x1); | 
| 367 | 5301 |  |  |  |  | 6690 | my $vdist = abs($y2 - $y1); | 
| 368 | 5301 | 50 | 100 |  |  | 22477 | return undef unless ($hdist == 0 || $vdist == 0 || $hdist == $vdist); | 
|  |  |  | 66 |  |  |  |  | 
| 369 | 5301 | 100 | 100 |  |  | 20334 | return ($sq1) unless($hdist || $vdist); | 
| 370 | 3985 | 100 |  |  |  | 8358 | my $hdelta = $hdist ? $hdist / ($x2 - $x1) : 0; | 
| 371 | 3985 | 100 |  |  |  | 7297 | my $vdelta = $vdist ? $vdist / ($y2 - $y1) : 0; | 
| 372 | 3985 |  |  |  |  | 4453 | my @squares; | 
| 373 | 3985 |  |  |  |  | 5094 | my $sq = $sq1; | 
| 374 | 3985 |  |  |  |  | 6006 | push @squares, $sq; | 
| 375 | 3985 | 100 | 100 |  |  | 27694 | if ($vdist and $hdelta == 0) { | 
|  |  | 100 | 66 |  |  |  |  | 
|  |  | 50 |  |  |  |  |  | 
| 376 | 1014 |  |  |  |  | 2516 | for (my $i = 0; $i < $vdist; $i++) { | 
| 377 | 7098 | 50 |  |  |  | 19846 | $sq = $vdelta > 0 ? Chess::Board->square_up_from($sq) : | 
| 378 |  |  |  |  |  |  | Chess::Board->square_down_from($sq); | 
| 379 | 7098 |  |  |  |  | 20747 | push @squares, $sq; | 
| 380 |  |  |  |  |  |  | } | 
| 381 |  |  |  |  |  |  | } | 
| 382 |  |  |  |  |  |  | elsif ($hdist and $vdelta == 0) { | 
| 383 | 1022 |  |  |  |  | 2525 | for (my $i = 0; $i < $hdist; $i++) { | 
| 384 | 7154 | 50 |  |  |  | 19382 | $sq = $hdelta > 0 ? Chess::Board->square_right_of($sq) : | 
| 385 |  |  |  |  |  |  | Chess::Board->square_left_of($sq); | 
| 386 | 7154 |  |  |  |  | 23937 | push @squares, $sq; | 
| 387 |  |  |  |  |  |  | } | 
| 388 |  |  |  |  |  |  | } | 
| 389 |  |  |  |  |  |  | elsif ($hdist == $vdist) { | 
| 390 | 1949 |  |  |  |  | 4480 | for (my $i = 0; $i < $hdist; $i++) { | 
| 391 | 6203 | 100 |  |  |  | 17254 | my $tsq = $hdelta > 0 ? Chess::Board->square_right_of($sq) : | 
| 392 |  |  |  |  |  |  | Chess::Board->square_left_of($sq); | 
| 393 | 6203 | 100 |  |  |  | 19340 | $sq = $vdelta > 0 ? Chess::Board->square_up_from($tsq) : | 
| 394 |  |  |  |  |  |  | Chess::Board->square_down_from($tsq); | 
| 395 | 6203 |  |  |  |  | 19895 | push @squares, $sq; | 
| 396 |  |  |  |  |  |  | } | 
| 397 |  |  |  |  |  |  | } | 
| 398 | 3985 |  |  |  |  | 24300 | return @squares; | 
| 399 |  |  |  |  |  |  | } | 
| 400 |  |  |  |  |  |  |  | 
| 401 |  |  |  |  |  |  | sub get_piece_at { | 
| 402 | 7822 |  |  | 7822 | 1 | 13603 | my ($self, $sq) = @_; | 
| 403 | 7822 | 50 |  |  |  | 16855 | if (!Chess::Board->square_is_valid($sq)) { | 
| 404 | 0 |  |  |  |  | 0 | carp "'$sq' is not a valid square"; | 
| 405 | 0 |  |  |  |  | 0 | return undef; | 
| 406 |  |  |  |  |  |  | } | 
| 407 | 7822 |  |  |  |  | 15263 | my ($x, $y) = _get_square_coords($sq); | 
| 408 | 7822 | 50 |  |  |  | 17555 | croak "Invalid Chess::Board reference" unless (ref($self)); | 
| 409 | 7822 | 50 |  |  |  | 15692 | return undef if $$self == IDX_EMPTY_BOARD; | 
| 410 | 7822 |  |  |  |  | 13537 | my $r_board_arr = _get_board_array_ref($$self); | 
| 411 | 7822 | 50 |  |  |  | 16201 | croak "Invalid Chess::Board reference" unless (defined($r_board_arr)); | 
| 412 | 7822 |  |  |  |  | 24302 | return $r_board_arr->[$y][$x]{piece}; | 
| 413 |  |  |  |  |  |  | } | 
| 414 |  |  |  |  |  |  |  | 
| 415 |  |  |  |  |  |  | sub set_piece_at { | 
| 416 | 1070 |  |  | 1070 | 1 | 2234 | my ($self, $sq, $piece) = @_; | 
| 417 | 1070 | 50 |  |  |  | 2600 | if (!Chess::Board->square_is_valid($sq)) { | 
| 418 | 0 |  |  |  |  | 0 | carp "'$sq' is not a valid square"; | 
| 419 | 0 |  |  |  |  | 0 | return undef; | 
| 420 |  |  |  |  |  |  | } | 
| 421 | 1070 |  |  |  |  | 2387 | my ($x, $y) = _get_square_coords($sq); | 
| 422 | 1070 | 50 |  |  |  | 2571 | croak "Invalid Chess::Board reference" unless (ref($self)); | 
| 423 | 1070 | 50 |  |  |  | 2301 | if ($$self == IDX_EMPTY_BOARD) { | 
| 424 | 0 |  |  |  |  | 0 | carp "Can't modify this board. Use Chess::Board->new() instead"; | 
| 425 | 0 |  |  |  |  | 0 | return; | 
| 426 |  |  |  |  |  |  | } | 
| 427 | 1070 |  |  |  |  | 1926 | my $r_board_arr = _get_board_array_ref($$self); | 
| 428 | 1070 | 50 |  |  |  | 2418 | croak "Invalid Chess::Board reference" unless (defined($r_board_arr)); | 
| 429 | 1070 |  |  |  |  | 3565 | $r_board_arr->[$y][$x]{piece} = $piece; | 
| 430 |  |  |  |  |  |  | } | 
| 431 |  |  |  |  |  |  |  | 
| 432 |  |  |  |  |  |  | sub line_is_open { | 
| 433 | 281 |  |  | 281 | 1 | 1613 | my ($self, $sq1, $sq2) = @_; | 
| 434 | 281 | 50 | 33 |  |  | 850 | if (!Chess::Board->square_is_valid($sq1) || !Chess::Board->square_is_valid($sq2)) { | 
| 435 | 0 |  |  |  |  | 0 | carp "'$sq1' is not a valid square"; | 
| 436 | 0 |  |  |  |  | 0 | return undef; | 
| 437 |  |  |  |  |  |  | } | 
| 438 | 281 | 50 |  |  |  | 908 | croak "Invalid Chess::Board reference" unless (ref($self)); | 
| 439 | 281 | 50 |  |  |  | 696 | return 1 if $$self == IDX_EMPTY_BOARD; | 
| 440 | 281 |  |  |  |  | 615 | my ($x1, $y1) = _get_square_coords($sq1); | 
| 441 | 281 |  |  |  |  | 624 | my ($x2, $y2) = _get_square_coords($sq2); | 
| 442 | 281 |  |  |  |  | 564 | my $hdist = abs($x2 - $x1); | 
| 443 | 281 |  |  |  |  | 511 | my $vdist = abs($y2 - $y1); | 
| 444 | 281 | 50 | 100 |  |  | 1658 | return undef unless ($hdist == 0 || $vdist == 0 || $hdist == $vdist); | 
|  |  |  | 66 |  |  |  |  | 
| 445 | 281 | 100 |  |  |  | 836 | my $hdelta = $hdist ? $hdist / ($x2 - $x1) : 0; | 
| 446 | 281 | 100 |  |  |  | 786 | my $vdelta = $vdist ? $vdist / ($y2 - $y1) : 0; | 
| 447 | 281 |  |  |  |  | 367 | my $xcurr = $x1; | 
| 448 | 281 |  |  |  |  | 350 | my $ycurr = $y1; | 
| 449 | 281 |  |  |  |  | 621 | my $r_board_arr = _get_board_array_ref($$self); | 
| 450 | 281 | 50 |  |  |  | 760 | croak "Invalid Chess::Board reference" unless (defined($r_board_arr)); | 
| 451 | 281 | 50 | 66 |  |  | 1150 | if (($hdist == 0) && ($hdist == $vdist)) { | 
| 452 | 0 | 0 |  |  |  | 0 | return 0 if (defined($r_board_arr->[$ycurr][$xcurr]{piece})); | 
| 453 | 0 |  |  |  |  | 0 | return 1; | 
| 454 |  |  |  |  |  |  | } | 
| 455 | 281 |  | 100 |  |  | 1164 | while (($xcurr != $x2) || ($ycurr != $y2)) { | 
| 456 | 657 | 100 |  |  |  | 2287 | return 0 if (defined($r_board_arr->[$ycurr][$xcurr]{piece})); | 
| 457 | 522 |  |  |  |  | 618 | $xcurr += $hdelta; | 
| 458 | 522 |  |  |  |  | 1687 | $ycurr += $vdelta; | 
| 459 |  |  |  |  |  |  | } | 
| 460 | 146 |  |  |  |  | 1235 | return 1; | 
| 461 |  |  |  |  |  |  | } | 
| 462 |  |  |  |  |  |  |  | 
| 463 |  |  |  |  |  |  | 1; |