1<?php 2/* vim: set expandtab tabstop=4 shiftwidth=4: */ 3// +----------------------------------------------------------------------+ 4// | PHP version 4 | 5// +----------------------------------------------------------------------+ 6// | Copyright (c) 2003 The PHP Group | 7// +----------------------------------------------------------------------+ 8// | This source file is subject to version 3.0 of the PHP license, | 9// | that is bundled with this package in the file LICENSE, and is | 10// | available through the world-wide-web at | 11// | http://www.php.net/license/3_0.txt. | 12// | If you did not receive a copy of the PHP license and are unable to | 13// | obtain it through the world-wide-web, please send a note to | 14// | license@php.net so we can mail you a copy immediately. | 15// +----------------------------------------------------------------------+ 16// | Authors: Gregory Beaver <cellog@php.net> | 17// +----------------------------------------------------------------------+ 18// 19// $Id: Standard.php,v 1.8 2006/11/18 00:09:20 cellog Exp $ 20/** 21 * A standard chess game representation 22 * @package Games_Chess 23 * @author Gregory Beaver <cellog@php.net> 24 */ 25/** 26 * The parent class 27 */ 28require_once 'Games/Chess.php'; 29 30/** 31 * Standard chess game 32 * @package Games_Chess 33 * @author Gregory Beaver <cellog@php.net> 34 */ 35class Games_Chess_Standard extends Games_Chess { 36 /** 37 * The chess pieces 38 * @access private 39 * @var array 40 */ 41 var $_pieces; 42 43 /** 44 * Set up a blank chess board 45 */ 46 function blankBoard() 47 { 48 parent::blankBoard(); 49 $this->_pieces = 50 array( 51 'WR1' => false, 52 'WN1' => false, 53 'WB1' => false, 54 'WQ' => false, 55 'WK' => false, 56 'WB2' => false, 57 'WN2' => false, 58 'WR2' => false, 59 60 'WP1' => false, 61 'WP2' => false, 62 'WP3' => false, 63 'WP4' => false, 64 'WP5' => false, 65 'WP6' => false, 66 'WP7' => false, 67 'WP8' => false, 68 69 'BP1' => false, 70 'BP2' => false, 71 'BP3' => false, 72 'BP4' => false, 73 'BP5' => false, 74 'BP6' => false, 75 'BP7' => false, 76 'BP8' => false, 77 78 'BR1' => false, 79 'BN1' => false, 80 'BB1' => false, 81 'BQ' => false, 82 'BK' => false, 83 'BB2' => false, 84 'BN2' => false, 85 'BR2' => false, 86 ); 87 } 88 89 /** 90 * Set up a starting position for a new chess game 91 * @access protected 92 */ 93 function _setupStartingPosition() 94 { 95 $this->_board = array( 96'a8' => 'BR1', 'b8' => 'BN1', 'c8' => 'BB1', 'd8' => 'BQ', 'e8' => 'BK', 'f8' => 'BB2', 'g8' => 'BN2', 'h8' => 'BR2', 97'a7' => 'BP1', 'b7' => 'BP2', 'c7' => 'BP3', 'd7' => 'BP4', 'e7' => 'BP5', 'f7' => 'BP6', 'g7' => 'BP7', 'h7' => 'BP8', 98'a6' => 'a6', 'b6' => 'b6', 'c6' => 'c6', 'd6' => 'd6', 'e6' => 'e6', 'f6' => 'f6', 'g6' => 'g6', 'h6' => 'h6', 99'a5' => 'a5', 'b5' => 'b5', 'c5' => 'c5', 'd5' => 'd5', 'e5' => 'e5', 'f5' => 'f5', 'g5' => 'g5', 'h5' => 'h5', 100'a4' => 'a4', 'b4' => 'b4', 'c4' => 'c4', 'd4' => 'd4', 'e4' => 'e4', 'f4' => 'f4', 'g4' => 'g4', 'h4' => 'h4', 101'a3' => 'a3', 'b3' => 'b3', 'c3' => 'c3', 'd3' => 'd3', 'e3' => 'e3', 'f3' => 'f3', 'g3' => 'g3', 'h3' => 'h3', 102'a2' => 'WP1', 'b2' => 'WP2', 'c2' => 'WP3', 'd2' => 'WP4', 'e2' => 'WP5', 'f2' => 'WP6', 'g2' => 'WP7', 'h2' => 'WP8', 103'a1' => 'WR1', 'b1' => 'WN1', 'c1' => 'WB1', 'd1' => 'WQ', 'e1' => 'WK', 'f1' => 'WB2', 'g1' => 'WN2', 'h1' => 'WR2', 104 ); 105 $this->_halfMoves = 0; 106 $this->_moveNumber = 1; 107 $this->_move = 'W'; 108 $this->_WCastleQ = true; 109 $this->_WCastleK = true; 110 $this->_BCastleQ = true; 111 $this->_BCastleK = true; 112 $this->_enPassantSquare = '-'; 113 $this->_pieces = 114 array( 115 'WR1' => 'a1', 116 'WN1' => 'b1', 117 'WB1' => 'c1', 118 'WQ' => 'd1', 119 'WK' => 'e1', 120 'WB2' => 'f1', 121 'WN2' => 'g1', 122 'WR2' => 'h1', 123 124 'WP1' => array('a2', 'P'), 125 'WP2' => array('b2', 'P'), 126 'WP3' => array('c2', 'P'), 127 'WP4' => array('d2', 'P'), 128 'WP5' => array('e2', 'P'), 129 'WP6' => array('f2', 'P'), 130 'WP7' => array('g2', 'P'), 131 'WP8' => array('h2', 'P'), 132 133 'BP1' => array('a7', 'P'), 134 'BP2' => array('b7', 'P'), 135 'BP3' => array('c7', 'P'), 136 'BP4' => array('d7', 'P'), 137 'BP5' => array('e7', 'P'), 138 'BP6' => array('f7', 'P'), 139 'BP7' => array('g7', 'P'), 140 'BP8' => array('h7', 'P'), 141 142 'BR1' => 'a8', 143 'BN1' => 'b8', 144 'BB1' => 'c8', 145 'BQ' => 'd8', 146 'BK' => 'e8', 147 'BB2' => 'f8', 148 'BN2' => 'g8', 149 'BR2' => 'h8', 150 ); 151 } 152 153 /** 154 * Add a piece to the chessboard 155 * @param W|B piece color 156 * @param K|Q|R|N|P|B Piece type 157 * @param string [a-h][1-8] algebraic location of piece 158 * @return true|PEAR_Error 159 * @throws GAMES_CHESS_ERROR_INVALIDSQUARE 160 * @throws GAMES_CHESS_ERROR_DUPESQUARE 161 * @throws GAMES_CHESS_ERROR_MULTIPIECE 162 */ 163 function addPiece($color, $type, $square) 164 { 165 if (!isset($this->_board[$square])) { 166 return $this->raiseError(GAMES_CHESS_ERROR_INVALIDSQUARE, 167 array('square' => $square)); 168 } 169 if ($this->_board[$square] != $square) { 170 $dpiece = $this->_board[$square]; 171 if ($dpiece{1} == 'P') { 172 $dpiece = $this->_pieces[$dpiece][1]; 173 } else { 174 $dpiece = $dpiece{1}; 175 } 176 return $this->raiseError(GAMES_CHESS_ERROR_DUPESQUARE, 177 array('piece' => $type, 'dpiece' => $dpiece, 'square' => $square)); 178 } 179 switch ($type) { 180 case 'B' : 181 case 'N' : 182 case 'R' : 183 $piece_name = $color . $type; 184 if (!$this->_pieces[$piece_name . '1']) { 185 $this->_board[$square] = $piece_name . '1'; 186 $this->_pieces[$piece_name . '1'] = $square; 187 } elseif (!$this->_pieces[$piece_name . '2']) { 188 $this->_board[$square] = $piece_name . '2'; 189 $this->_pieces[$piece_name . '2'] = $square; 190 } else { 191 // handle promoted pawns 192 for ($col = 1; $col <= 8; $col++) { 193 if (!$this->_pieces[$color . 'P' . $col]) { 194 $this->_pieces[$color . 'P' . $col] = 195 array($square, $type); 196 $this->_board[$square] = $color . 'P' . $col; 197 break 2; 198 } 199 } 200 return $this->raiseError(GAMES_CHESS_ERROR_MULTIPIECE, 201 array('color' => $color, 'piece' => $type)); 202 203 } 204 break; 205 case 'Q' : 206 $piece_name = $color . 'Q'; 207 if (!$this->_pieces[$piece_name]) { 208 $this->_board[$square] = $piece_name; 209 $this->_pieces[$piece_name] = $square; 210 } else { 211 // handle promoted pawns 212 for ($col = 1; $col <= 8; $col++) { 213 if (!$this->_pieces[$color . 'P' . $col]) { 214 $this->_pieces[$color . 'P' . $col] = 215 array($square, 'Q'); 216 $this->_board[$square] = $color . 'P' . $col; 217 break 2; 218 } 219 } 220 return $this->raiseError(GAMES_CHESS_ERROR_MULTIPIECE, 221 array('color' => $color, 'piece' => $type)); 222 } 223 break; 224 case 'P' : 225 // handle regular pawns 226 for ($col = 1; $col <= 8; $col++) { 227 if (!$this->_pieces[$color . 'P' . $col]) { 228 $this->_pieces[$color . 'P' . $col] = 229 array($square, 'P'); 230 $this->_board[$square] = $color . 'P' . $col; 231 break 2; 232 } 233 } 234 return $this->raiseError(GAMES_CHESS_ERROR_MULTIPIECE, 235 array('color' => $color, 'piece' => $type)); 236 break; 237 case 'K' : 238 if (!$this->_pieces[$color . 'K']) { 239 $this->_pieces[$color . 'K'] = $square; 240 $this->_board[$square] = $color . 'K'; 241 } else { 242 return $this->raiseError(GAMES_CHESS_ERROR_MULTIPIECE, 243 array('color' => $color, 'piece' => $type)); 244 } 245 break; 246 } 247 return true; 248 } 249 250 /** 251 * Generate a representation of the chess board and pieces for use as a 252 * direct translation to a visual chess board 253 * @return array 254 */ 255 function toArray() 256 { 257 $ret = array(); 258 foreach ($this->_board as $square => $piece) { 259 if ($piece == $square) { 260 $ret[$square] = false; 261 continue; 262 } 263 $lower = $piece{0}; 264 if (is_array($this->_pieces[$piece])) { 265 $piece = $this->_pieces[$piece][1]; 266 } else { 267 $piece = $piece{1}; 268 } 269 if ($lower == 'B') { 270 $piece = strtolower($piece); 271 } 272 $ret[$square] = $piece; 273 } 274 uksort($ret, array($this, '_sortToArray')); 275 return $ret; 276 } 277 278 /** 279 * Sort two algebraic coordinates for easy display by foreach() iteration 280 * @param string 281 * @param string 282 * @access private 283 */ 284 function _sortToArray($a, $b) 285 { 286 if ($a == $b) { 287 return 0; 288 } 289 if ($a{1} == $b{1}) { 290 return strnatcmp($a{0}, $b{0}); 291 } 292 if ($a{0} == $b{0}) { 293 return strnatcmp($b{1}, $a{1}); 294 } 295 if ($b{1} > $a{1}) { 296 return 1; 297 } 298 if ($a{1} > $b{1}) { 299 return -1; 300 } 301 } 302 303 /** 304 * Render the current board position into Forsyth-Edwards Notation 305 * 306 * This method only renders the board contents, not the castling and other 307 * information 308 * @return string 309 * @access protected 310 */ 311 function _renderFen() 312 { 313 $fen = ''; 314 $ws = 0; 315 $saverow = '8'; 316 foreach ($this->_board as $square => $piece) { 317 if ($square{1} != $saverow) { 318 // if we have just moved to the next rank, 319 // output any whitespace, and a '/' 320 if ($ws) { 321 $fen .= $ws; 322 } 323 $fen .= '/'; 324 $ws = 0; 325 $saverow = $square{1}; 326 } 327 if ($square == $piece) { 328 // increment whitespace - no piece on this square 329 $ws++; 330 } else { 331 // add any whitespace and reset 332 if ($ws) { 333 $fen .= $ws; 334 } 335 $ws = 0; 336 if (is_array($this->_pieces[$piece])) { 337 // add pawns/promoted pawns 338 $p = ($piece{0} == 'W') ? $this->_pieces[$piece][1] : 339 strtolower($this->_pieces[$piece][1]); 340 } else { 341 // add pieces 342 $p = ($piece{0} == 'W') ? $piece{1} : strtolower($piece{1}); 343 } 344 $fen .= $p; 345 } 346 } 347 // add any trailing whitespace 348 if ($ws) { 349 $fen .= $ws; 350 } 351 return $fen; 352 } 353 354 /** 355 * Get the location of every piece on the board of color $color 356 * @access protected 357 * @param W|B color of pieces to check 358 */ 359 function _getAllPieceLocations($color) 360 { 361 $ret = array(); 362 foreach ($this->_pieces as $name => $loc) { 363 if ($name{0} == $color) { 364 $where = (is_array($loc) ? $loc[0] : $loc); 365 if ($where) { 366 $ret[] = $where; 367 } 368 } 369 } 370 return $ret; 371 } 372 373 /** 374 * Used to determine check 375 * 376 * Retrieve all of the moves of the pieces matching the color passed in. 377 * @param W|B 378 * @return array 379 * @access protected 380 */ 381 function _getPossibleChecks($color) 382 { 383 $ret = array(); 384 foreach ($this->_pieces as $name => $loc) { 385 if (!$loc) { 386 continue; 387 } 388 if ($name{0} == $color) { 389 if ($name{1} == 'P') { 390 $ret[$name] = $this->getPossibleMoves($loc[1], $loc[0], $color, false); 391 } else { 392 $ret[$name] = $this->getPossibleMoves($name{1}, $loc, $color, false); 393 } 394 } 395 } 396 return $ret; 397 } 398 399 /** 400 * Determine whether one side's king is in check by the other side's pieces 401 * @param W|B color of pieces to determine enemy check 402 * @return string|array|false square of checking piece(s) or false 403 */ 404 function inCheck($color) 405 { 406 $ret = array(); 407 $king = $this->_pieces[$color . 'K']; 408 $possible = $this->_getPossibleChecks($color == 'W' ? 'B' : 'W'); 409 foreach ($possible as $piece => $squares) { 410 if (in_array($king, $squares)) { 411 $loc = $this->_pieces[$piece]; 412 $ret[] = is_array($loc) ? $loc[0] : $loc; 413 } 414 } 415 if (!count($ret)) { 416 return false; 417 } 418 if (count($ret) == 1) { 419 return $ret[0]; 420 } 421 return $ret; 422 } 423 424 /** 425 * Mark a piece as having been taken. No validation is performed 426 * @param string [a-h][1-8] 427 * @access protected 428 */ 429 function _takePiece($piece) 430 { 431 if (isset($this->_pieces[$this->_board[$piece]])) { 432 $this->_pieces[$this->_board[$piece]] = false; 433 } 434 } 435 436 /** 437 * Move a piece from one square to another, disregarding any existing pieces 438 * 439 * {@link _takePiece()} should always be used prior to this method. No 440 * validation is performed 441 * @param string [a-h][1-8] square the piece resides on 442 * @param string [a-h][1-8] square the piece moves to 443 * @param string Piece to promote to if this is a promotion move 444 */ 445 function _movePiece($from, $to, $promote = '') 446 { 447 if (isset($this->_pieces[$this->_board[$from]])) { 448 $newto = $this->_pieces[$this->_board[$from]]; 449 if (is_array($newto)) { 450 $newto[0] = $to; 451 if ($promote && ($to{1} == '8' || $to{1} == '1')) { 452 $newto[1] = $promote; 453 } 454 } else { 455 $newto = $to; 456 } 457 $this->_pieces[$this->_board[$from]] = $newto; 458 } 459 } 460 461 /** 462 * Translate an algebraic coordinate into the color and name of a piece, 463 * or false if no piece is on that square 464 * @return false|array Format array('color' => B|W, 'piece' => P|R|Q|N|K|B) 465 * @param string [a-h][1-8] 466 * @access protected 467 */ 468 function _squareToPiece($square) 469 { 470 if ($this->_board[$square] != $square) { 471 $piece = $this->_board[$square]; 472 if ($piece{1} == 'P') { 473 $color = $piece{0}; 474 $piece = $this->_pieces[$piece][1]; 475 } else { 476 $color = $piece{0}; 477 $piece = $piece{1}; 478 } 479 return array('color' => $color, 'piece' => $piece); 480 } else { 481 return false; 482 } 483 } 484 485 /** 486 * Retrieve the locations of all pieces of the same type as $piece 487 * @param K|B|N|R|W|P 488 * @param W|B 489 * @param string [a-h][1-8] optional square of piece to exclude from the listing 490 * @access protected 491 * @return array 492 */ 493 function _getAllPieceSquares($piece, $color, $exclude = null) 494 { 495 $ret = array(); 496 foreach ($this->_pieces as $name => $loc) { 497 if (!$loc) { 498 continue; 499 } 500 if ($name{0} != $color) { 501 continue; 502 } 503 if ($name{1} == 'P') { 504 if ($loc[1] != $piece || $loc[0] == $exclude) { 505 continue; 506 } else { 507 $ret[] = $loc[0]; 508 continue; 509 } 510 } 511 if ($loc == $exclude) { 512 continue; 513 } 514 if ($name{1} != $piece) { 515 continue; 516 } 517 $ret[] = $loc; 518 } 519 return $ret; 520 } 521 522 /** 523 * @return string|PEAR_Error 524 * @param array contents returned from {@link parent::_parseMove()} 525 * in other words, not array(GAMES_CHESS_PIECEMOVE => 526 * array('piece' => 'K', ...)), but array('piece' => 'K', ...) 527 * @param W|B current side moving 528 */ 529 function _getSquareFromParsedMove($parsedmove, $color = null) 530 { 531 if (is_null($color)) { 532 $color = $this->_move; 533 } 534 switch ($parsedmove['piece']) { 535 case 'K' : 536 if (in_array($parsedmove['square'], 537 $this->getPossibleKingMoves($this->_pieces[$color . 'K'], $color))) { 538 return $this->_pieces[$color . 'K']; 539 } 540 break; 541 case 'Q' : 542 case 'B' : 543 case 'R' : 544 case 'N' : 545 if ($parsedmove['disambiguate']) { 546 if (strlen($parsedmove['disambiguate']) == 2) { 547 $square = $parsedmove['disambiguate']; 548 } elseif (is_numeric($parsedmove['disambiguate'])) { 549 $row = $parsedmove['disambiguate']; 550 } else { 551 $col = $parsedmove['disambiguate']; 552 } 553 } else { 554 $others = array(); 555 $others = $this->_getAllPieceSquares($parsedmove['piece'], 556 $color); 557 $disambiguate = ''; 558 $ambiguous = array(); 559 if (count($others)) { 560 foreach ($others as $square) { 561 if (in_array($parsedmove['square'], 562 $this->getPossibleMoves($parsedmove['piece'], 563 $square, 564 $color))) { 565 // other pieces can move to this square - need to disambiguate 566 $ambiguous[] = $square; 567 } 568 } 569 } 570 if (count($ambiguous) > 1) { 571 $pieces = implode($ambiguous, ' '); 572 return $this->raiseError( 573 GAMES_CHESS_ERROR_TOO_AMBIGUOUS, 574 array('san' => $parsedmove['piece'] . 575 $parsedmove['disambiguate'] . $parsedmove['takes'] 576 . $parsedmove['square'], 577 'squares' => $pieces, 578 'piece' => $parsedmove['piece'])); 579 } 580 $square = $col = $row = null; 581 } 582 $potentials = array(); 583 foreach ($this->_pieces as $name => $value) { 584 if (!$value) { 585 continue; 586 } 587 if ($name{0} != $color) { 588 continue; 589 } 590 if (isset($square)) { 591 if ($name{1} == $parsedmove['piece'] && 592 $value[0] == $square) { 593 return $square; 594 } 595 if ($name{1} == 'P' && $value[0] == $square && 596 $value[1] == $parsedmove['piece']) { 597 return $square; 598 } 599 } elseif (isset($col)) { 600 if ($name{1} == $parsedmove['piece'] && 601 $value{0} == $col) { 602 if (in_array($parsedmove['square'], 603 $this->getPossibleMoves($parsedmove['piece'], 604 $value, $color))) { 605 $potentials[] = $value; 606 } 607 } 608 if ($name{1} == 'P' && $value[0]{0} == $col && 609 $value[1] == $parsedmove['piece']) { 610 if (in_array($parsedmove['square'], 611 $this->getPossibleMoves($parsedmove['piece'], 612 $value[0], $color))) { 613 $potentials[] = $value[0]; 614 } 615 } 616 } elseif (isset($row)) { 617 if ($name{1} == $parsedmove['piece'] && 618 $value{1} == $row) { 619 if (in_array($parsedmove['square'], 620 $this->getPossibleMoves($parsedmove['piece'], 621 $value, $color))) { 622 $potentials[] = $value; 623 } 624 } 625 if ($name{1} == 'P' && $value[0]{1} == $row && 626 $value[1] == $parsedmove['piece']) { 627 if (in_array($parsedmove['square'], 628 $this->getPossibleMoves($parsedmove['piece'], 629 $value[0], $color))) { 630 $potentials[] = $value[0]; 631 } 632 } 633 } else { 634 if ($name{1} == $parsedmove['piece']) { 635 if (in_array($parsedmove['square'], 636 $this->getPossibleMoves($parsedmove['piece'], 637 $value, $color))) { 638 $potentials[] = $value; 639 } 640 } elseif ($name{1} == 'P' && 641 $value[1] == $parsedmove['piece']) { 642 if (in_array($parsedmove['square'], 643 $this->getPossibleMoves($parsedmove['piece'], 644 $value[0], $color))) { 645 $potentials[] = $value[0]; 646 } 647 } 648 } 649 } 650 if (count($potentials) == 1) { 651 return $potentials[0]; 652 } 653 break; 654 case 'P' : 655 if ($parsedmove['disambiguate']) { 656 $square = $parsedmove['disambiguate'] . $parsedmove['takesfrom']; 657 } else { 658 $square = null; 659 } 660 if ($parsedmove['takesfrom']) { 661 $col = $parsedmove['takesfrom']; 662 } else { 663 $col = null; 664 } 665 $potentials = array(); 666 foreach ($this->_pieces as $name => $value) { 667 if ($name{0} != $color) { 668 continue; 669 } 670 if (isset($square)) { 671 if ($name{1} == 'P' && $value[0] == $square && $value[1] == 'P') { 672 return $square; 673 } 674 } elseif (isset($col)) { 675 if ($name{1} == 'P' && $value[0]{0} == $col && $value[1] == 'P') { 676 if (in_array($parsedmove['square'], 677 $this->getPossiblePawnMoves($value[0], $color))) { 678 $potentials[] = $value[0]; 679 } 680 } 681 } else { 682 if ($name{1} == 'P' && $value[1] == 'P') { 683 if (in_array($parsedmove['square'], 684 $this->getPossiblePawnMoves($value[0], $color))) { 685 $potentials[] = $value[0]; 686 } 687 } 688 } 689 } 690 if (count($potentials) == 1) { 691 return $potentials[0]; 692 } 693 break; 694 } 695 if ($parsedmove['piece'] == 'P') { 696 $san = $parsedmove['takesfrom'] . $parsedmove['takes'] . $parsedmove['square']; 697 } else { 698 $san = $parsedmove['piece'] . 699 $parsedmove['disambiguate'] . $parsedmove['takes'] . 700 $parsedmove['square']; 701 } 702 return $this->raiseError(GAMES_CHESS_ERROR_NOPIECE_CANDOTHAT, 703 array('san' => $san, 704 'color' => $color)); 705 } 706 707 /** 708 * Get the location of the king 709 * 710 * assumes valid color input 711 * @return false|string 712 * @access protected 713 */ 714 function _getKing($color = null) 715 { 716 if (!is_null($color)) { 717 return $this->_pieces[$color . 'K']; 718 } else { 719 return $this->_pieces[$this->_move . 'K']; 720 } 721 } 722 723 /** 724 * Get the location of a piece 725 * 726 * This does NOT take an algebraic square as the argument, but the contents 727 * of _board[algebraic square] 728 * @param string 729 * @return string|array 730 * @access protected 731 */ 732 function _getPiece($piecename) 733 { 734 return is_array($this->_pieces[$piecename]) ? 735 $this->_pieces[$piecename][0] : 736 $this->_pieces[$piecename]; 737 } 738 739 /** 740 * Determine whether a piece name is a knight 741 * 742 * This does NOT take an algebraic square as the argument, but the contents 743 * of _board[algebraic square] 744 * @param string 745 * @return boolean 746 * @access protected 747 */ 748 function _isKnight($piecename) 749 { 750 return $piecename{1} == 'N' || 751 ($piecename{1} == 'P' && 752 $this->_pieces[$piecename][1] == 'N'); 753 } 754 755 /** 756 * Determine whether a piece name is a queen 757 * 758 * This does NOT take an algebraic square as the argument, but the contents 759 * of _board[algebraic square] 760 * @param string 761 * @return boolean 762 * @access protected 763 */ 764 function _isQueen($piecename) 765 { 766 return $piecename{1} == 'Q' || 767 ($piecename{1} == 'P' && 768 $this->_pieces[$piecename][1] == 'Q'); 769 } 770 771 /** 772 * Determine whether a piece name is a bishop 773 * 774 * This does NOT take an algebraic square as the argument, but the contents 775 * of _board[algebraic square] 776 * @param string 777 * @return boolean 778 * @access protected 779 */ 780 function isBishop($piecename) 781 { 782 return $piecename{1} == 'B' || 783 ($piecename{1} == 'P' && 784 $this->_pieces[$piecename][1] == 'B'); 785 } 786 787 /** 788 * Determine whether a piece name is a rook 789 * 790 * This does NOT take an algebraic square as the argument, but the contents 791 * of _board[algebraic square] 792 * @param string 793 * @return boolean 794 * @access protected 795 */ 796 function isRook($piecename) 797 { 798 return $piecename{1} == 'R' || 799 ($piecename{1} == 'P' && 800 $this->_pieces[$piecename][1] == 'R'); 801 } 802 803 /** 804 * Determine whether a piece name is a pawn 805 * 806 * This does NOT take an algebraic square as the argument, but the contents 807 * of _board[algebraic square] 808 * @param string 809 * @return boolean 810 * @access protected 811 */ 812 function isPawn($piecename) 813 { 814 return $piecename{1} == 'P' && 815 $this->_pieces[$piecename][1] == 'P'; 816 } 817 818 /** 819 * Determine whether a piece name is a king 820 * 821 * This does NOT take an algebraic square as the argument, but the contents 822 * of _board[algebraic square] 823 * @param string 824 * @return boolean 825 * @access protected 826 */ 827 function isKing($piecename) 828 { 829 return $piecename{1} == 'K'; 830 } 831 832 /** 833 * Determine whether it is possible to capture the piece delivering check, 834 * or to interpose a piece in between the checking piece and the king 835 * @param array squares that will block a checkmate 836 * @param W|B color of the side attempting to prevent checkmate 837 * @return boolean true if it is possible to remove check 838 */ 839 function _interposeOrCapture($squares, $color) 840 { 841 foreach ($this->_pieces as $name => $value) { 842 if (!$value) { 843 continue; 844 } 845 if ($name{0} != $color) { 846 continue; 847 } 848 if ($name{1} == 'K') { 849 continue; 850 } 851 if (is_array($value)) { 852 $name = $value[1]; 853 $value = $value[0]; 854 } else { 855 $name = $name{1}; 856 } 857 $allmoves = $this->getPossibleMoves($name, $value, $color); 858 foreach($squares as $square) { 859 if (in_array($square, $allmoves)) { 860 // try the move, see if we're still in check 861 // if so, then the piece is pinned and cannot move 862 $this->startTransaction(); 863 $this->_move = $color; 864 if (!class_exists('PEAR')) { 865 require_once 'PEAR.php'; 866 } 867 PEAR::pushErrorHandling(PEAR_ERROR_RETURN); 868 $ret = $this->moveSquare($value, $square); 869 PEAR::popErrorHandling(PEAR_ERROR_RETURN); 870 $this->_move = $color; 871 $stillchecked = $this->inCheck($color); 872 $this->rollbackTransaction(); 873 if (!$stillchecked) { 874 return true; 875 } 876 } 877 } 878 } 879 return false; 880 } 881 882 /** 883 * Retrieve the color of a piece from its name 884 * 885 * Game-specific method of retrieving the color of a piece 886 * @access protected 887 */ 888 function _getColor($name) 889 { 890 return $name{0}; 891 } 892 893 /** 894 * Get a list of all pieces on the board organized by the type of piece, 895 * and the color of the square the piece is on. 896 * 897 * Used to determine basic draw conditions 898 * @return array Format: 899 * 900 * <pre> 901 * array( 902 * // white pieces 903 * 'W' => array('B' => array('W', 'B'), // all bishops 904 * 'K' => array('W'),... 905 * ), 906 * // black pieces 907 * 'B' => array('Q' => array('B'), // all queens 908 * 'K' => array('W'),... // king is on white square 909 * </pre> 910 * @access protected 911 */ 912 function _getPieceTypes() 913 { 914 $ret = array('W' => array(), 'B' => array()); 915 foreach($this->_pieces as $name => $loc) { 916 if (!$loc) { 917 continue; 918 } 919 $type = $name{1}; 920 if (is_array($loc)) { 921 $type = $loc[1]; 922 $loc = $loc[0]; 923 } 924 $ret[$name{0}][$type][] = $this->_getDiagonalColor($loc); 925 } 926 return $ret; 927 } 928} 929?>