Games_Chess
[ class tree: Games_Chess ] [ index: Games_Chess ] [ all elements ]

Source for file Crazyhouse.php

Documentation is available at Crazyhouse.php

  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: Crazyhouse.php,v 1.10 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.  */
  28. require_once 'Games/Chess/Standard.php';
  29.  
  30. /**
  31.  * Crazyhouse chess game
  32.  * 
  33.  * A captured piece may be placed on the board as your own piece!
  34.  * Note that FEN is incapable of setting up a game mid-swing - no
  35.  * record of captured pieces is possible.  If requested, a future version
  36.  * may extend the FEN standard to allow this, particularly if ICC follows
  37.  * the same standard
  38.  * @package Games_Chess
  39.  * @author Gregory Beaver <cellog@php.net>
  40.  */
  41.     /**
  42.      * Captured piece count.
  43.      * 
  44.      * Each sub-array consists of pieces owned by the color, so
  45.      * 'W' (white) has captured the pieces in the 'W' sub-array
  46.      * @var array 
  47.      */
  48.     var $_captured =
  49.         array(
  50.             'W' =>
  51.                 array(
  52.                     'P' => 0,
  53.                     'B' => 0,
  54.                     'N' => 0,
  55.                     'Q' => 0,
  56.                     'R' => 0,
  57.                 ),
  58.             'B' =>
  59.                 array(
  60.                     'P' => 0,
  61.                     'B' => 0,
  62.                     'N' => 0,
  63.                     'Q' => 0,
  64.                     'R' => 0,
  65.                 )
  66.         );
  67.  
  68.     /**
  69.      *
  70.      */
  71.     var $_pieces =
  72.         array(
  73.             'W' =>
  74.                 array(
  75.                     'P' =>
  76.                         array(),
  77.                     'B' =>
  78.                         array(),
  79.                     'N' =>
  80.                         array(),
  81.                     'Q' =>
  82.                         array(),
  83.                     'R' =>
  84.                         array(),
  85.                     'K' =>
  86.                         array(),
  87.                 ),
  88.             'B' =>
  89.                 array(
  90.                     'P' =>
  91.                         array(),
  92.                     'B' =>
  93.                         array(),
  94.                     'N' =>
  95.                         array(),
  96.                     'Q' =>
  97.                         array(),
  98.                     'R' =>
  99.                         array(),
  100.                     'K' =>
  101.                         array(),
  102.                 ),
  103.         );
  104.     /**
  105.      * Set up a blank chess board
  106.      */
  107.     function blankBoard()
  108.     {
  109.         Games_Chess::blankBoard();
  110.         $this->_captured =
  111.         array(
  112.             'W' =>
  113.                 array(
  114.                     'P' => 0,
  115.                     'B' => 0,
  116.                     'N' => 0,
  117.                     'Q' => 0,
  118.                     'R' => 0,
  119.                 ),
  120.             'B' =>
  121.                 array(
  122.                     'P' => 0,
  123.                     'B' => 0,
  124.                     'N' => 0,
  125.                     'Q' => 0,
  126.                     'R' => 0,
  127.                 )
  128.         );
  129.         $this->_pieces =
  130.         array(
  131.             'W' =>
  132.                 array(
  133.                     'P' =>
  134.                         array(),
  135.                     'B' =>
  136.                         array(),
  137.                     'N' =>
  138.                         array(),
  139.                     'Q' =>
  140.                         array(),
  141.                     'R' =>
  142.                         array(),
  143.                     'K' =>
  144.                         array(),
  145.                 ),
  146.             'B' =>
  147.                 array(
  148.                     'P' =>
  149.                         array(),
  150.                     'B' =>
  151.                         array(),
  152.                     'N' =>
  153.                         array(),
  154.                     'Q' =>
  155.                         array(),
  156.                     'R' =>
  157.                         array(),
  158.                     'K' =>
  159.                         array(),
  160.                 ),
  161.         );
  162.     }
  163.  
  164.     /**
  165.      * Set up a starting position for a new chess game
  166.      * @access protected
  167.      */
  168.     function _setupStartingPosition()
  169.     {
  170.         parent::_setupStartingPosition();
  171.         $this->_board = array(
  172. 'a8' => 'BR0''b8' => 'BN0''c8' => 'BB0''d8' => 'BQ0''e8' => 'BK0''f8' => 'BB1''g8' => 'BN1''h8' => 'BR1',
  173. 'a7' => 'BP0''b7' => 'BP1''c7' => 'BP2''d7' => 'BP3''e7' => 'BP4''f7' => 'BP5''g7' => 'BP6''h7' => 'BP7',
  174. 'a6' => 'a6''b6' => 'b6''c6' => 'c6''d6' => 'd6''e6' => 'e6''f6' => 'f6''g6' => 'g6''h6' => 'h6',
  175. 'a5' => 'a5''b5' => 'b5''c5' => 'c5''d5' => 'd5''e5' => 'e5''f5' => 'f5''g5' => 'g5''h5' => 'h5',
  176. 'a4' => 'a4''b4' => 'b4''c4' => 'c4''d4' => 'd4''e4' => 'e4''f4' => 'f4''g4' => 'g4''h4' => 'h4',
  177. 'a3' => 'a3''b3' => 'b3''c3' => 'c3''d3' => 'd3''e3' => 'e3''f3' => 'f3''g3' => 'g3''h3' => 'h3',
  178. 'a2' => 'WP0''b2' => 'WP1''c2' => 'WP2''d2' => 'WP3''e2' => 'WP4''f2' => 'WP5''g2' => 'WP6''h2' => 'WP7',
  179. 'a1' => 'WR0''b1' => 'WN0''c1' => 'WB0''d1' => 'WQ0''e1' => 'WK0''f1' => 'WB1''g1' => 'WN1''h1' => 'WR1',
  180.         );
  181.         $this->_captured =
  182.         array(
  183.             'W' =>
  184.                 array(
  185.                     'P' => 0,
  186.                     'B' => 0,
  187.                     'N' => 0,
  188.                     'Q' => 0,
  189.                     'R' => 0,
  190.                 ),
  191.             'B' =>
  192.                 array(
  193.                     'P' => 0,
  194.                     'B' => 0,
  195.                     'N' => 0,
  196.                     'Q' => 0,
  197.                     'R' => 0,
  198.                 )
  199.         );
  200.         $this->_pieces =
  201.         array(
  202.             'W' =>
  203.                 array(
  204.                     'P' =>
  205.                         array(
  206.                             array('a2''P'),
  207.                             array('b2''P'),
  208.                             array('c2''P'),
  209.                             array('d2''P'),
  210.                             array('e2''P'),
  211.                             array('f2''P'),
  212.                             array('g2''P'),
  213.                             array('h2''P'),
  214.                         ),
  215.                     'B' =>
  216.                         array(
  217.                             'c1',
  218.                             'f1',
  219.                         ),
  220.                     'N' =>
  221.                         array(
  222.                             'b1',
  223.                             'g1',
  224.                         ),
  225.                     'Q' =>
  226.                         array(
  227.                             'd1'
  228.                         ),
  229.                     'R' =>
  230.                         array(
  231.                             'a1',
  232.                             'h1',
  233.                         ),
  234.                     'K' =>
  235.                         array(
  236.                             'e1'
  237.                         ),
  238.                 ),
  239.             'B' =>
  240.                 array(
  241.                     'P' =>
  242.                         array(
  243.                             array('a7''P'),
  244.                             array('b7''P'),
  245.                             array('c7''P'),
  246.                             array('d7''P'),
  247.                             array('e7''P'),
  248.                             array('f7''P'),
  249.                             array('g7''P'),
  250.                             array('h7''P'),
  251.                         ),
  252.                     'B' =>
  253.                         array(
  254.                             'c8',
  255.                             'f8',
  256.                         ),
  257.                     'N' =>
  258.                         array(
  259.                             'b8',
  260.                             'g8',
  261.                         ),
  262.                     'Q' =>
  263.                         array(
  264.                             'd8',
  265.                         ),
  266.                     'R' =>
  267.                         array(
  268.                             'a8',
  269.                             'h8',
  270.                         ),
  271.                     'K' =>
  272.                         array(
  273.                             'e8'
  274.                         ),
  275.                 ),
  276.         );
  277.     }
  278.  
  279.     /**
  280.      * Make a move from a Standard Algebraic Notation (SAN) format
  281.      *
  282.      * SAN is just a normal chess move like Na4, instead of the English Notation,
  283.      * like NR4
  284.      * @param string 
  285.      * @return true|PEAR_Error
  286.      */
  287.     function moveSAN($move)
  288.     {
  289.         if (!is_array($this->_board)) {
  290.             $this->resetGame();
  291.         }
  292.         if (!strpos($move'@')) {
  293.             return parent::moveSAN($move);
  294.         }
  295.         if (!$this->isError($parsedMove $this->_parseMove($move))) {
  296.             if (!$this->isError($err $this->_validMove($parsedMove))) {
  297.                 $p $parsedMove[GAMES_CHESS_PIECEPLACEMENT]['piece'];
  298.                 $sq $parsedMove[GAMES_CHESS_PIECEPLACEMENT]['square'];
  299.                 $this->_captured[$this->_move][$p]--;
  300.                 $set ($p == 'P'? array($sq'P'$sq;
  301.                 $this->_pieces[$this->_move][$p][$set;
  302.                 $this->_board[$sq$this->_move $p .
  303.                     (count($this->_pieces[$this->_move][$p]- 1);
  304.                 $this->_enPassantSquare '-';
  305.                 $this->_moves[$this->_moveNumber][($this->_move == 'W'? 0 : 1$move;
  306.                 $oldMoveNumber $this->_moveNumber;
  307.                 $this->_moveNumber += ($this->_move == 'W'? 0 : 1;
  308.                 $this->_halfMoves++;
  309.                 $moveWithCheck $move;
  310.                 if ($this->inCheckMate(($this->_move == 'W''B' 'W')) {
  311.                     $moveWithCheck .= '#';
  312.                 elseif ($this->inCheck(($this->_move == 'W''B' 'W')) {
  313.                     $moveWithCheck .= '+';
  314.                 }
  315.                 $this->_movesWithCheck[$oldMoveNumber][($this->_move == 'W'? 0 : 1$moveWithCheck;
  316.                 $this->_move ($this->_move == 'W' 'B' 'W');
  317.                 
  318.                 // increment the position counter for this position
  319.                 $x $this->renderFen(false);
  320.                 if (!isset($this->_allFENs[$x])) {
  321.                     $this->_allFENs[$x= 0;
  322.                 }
  323.                 $this->_allFENs[$x]++;
  324.                 return true;
  325.             else {
  326.                 return $err;
  327.             }
  328.         else {
  329.             return $parsedMove;
  330.         }
  331.     }
  332.  
  333.     function _validMove($move)
  334.     {
  335.         list($type$infoeach($move);
  336.         reset($move);
  337.         if ($type == GAMES_CHESS_PIECEPLACEMENT{
  338.             if (!$this->_captured[$this->_move][$info['piece']]{
  339.                 return $this->raiseError(GAMES_CHESS_ERROR_NOPIECES_TOPLACE,
  340.                     array('color' => $this->_move == 'W' 'B' 'W''piece' => $info['piece']));
  341.             }
  342.             if ($this->_board[$info['square']] != $info['square']{
  343.                 return $this->raiseError(GAMES_CHESS_ERROR_PIECEINTHEWAY,
  344.                     array('square' => $info['square']));
  345.             }
  346.             return true;
  347.         else {
  348.             return parent::_validMove($move);
  349.         }
  350.     }
  351.  
  352.     function _takePiece($square)
  353.     {
  354.         $piece $this->_board[$square];
  355.         unset($this->_pieces[$piece{0}][$piece{1}][$piece{2+ 0]);
  356.         // add a piece to the list of pieces captured by the enemy
  357.         $this->_captured[$piece{0== 'W' 'B' 'W'][$piece{1}]++;
  358.         // ensure integrity of the remaining pieces
  359.         for ($i $piece{2+ 1; $i <= count($this->_pieces[$piece{0}][$piece{1}])$i++{
  360.             $value $this->_pieces[$piece{0}][$piece{1}][$i];
  361.             // get the square this piece is on
  362.             if (is_array($value)) {
  363.                 $value $value[0];
  364.             }
  365.             // adjust to the right value
  366.             $this->_board[$value]{2($this->_board[$value]{2- 1'';
  367.         }
  368.         // fix the indices
  369.         $this->_pieces[$piece{0}][$piece{1}array_values($this->_pieces[$piece{0}][$piece{1}]);
  370.     }
  371.  
  372.     /**
  373.      * Move a piece from one square to another, disregarding any existing pieces
  374.      *
  375.      * {@link _takePiece()} should always be used prior to this method.  No
  376.      * validation is performed
  377.      * @param string [a-h][1-8] square the piece resides on
  378.      * @param string [a-h][1-8] square the piece moves to
  379.      * @param string Piece to promote to if this is a promotion move
  380.      */
  381.     function _movePiece($from$to$promote '')
  382.     {
  383.         $piece $this->_board[$from];
  384.         if ($piece == $from{
  385.             return;
  386.         }
  387.         if (isset($this->_pieces[$piece{0}][$piece{1}][$piece{2}])) {
  388.             $newto $this->_pieces[$piece{0}][$piece{1}][$piece{2}];
  389.             if (is_array($newto)) {
  390.                 $newto[0$to;
  391.                 if ($to{1== '8' || $to{1== '1'{
  392.                     $newto[1$promote;
  393.                 }
  394.             else {
  395.                 $newto $to;
  396.             }
  397.             $this->_pieces[$piece{0}][$piece{1}][$piece{2}$newto;
  398.         }
  399.     }
  400.     
  401.     /**
  402.      * Translate an algebraic coordinate into the color and name of a piece,
  403.      * or false if no piece is on that square
  404.      * @return false|arrayFormat array('color' => B|W, 'piece' => P|R|Q|N|K|B)
  405.      * @param string [a-h][1-8]
  406.      * @access protected
  407.      */
  408.     function _squareToPiece($square)
  409.     {
  410.         if ($this->_board[$square!= $square{
  411.             $piece $this->_board[$square];
  412.             if ($piece{1== 'P'{
  413.                 $color $piece{0};
  414.                 $piece $this->_pieces[$piece{0}][$piece{1}][$piece{2}][1];
  415.             else {
  416.                 $color $piece{0};
  417.                 $piece $piece{1};
  418.             }
  419.             return array('color' => $color'piece' => $piece);
  420.         else {
  421.             return false;
  422.         }
  423.     }
  424.     
  425.     /**
  426.      * Retrieve the locations of all pieces of the same type as $piece
  427.      * @param K|B|N|R|W|P
  428.      * @param W|B
  429.      * @param string [a-h][1-8] optional square of piece to exclude from the listing
  430.      * @access protected
  431.      * @return array 
  432.      */
  433.     function _getAllPieceSquares($piece$color$exclude = null)
  434.     {
  435.         $ret = array();
  436.         if ($piece != 'P'{
  437.             foreach ($this->_pieces[$color]['P'as $loc{
  438.                 if ($loc[1!= $piece || $loc[0== $exclude{
  439.                     continue;
  440.                 }
  441.                 $ret[$loc[0];
  442.             }
  443.         }
  444.         foreach ($this->_pieces[$color][$pieceas $loc{
  445.             if ($loc != $exclude{
  446.                 $ret[$loc;
  447.             }
  448.         }
  449.         return $ret;
  450.     }
  451.     
  452.     /**
  453.      * @return string|PEAR_Error
  454.      * @param array contents returned from {@link parent::_parseMove()}
  455.      *               in other words, not array(GAMES_CHESS_PIECEMOVE =>
  456.      *               array('piece' => 'K', ...)), but array('piece' => 'K', ...)
  457.      * @param W|Bcurrent side moving
  458.      */
  459.     function _getSquareFromParsedMove($parsedmove$color = null)
  460.     {
  461.         if (is_null($color)) {
  462.             $color $this->_move;
  463.         }
  464.         switch ($parsedmove['piece']{
  465.             case 'K' :
  466.                 if (in_array($parsedmove['square'],
  467.                     $this->getPossibleKingMoves($king $this->_getKing($color)$color))) {
  468.                     return $king;
  469.                 }
  470.             break;
  471.             case 'Q' :
  472.             case 'B' :
  473.             case 'R' :
  474.             case 'N' :
  475.                 if ($parsedmove['disambiguate']{
  476.                     if (strlen($parsedmove['disambiguate']== 2{
  477.                         $square $parsedmove['disambiguate'];
  478.                     elseif (is_numeric($parsedmove['disambiguate'])) {
  479.                         $row $parsedmove['disambiguate'];
  480.                     else {
  481.                         $col $parsedmove['disambiguate'];
  482.                     }
  483.                 else {
  484.                     $others = array();
  485.                     $others $this->_getAllPieceSquares($parsedmove['piece'],
  486.                                                          $color);
  487.                     $disambiguate '';
  488.                     $ambiguous = array();
  489.                     if (count($others)) {
  490.                         foreach ($others as $square{
  491.                             if (in_array($parsedmove['square'],
  492.                                     $this->getPossibleMoves($parsedmove['piece'],
  493.                                                             $square,
  494.                                                             $color))) {
  495.                                 // other pieces can move to this square - need to disambiguate
  496.                                 $ambiguous[$square;
  497.                             }
  498.                         }
  499.                     }
  500.                     if (count($ambiguous> 1{
  501.                         $pieces implode($ambiguous' ');
  502.                         return $this->raiseError(
  503.                             GAMES_CHESS_ERROR_TOO_AMBIGUOUS,
  504.                             array('san' => $parsedmove['piece'.
  505.                                 $parsedmove['disambiguate'$parsedmove['takes']
  506.                                 . $parsedmove['square'],
  507.                                   'squares' => $pieces,
  508.                                   'piece' => $parsedmove['piece']));
  509.                     }
  510.                     $square $col $row = null;
  511.                 }
  512.                 $potentials = array();
  513.                 foreach ($this->_pieces[$color]['P'as $name => $value{
  514.                     if (isset($square)) {
  515.                         if ($value[0== $square &&
  516.                               $value[1== $parsedmove['piece']{
  517.                             return $square;
  518.                         }
  519.                     elseif (isset($col)) {
  520.                         if ($value[0]{0== $col &&
  521.                               $value[1== $parsedmove['piece']{
  522.                             if (in_array($parsedmove['square'],
  523.                                   $this->getPossibleMoves($parsedmove['piece'],
  524.                                                             $value[0]$color))) {
  525.                                 $potentials[$value[0];
  526.                             }
  527.                         }
  528.                     elseif (isset($row)) {
  529.                         if ($value[0]{1== $row &&
  530.                               $value[1== $parsedmove['piece']{
  531.                             if (in_array($parsedmove['square'],
  532.                                   $this->getPossibleMoves($parsedmove['piece'],
  533.                                                             $value[0]$color))) {
  534.                                 $potentials[$value[0];
  535.                             }
  536.                         }
  537.                     else {
  538.                         if ($value[1== $parsedmove['piece']{
  539.                             if (in_array($parsedmove['square'],
  540.                                   $this->getPossibleMoves($parsedmove['piece'],
  541.                                                             $value[0]$color))) {
  542.                                 $potentials[$value[0];
  543.                             }
  544.                         }
  545.                     }
  546.                 }
  547.                 foreach ($this->_pieces[$color][$parsedmove['piece']] as $name => $value{
  548.                     if (isset($square)) {
  549.                         if ($value == $square{
  550.                             return $square;
  551.                         }
  552.                     elseif (isset($col)) {
  553.                         if ($value{0== $col{
  554.                             if (in_array($parsedmove['square'],
  555.                                   $this->getPossibleMoves($parsedmove['piece'],
  556.                                                             $value$color))) {
  557.                                 $potentials[$value;
  558.                             }
  559.                         }
  560.                     elseif (isset($row)) {
  561.                         if ($value{1== $row{
  562.                             if (in_array($parsedmove['square'],
  563.                                   $this->getPossibleMoves($parsedmove['piece'],
  564.                                                             $value$color))) {
  565.                                 $potentials[$value;
  566.                             }
  567.                         }
  568.                     else {
  569.                         if (in_array($parsedmove['square'],
  570.                               $this->getPossibleMoves($parsedmove['piece'],
  571.                                                         $value$color))) {
  572.                             $potentials[$value;
  573.                         }
  574.                     }
  575.                 }
  576.                 if (count($potentials== 1{
  577.                     return $potentials[0];
  578.                 }
  579.             break;
  580.             case 'P' :
  581.                 if ($parsedmove['disambiguate']{
  582.                     $square $parsedmove['disambiguate'$parsedmove['takesfrom'];
  583.                 else {
  584.                     $square = null;
  585.                 }
  586.                 if ($parsedmove['takesfrom']{
  587.                     $col $parsedmove['takesfrom'];
  588.                 else {
  589.                     $col = null;
  590.                 }
  591.                 $potentials = array();
  592.                 foreach ($this->_pieces[$color]['P'as $name => $value{
  593.                     if (isset($square)) {
  594.                         if ($value[0== $square && $value[1== 'P'{
  595.                             return $square;
  596.                         }
  597.                     elseif (isset($col)) {
  598.                         if ($value[0]{0== $col && $value[1== 'P'{
  599.                             if (in_array($parsedmove['square'],
  600.                                   $this->getPossiblePawnMoves($value[0]$color))) {
  601.                                 $potentials[$value[0];
  602.                             }
  603.                         }
  604.                     else {
  605.                         if ($value[1== 'P'{
  606.                             if (in_array($parsedmove['square'],
  607.                                   $this->getPossiblePawnMoves($value[0]$color))) {
  608.                                 $potentials[$value[0];
  609.                             }
  610.                         }
  611.                     }
  612.                 }
  613.                 if (count($potentials== 1{
  614.                     return $potentials[0];
  615.                 }
  616.             break;
  617.         }
  618.         if ($parsedmove['piece'== 'P'{
  619.             $san $parsedmove['takesfrom'$parsedmove['takes'$parsedmove['square'];
  620.         else {
  621.             $san $parsedmove['piece'.
  622.                            $parsedmove['disambiguate'$parsedmove['takes'.
  623.                            $parsedmove['square'];
  624.         }
  625.         return $this->raiseError(GAMES_CHESS_ERROR_NOPIECE_CANDOTHAT,
  626.             array('san' => $san,
  627.                   'color' => $color));
  628.     }
  629.  
  630.     /**
  631.      * Get the location of the king
  632.      *
  633.      * assumes valid color input
  634.      * @return false|string
  635.      * @access protected
  636.      */
  637.     function _getKing($color = null)
  638.     {
  639.         if (!is_null($color)) {
  640.             if (!isset($this->_pieces[$color]['K'][0])) {
  641.                 return false;
  642.             }
  643.             return $this->_pieces[$color]['K'][0];
  644.         else {
  645.             if (!isset($this->_pieces[$this->_move]['K'][0])) {
  646.                 return false;
  647.             }
  648.             return $this->_pieces[$this->_move]['K'][0];
  649.         }
  650.     }
  651.  
  652.     /**
  653.      * Get the location of a piece
  654.      *
  655.      * This does NOT take an algebraic square as the argument, but the contents
  656.      * of _board[algebraic square]
  657.      * @param string 
  658.      * @return string|array
  659.      * @access protected
  660.      */
  661.     function _getPiece($piece)
  662.     {
  663.         if (!isset($this->_pieces[$piece{0}][$piece{1}][$piece{2}])) {
  664.             return false;
  665.         }
  666.         return $piece{1== 'P' ?
  667.             $this->_pieces[$piece{0}][$piece{1}][$piece{2}][0:
  668.             $this->_pieces[$piece{0}][$piece{1}][$piece{2}];
  669.     }
  670.  
  671.     /**
  672.      * Determine whether a piece name is a knight
  673.      *
  674.      * This does NOT take an algebraic square as the argument, but the contents
  675.      * of _board[algebraic square]
  676.      * @param string 
  677.      * @return boolean 
  678.      * @access protected
  679.      */
  680.     function _isKnight($piece)
  681.     {
  682.         if (!isset($this->_pieces[$piece{0}][$piece{1}][$piece{2}])) {
  683.             return false;
  684.         }
  685.         return $piece{1== 'N' ||
  686.             ($piece{1== 'P' &&
  687.                 $this->_pieces[$piece{0}][$piece{1}][$piece{2}][1== 'N');
  688.     }
  689.  
  690.    /**
  691.      * Determine whether a piece name is a king
  692.      *
  693.      * This does NOT take an algebraic square as the argument, but the contents
  694.      * of _board[algebraic square]
  695.      * @param string 
  696.      * @return boolean 
  697.      * @access protected
  698.      */
  699.     function isKing($piecename)
  700.     {
  701.         if ($piecename{2!= '0'{
  702.             return false;
  703.         }
  704.         return $piecename{1== 'K';
  705.     }
  706.  
  707.     /**
  708.      * Determine whether a piece name is a queen
  709.      *
  710.      * This does NOT take an algebraic square as the argument, but the contents
  711.      * of _board[algebraic square]
  712.      * @param string 
  713.      * @return boolean 
  714.      * @access protected
  715.      */
  716.     function _isQueen($piece)
  717.     {
  718.         if (!isset($this->_pieces[$piece{0}][$piece{1}][$piece{2}])) {
  719.             return false;
  720.         }
  721.         return $piece{1== 'Q' ||
  722.             ($piece{1== 'P' &&
  723.                 $this->_pieces[$piece{0}][$piece{1}][$piece{2}][1== 'Q');
  724.     }
  725.  
  726.     /**
  727.      * Determine whether a piece name is a bishop
  728.      *
  729.      * This does NOT take an algebraic square as the argument, but the contents
  730.      * of _board[algebraic square]
  731.      * @param string 
  732.      * @return boolean 
  733.      */
  734.     function isBishop($piece)
  735.     {
  736.         if (!isset($this->_pieces[$piece{0}][$piece{1}][$piece{2}])) {
  737.             return false;
  738.         }
  739.         return $piece{1== 'B' ||
  740.             ($piece{1== 'P' &&
  741.                 $this->_pieces[$piece{0}][$piece{1}][$piece{2}][1== 'B');
  742.     }
  743.  
  744.     /**
  745.      * Determine whether a piece name is a rook
  746.      *
  747.      * This does NOT take an algebraic square as the argument, but the contents
  748.      * of _board[algebraic square]
  749.      * @param string 
  750.      * @return boolean 
  751.      */
  752.     function isRook($piece)
  753.     {
  754.         if (!isset($this->_pieces[$piece{0}][$piece{1}][$piece{2}])) {
  755.             return false;
  756.         }
  757.         return $piece{1== 'R' ||
  758.             ($piece{1== 'P' &&
  759.                 $this->_pieces[$piece{0}][$piece{1}][$piece{2}][1== 'R');
  760.     }
  761.  
  762.     /**
  763.      * Determine whether a piece name is a pawn
  764.      *
  765.      * This does NOT take an algebraic square as the argument, but the contents
  766.      * of _board[algebraic square]
  767.      * @param string 
  768.      * @return boolean 
  769.      */
  770.     function isPawn($piece)
  771.     {
  772.         if (!isset($this->_pieces[$piece{0}][$piece{1}][$piece{2}])) {
  773.             return false;
  774.         }
  775.         return $piece{1== 'P' &&
  776.                 $this->_pieces[$piece{0}][$piece{1}][$piece{2}][1== 'P';
  777.     }
  778.     
  779.     /**
  780.      * Determine whether it is possible to capture the piece delivering check,
  781.      * or to interpose a piece in between the checking piece and the king
  782.      * @param array squares that will block a checkmate
  783.      * @param W|Bcolor of the side attempting to prevent checkmate
  784.      * @return boolean true if it is possible to remove check
  785.      */
  786.     function _interposeOrCapture($squares$color)
  787.     {
  788.         foreach ($squares as $square{
  789.             // if any squares are unoccupied, and we can place a piece,
  790.             // then it is possible to interpose through piece placement
  791.             if (!$this->_squareToPiece($square)) {
  792.                 foreach ($this->_captured[$coloras $name => $count{
  793.                     if (!$count{
  794.                         continue;
  795.                     }
  796.                     if ($name == 'P'{
  797.                         // can't place on 1 or 8
  798.                         if ($square[1== '1' || $square[1== '8'{
  799.                             continue;
  800.                         }
  801.                     }
  802.                     return true;
  803.                 }
  804.             }
  805.         }
  806.         // placement is not possible, try regular interpose/capture
  807.         foreach ($this->_pieces[$coloras $name => $pieces{
  808.             if ($name == 'K'{
  809.                 continue;
  810.             }
  811.             foreach ($pieces as $value{
  812.                 if (is_array($value)) {
  813.                     $name $value[1];
  814.                     $value $value[0];
  815.                 }
  816.                 $allmoves $this->getPossibleMoves($name$value$color);
  817.                 foreach($squares as $square{
  818.                     if (in_array($square$allmoves)) {
  819.                         // try the move, see if we're still in check
  820.                         // if so, then the piece is pinned and cannot move
  821.                         $this->startTransaction();
  822.                         $this->_move $color;
  823.                         if (!class_exists('PEAR')) {
  824.                             require_once 'PEAR.php';
  825.                         }
  826.                         PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
  827.                         $ret $this->moveSquare($value$square);
  828.                         PEAR::popErrorHandling(PEAR_ERROR_RETURN);
  829.                         $this->_move $color;
  830.                         $stillchecked $this->inCheck($color);
  831.                         $this->rollbackTransaction();
  832.                         if (!$stillchecked{
  833.                             return true;
  834.                         }
  835.                     }
  836.                 }
  837.             }
  838.         }
  839.         return false;
  840.     }
  841.  
  842.     /**
  843.      * Get a list of all pieces on the board organized by the type of piece,
  844.      * and the color of the square the piece is on.
  845.      *
  846.      * Used to determine basic draw conditions
  847.      * @return array Format:
  848.      *
  849.      *  <pre>
  850.      *  array(
  851.      *    // white pieces
  852.      *    'W' => array('B' => array('W', 'B'), // all bishops
  853.      *                 'K' => array('W'),...
  854.      *                ),
  855.      *    // black pieces
  856.      *    'B' => array('Q' => array('B'), // all queens
  857.      *                 'K' => array('W'),... // king is on white square
  858.      *  </pre>
  859.      * @access protected
  860.      */
  861.     function _getPieceTypes()
  862.     {
  863.         $ret = array('W' => array()'B' => array());
  864.         foreach($this->_pieces as $color => $all{
  865.             foreach ($all as $name => $pieces{
  866.                 foreach ($pieces as $loc{
  867.                     if (is_array($loc)) {
  868.                         $name $loc[1];
  869.                         $loc $loc[0];
  870.                     }
  871.                     $ret[$color][$name][$this->_getDiagonalColor($loc);
  872.                 }
  873.             }
  874.         }
  875.         return $ret;
  876.     }
  877.  
  878.     /**
  879.      * Used to determine check
  880.      *
  881.      * Retrieve all of the moves of the pieces matching the color passed in.
  882.      * @param W|B
  883.      * @return array 
  884.      * @access protected
  885.      */
  886.     function _getPossibleChecks($color)
  887.     {
  888.         $ret = array();
  889.         foreach ($this->_pieces[$coloras $name => $pieces{
  890.             foreach ($pieces as $i => $loc{
  891.                 if ($name == 'P'{
  892.                     $ret[$color $name $i$this->getPossibleMoves($loc[1]$loc[0]$colorfalse);
  893.                 else {
  894.                     $ret[$color $name $i$this->getPossibleMoves($name$loc$colorfalse);
  895.                 }
  896.             }
  897.         }
  898.         return $ret;
  899.     }
  900.  
  901.     /**
  902.      * Get the location of every piece on the board of color $color
  903.      * @access protected
  904.      * @param W|Bcolor of pieces to check
  905.      */
  906.     function _getAllPieceLocations($color)
  907.     {
  908.         $ret = array();
  909.         foreach ($this->_pieces[$coloras $name => $pieces{
  910.             foreach ($pieces as $loc{
  911.                 $where =  (is_array($loc$loc[0$loc);
  912.                 $ret[$where;
  913.             }
  914.         }
  915.         return $ret;
  916.     }
  917.  
  918.     /**
  919.      * Render the current board position into Forsyth-Edwards Notation
  920.      *
  921.      * This method only renders the board contents, not the castling and other
  922.      * information
  923.      * @return string 
  924.      * @access protected
  925.      */
  926.     function _renderFen()
  927.     {
  928.         $fen '';
  929.         $ws = 0;
  930.         $saverow '8';
  931.         foreach ($this->_board as $square => $piece{
  932.             if ($square{1!= $saverow{
  933.                 // if we have just moved to the next rank,
  934.                 // output any whitespace, and a '/'
  935.                 if ($ws{
  936.                     $fen .= $ws;
  937.                 }
  938.                 $fen .= '/';
  939.                 $ws = 0;
  940.                 $saverow $square{1};
  941.             }
  942.             if ($square == $piece{
  943.                 // increment whitespace - no piece on this square
  944.                 $ws++;
  945.             else {
  946.                 // add any whitespace and reset
  947.                 if ($ws{
  948.                     $fen .= $ws;
  949.                 }
  950.                 $ws = 0;
  951.                 if (is_array($this->_pieces[$piece{0}][$piece{1}][$piece{2}])) {
  952.                     // add pawns/promoted pawns
  953.                     $p ($piece{0== 'W'$this->_pieces[$piece{0}][$piece{1}][$piece{2}][1:
  954.                         strtolower($this->_pieces[$piece{0}][$piece{1}][$piece{2}][1]);
  955.                 else {
  956.                     // add pieces
  957.                     $p ($piece{0== 'W'$piece{1strtolower($piece{1});
  958.                 }
  959.                 $fen .= $p;
  960.             }
  961.         }
  962.         // add any trailing whitespace
  963.         if ($ws{
  964.             $fen .= $ws;
  965.         }
  966.         return $fen;
  967.     }
  968.  
  969.     /**
  970.      * Determine whether one side's king is in check by the other side's pieces
  971.      * @param W|Bcolor of pieces to determine enemy check
  972.      * @return string|array|falsesquare of checking piece(s) or false
  973.      */
  974.     function inCheck($color)
  975.     {
  976.         $ret = array();
  977.         $king $this->_getKing($color);
  978.         $possible $this->_getPossibleChecks($color == 'W' 'B' 'W');
  979.         foreach ($possible as $piece => $squares{
  980.             if (in_array($king$squares)) {
  981.                 $loc $this->_pieces[$piece{0}][$piece{1}][$piece{2}];
  982.                 $ret[is_array($loc$loc[0$loc;
  983.             }
  984.         }
  985.         if (!count($ret)) {
  986.             return false;
  987.         }
  988.         if (count($ret== 1{
  989.             return $ret[0];
  990.         }
  991.         return $ret;
  992.     }
  993.  
  994.     function toArray()
  995.     {
  996.         $ret = array();
  997.         foreach ($this->_board as $square => $piece{
  998.             if ($piece == $square{
  999.                 $ret[$square= false;
  1000.                 continue;
  1001.             }
  1002.             $lower $piece{0};
  1003.             if (is_array($this->_pieces[$piece{0}][$piece{1}][$piece{2}])) {
  1004.                 $piece $this->_pieces[$piece{0}][$piece{1}][$piece{2}][1];
  1005.             else {
  1006.                 $piece $piece{1};
  1007.             }
  1008.             if ($lower == 'B'{
  1009.                 $piece strtolower($piece);
  1010.             }
  1011.             $ret[$square$piece;
  1012.         }
  1013.         uksort($retarray($this'_sortToArray'));
  1014.         return array('board' => $ret'captured' => $this->_captured);
  1015.     }
  1016.  
  1017.     /**
  1018.      * Add a piece to the chessboard
  1019.      * @param W|Bpiece color
  1020.      * @param K|Q|R|N|P|BPiece type
  1021.      * @param string [a-h][1-8] algebraic location of piece
  1022.      * @return true|PEAR_Error
  1023.      * @throws GAMES_CHESS_ERROR_INVALIDSQUARE
  1024.      * @throws GAMES_CHESS_ERROR_DUPESQUARE
  1025.      * @throws GAMES_CHESS_ERROR_MULTIPIECE
  1026.      */
  1027.     function addPiece($color$type$square)
  1028.     {
  1029.         if (!isset($this->_board[$square])) {
  1030.             return $this->raiseError(GAMES_CHESS_ERROR_INVALIDSQUARE,
  1031.                 array('square' => $square));
  1032.         }
  1033.         if ($this->_board[$square!= $square{
  1034.             $dpiece $this->_board[$square];
  1035.             if ($dpiece{1== 'P'{
  1036.                 $dpiece $this->_pieces[$dpiece{0}][$dpiece{1}][$dpiece{2}][1];
  1037.             else {
  1038.                 $dpiece $dpiece{1};
  1039.             }
  1040.             return $this->raiseError(GAMES_CHESS_ERROR_DUPESQUARE,
  1041.                 array('piece' => $type'dpiece' => $dpiece'square' => $square));
  1042.         }
  1043.         switch ($type{
  1044.             case 'B' :
  1045.             case 'N' :
  1046.             case 'R' :
  1047.             case 'Q' :
  1048.                 $addas $this->_canAddPiece($type$color);
  1049.                 if (!$addas{
  1050.                     return $this->raiseError(GAMES_CHESS_ERROR_MULTIPIECE,
  1051.                         array('color' => $color'piece' => $type));
  1052.                 }
  1053.                 if ($addas[0== 'P'{
  1054.                     $add = array($square$type);
  1055.                 else {
  1056.                     $add $square;
  1057.                 }
  1058.                 if ($addas[1== 2{
  1059.                     // using a captured piece to place, so decrease captured count
  1060.                     $this->_captured[$color][$type]--;
  1061.                 }
  1062.                 $this->_pieces[$color][$addas[0]][$add;
  1063.                 $this->_board[$square$color $addas[0.
  1064.                     (count($this->_pieces[$color][$addas[0]]- 1);
  1065.             break;
  1066.             case 'P' :
  1067.                 $addas $this->_canAddPiece($type$color);
  1068.                 if (!$addas{
  1069.                     return $this->raiseError(GAMES_CHESS_ERROR_MULTIPIECE,
  1070.                         array('color' => $color'piece' => $type));
  1071.                 }
  1072.                 if ($addas[1== 2{
  1073.                     // using a captured pawn to place, so decrease captured count
  1074.                     $this->_captured[$color]['P']--;
  1075.                 }
  1076.                 // handle regular pawns
  1077.                 $this->_pieces[$color]['P'][=
  1078.                     array($square'P');
  1079.                 $this->_board[$square$color 'P' (count($this->_pieces[$color]['P']- 1);
  1080.             break;
  1081.             case 'K' :
  1082.                 if (!isset($this->_pieces[$color]['K'][0])) {
  1083.                     $this->_pieces[$color]['K'][0$square;
  1084.                     $this->_board[$square$color 'K0';
  1085.                 else {
  1086.                     return $this->raiseError(GAMES_CHESS_ERROR_MULTIPIECE,
  1087.                         array('color' => $color'piece' => $type));
  1088.                 }
  1089.             break;
  1090.         }
  1091.         return true;
  1092.     }
  1093.  
  1094.     /**
  1095.      * Determine whether we can add a piece to the board legally
  1096.      *
  1097.      * A piece can be added if it meets any of these conditions in this order:
  1098.      * 
  1099.      *  1. it is one of the existing pieces, and has not already been placed
  1100.      *     on the enemy side
  1101.      *  2. it can be created from a promoted pawn
  1102.      *  3. it can be placed from captured enemy pieces
  1103.      *  4. the enemy piece can be "captured" (is not present on the board)
  1104.      * @param P|Q|R|N$piece 
  1105.      * @return false|stringeither the name of the piece to add this as, or false if no room
  1106.      */
  1107.     function _canAddPiece($piece$color)
  1108.     {
  1109.         $enemy $color == 'W' 'B' 'W';
  1110.         $possible = array(
  1111.             'P' => 8,
  1112.             'Q' => 1,
  1113.             'R' => 2,
  1114.             'N' => 2,
  1115.             'B' => 2,
  1116.         );
  1117.         // determine if the enemy has captured any of our pieces and placed them
  1118.         $total count($this->_pieces[$enemy][$piece]$possible[$piece];
  1119.         if ($total < 0{
  1120.             // we only care about captured pieces that have been placed
  1121.             $total = 0;
  1122.         }
  1123.         // add the number of these pieces the enemy has captured
  1124.         $total += $this->_captured[$enemy][$piece];
  1125.         // add the number of this piece (not promoted pawns) we have on the board
  1126.         $total += count($this->_pieces[$color][$piece]);
  1127.         if ($total $possible[$piece]{
  1128.             // can add it as a normal piece
  1129.             return array($piece1);
  1130.         }
  1131.         
  1132.         // try promotion next
  1133.         if ($piece != 'P'{
  1134.             do {
  1135.                 // only non-pawns can be promoted to a pawn
  1136.                 // extract the number of placed captured pawns on the enemy's side
  1137.                 $ptotal count($this->_pieces[$enemy]['P']- 8;
  1138.                 if ($ptotal < 0{
  1139.                     $ptotal = 0;
  1140.                 }
  1141.                 $ptotal += count($this->_pieces[$color]['P']+
  1142.                     $this->_captured[$enemy]['P'];
  1143.                 if ($ptotal == 8{
  1144.                     // no space available for promoted pawns
  1145.                     break;
  1146.                 }
  1147.                 // add it as a pawn
  1148.                 return array('P'1);
  1149.             while (false);
  1150.         }
  1151.         if ($this->_captured[$color][$piece]{
  1152.             // determine whether we have captured any enemy pieces of this type
  1153.             $total += $this->_captured[$color][$piece];
  1154.             if ($total $possible[$piece* 2{
  1155.                 // allowed, through placement move
  1156.                 return array($piece2);
  1157.             }
  1158.         }
  1159.         if (count($this->_pieces[$color][$piece]$this->_captured[$enemy][$piece==
  1160.               2 * $possible[$piece]{
  1161.             // full - we've captured/placed all enemy pieces and promoted
  1162.             // all pawns as well
  1163.             return false;
  1164.         }
  1165.         if (count($this->_pieces[$enemy][$piece]$possible[$piece]{
  1166.             // simulate a capture followed by placement
  1167.             return array($piece3);
  1168.         }
  1169.         return false;
  1170.     }
  1171.  
  1172.     /**
  1173.      * Basic draw is impossible in crazyhouse, because it is always possible
  1174.      * to place another piece
  1175.      * @return false 
  1176.      */
  1177.     function inBasicDraw()
  1178.     {
  1179.         return false;
  1180.     }
  1181.  
  1182.     /**
  1183.      * Repetition draw is not allowed in crazyhouse
  1184.      * @return false 
  1185.      */
  1186.     function inRepetitionDraw()
  1187.     {
  1188.         return false;
  1189.     }
  1190.  
  1191.     /**
  1192.      * 50 move draw is not allowed in crazyhouse
  1193.      * @return false 
  1194.      */
  1195.     function in50MoveDraw()
  1196.     {
  1197.         return false;
  1198.     }
  1199. }
  1200. ?>

Documentation generated on Sun, 17 Jun 2007 02:00:49 -0400 by phpDocumentor 1.3.2. PEAR Logo Copyright © PHP Group 2004.