Exemplo n.º 1
0
 public function getPiecePossibleMoves(Piece $piece)
 {
     $player = $piece->getPlayer();
     $isKingAttacked = $this->isKingAttacked($player);
     $allOpponentPieces = PieceFilter::filterNotClass(PieceFilter::filterAlive($player->getOpponent()->getPieces()), 'King');
     $king = $player->getKing();
     $kingSquareKey = $king->getSquareKey();
     $pieceOriginalX = $piece->getX();
     $pieceOriginalY = $piece->getY();
     $opponentPieces = PieceFilter::filterProjection($allOpponentPieces);
     //if we are not moving the king, and the king is not attacked, don't check pawns nor knights
     if (!$piece instanceof King && !$isKingAttacked) {
         $opponentPieces = PieceFilter::filterProjection($opponentPieces);
     }
     $squares = $this->board->keysToSquares($piece->getBasicTargetKeys());
     if ($piece instanceof King && !$isKingAttacked && !$piece->hasMoved()) {
         $squares = $this->addCastlingSquares($piece, $squares);
     }
     foreach ($squares as $it => $square) {
         // kings move to its target so we update its position
         if ($piece instanceof King) {
             $kingSquareKey = $square->getKey();
         }
         // kill opponent piece
         if ($killedPiece = $square->getPiece()) {
             $killedPiece->setIsDead(true);
         }
         $this->board->move($piece, $square->getX(), $square->getY());
         foreach ($opponentPieces as $opponentPiece) {
             if (null !== $killedPiece && $opponentPiece->getIsDead()) {
                 continue;
             }
             // if our king gets attacked
             if (in_array($kingSquareKey, $this->getPieceControlledKeys($opponentPiece, $piece instanceof King))) {
                 // can't go here
                 unset($squares[$it]);
                 break;
             }
         }
         $this->board->move($piece, $pieceOriginalX, $pieceOriginalY);
         // if a piece has been killed, bring it back to life
         if ($killedPiece) {
             $killedPiece->setIsDead(false);
             $this->board->add($killedPiece);
         }
     }
     return $this->board->squaresToKeys($squares);
 }
Exemplo n.º 2
0
 /**
  * Handle castling
  **/
 protected function castling(King $king, Square $to)
 {
     if (7 === $to->getX()) {
         $rookSquare = $to->getSquareByRelativePos(1, 0);
         $newRookSquare = $to->getSquareByRelativePos(-1, 0);
     } else {
         $rookSquare = $to->getSquareByRelativePos(-2, 0);
         $newRookSquare = $to->getSquareByRelativePos(1, 0);
     }
     $rook = $rookSquare->getPiece();
     $this->board->move($rook, $newRookSquare->getX(), $newRookSquare->getY());
     $rook->setFirstMove($this->game->getTurns());
     if ($this->stack) {
         $this->stack->add(array('type' => 'castling', 'from' => $rookSquare->getKey(), 'to' => $newRookSquare->getKey()));
     }
 }
Exemplo n.º 3
0
 /**
  * Move a piece on the board
  * Performs several validation before applying the move
  *
  * @param mixed $notation Valid algebraic notation (e.g. "a2 a4")
  * @return string PGN notation of the move
  */
 public function move($notation, array $options = array())
 {
     list($fromKey, $toKey) = explode(' ', $notation);
     if (!$fromKey || !$toKey) {
         throw new \InvalidArgumentException(sprintf('Manipulator:move game:%s, Invalid internal move notation "%s"', $this->game->getId(), $notation));
     }
     if (!($from = $this->board->getSquareByKey($fromKey))) {
         throw new \InvalidArgumentException(sprintf('Manipulator:move game:%s, Square ' . $fromKey . ' does not exist', $this->game->getId()));
     }
     if (!($to = $this->board->getSquareByKey($toKey))) {
         throw new \InvalidArgumentException(sprintf('Manipulator:move game:%s, Square ' . $toKey . ' does not exist', $this->game->getId()));
     }
     if (!($piece = $from->getPiece())) {
         throw new \InvalidArgumentException(sprintf('Manipulator:move game:%s, No piece on ' . $from, $this->game->getId()));
     }
     $player = $piece->getPlayer();
     if (!$player->isMyTurn()) {
         throw new \LogicException(sprintf('Manipulator:move game:%s, Can not play ' . $from . ' ' . $to . ' - Not ' . $piece->getColor() . ' player turn', $this->game->getId()));
     }
     $pieceClass = $piece->getClass();
     $isPlayerKingAttacked = $this->analyser->isKingAttacked($player);
     $playerPossibleMoves = $this->analyser->getPlayerPossibleMoves($player, $isPlayerKingAttacked);
     $possibleMoves = isset($playerPossibleMoves[$fromKey]) ? $playerPossibleMoves[$fromKey] : false;
     if (!$possibleMoves) {
         throw new \LogicException(sprintf('Manipulator:move game:%s, %s can not move', $this->game->getId(), $piece));
     }
     if (!in_array($toKey, $possibleMoves)) {
         throw new \LogicException(sprintf('Manipulator:move game:%s, %s can not go to ' . $to . ' (' . implode(',', $possibleMoves) . ')', $this->game->getId(), $piece));
     }
     // killed?
     $killed = $to->getPiece();
     // castling?
     $isCastling = false;
     if ('King' === $pieceClass) {
         // standard castling
         if (1 < abs($from->getX() - $to->getX())) {
             $isCastling = true;
         } elseif ($killed && $killed->getColor() === $piece->getColor()) {
             $isCastling = true;
             $killed = null;
         }
     }
     // promotion?
     if ('Pawn' === $pieceClass && $to->getY() === ($player->isWhite() ? 8 : 1)) {
         $isPromotion = true;
         $promotionClass = isset($options['promotion']) ? ucfirst($options['promotion']) : 'Queen';
         if (!in_array($promotionClass, array('Queen', 'Knight', 'Bishop', 'Rook'))) {
             throw new \InvalidArgumentException(sprintf('Manipulator:move game:%s, Bad promotion class: ' . $promotionClass, $this->game->getId()));
         }
         $options['promotion'] = $promotionClass;
     } else {
         $isPromotion = false;
     }
     // enpassant?
     $isEnPassant = 'Pawn' === $pieceClass && $to->getX() !== $from->getX() && !$killed;
     $pgnDumper = new PgnDumper();
     $pgn = $pgnDumper->dumpMove($this->game, $piece, $from, $to, $playerPossibleMoves, $killed, $isCastling, $isPromotion, $isEnPassant, $options);
     $this->stack->addEvent(array('type' => 'move', 'from' => $from->getKey(), 'to' => $to->getKey(), 'color' => $piece->getColor()));
     if ($isCastling) {
         $this->castle($piece, $to);
     } else {
         if ($killed) {
             $killed->setIsDead(true);
             $this->board->remove($killed);
         }
         $this->board->move($piece, $to->getX(), $to->getY());
         if (null === $piece->getFirstMove()) {
             $piece->setFirstMove($this->game->getTurns());
         }
     }
     if ($isPromotion) {
         $this->promotion($piece, $options['promotion']);
     }
     if ($isEnPassant) {
         $this->enpassant($piece, $to);
     }
     // When an irreversible event happens,
     // we can safely clear the game position hashes
     if ($killed || $isPromotion || $isCastling || 'Pawn' === $pieceClass) {
         $this->game->clearPositionHashes();
     }
     return $pgn;
 }
Exemplo n.º 4
0
 /**
  * @return array key => array keys
  */
 public function getPlayerPossibleMoves(Player $player, $isKingAttacked = null)
 {
     $possibleMoves = array();
     $isKingAttacked = null === $isKingAttacked ? $this->isKingAttacked($player) : $isKingAttacked;
     $allOpponentPieces = PieceFilter::filterAlive($player->getOpponent()->getPieces());
     $attackOpponentPieces = PieceFilter::filterNotClass($allOpponentPieces, 'King');
     $projectionOpponentPieces = PieceFilter::filterProjection($allOpponentPieces);
     $king = $player->getKing();
     foreach (PieceFilter::filterAlive($player->getPieces()) as $piece) {
         $kingSquareKey = $king->getSquareKey();
         $pieceOriginalX = $piece->getX();
         $pieceOriginalY = $piece->getY();
         $pieceClass = $piece->getClass();
         //if we are not moving the king, and the king is not attacked, don't check pawns nor knights
         if ('King' === $pieceClass) {
             $opponentPieces = $allOpponentPieces;
         } elseif ($isKingAttacked) {
             $opponentPieces = $attackOpponentPieces;
         } else {
             $opponentPieces = $projectionOpponentPieces;
         }
         $squares = $this->board->keysToSquares($piece->getBasicTargetKeys());
         foreach ($squares as $it => $square) {
             // king move to its target so we update its position
             if ('King' === $pieceClass) {
                 $kingSquareKey = $square->getKey();
             }
             // kill opponent piece
             if ($killedPiece = $square->getPiece()) {
                 $killedPiece->setIsDead(true);
             } elseif ('Pawn' === $pieceClass && $square->getX() !== $pieceOriginalX) {
                 $killedPiece = $square->getSquareByRelativePos(0, $player->isWhite() ? -1 : 1)->getPiece();
                 $killedPiece->setIsDead(true);
             }
             $this->board->move($piece, $square->getX(), $square->getY());
             foreach ($opponentPieces as $opponentPiece) {
                 if (null !== $killedPiece && $opponentPiece->getIsDead()) {
                     continue;
                 }
                 // if our king gets attacked
                 if (in_array($kingSquareKey, $opponentPiece->getAttackTargetKeys())) {
                     // can't go here
                     unset($squares[$it]);
                     break;
                 }
             }
             $this->board->move($piece, $pieceOriginalX, $pieceOriginalY);
             // if a piece has been killed, bring it back to life
             if ($killedPiece) {
                 $killedPiece->setIsDead(false);
                 $this->board->add($killedPiece);
             }
         }
         if ('King' === $pieceClass && !$isKingAttacked && !$piece->hasMoved()) {
             $squares = $this->addCastlingSquares($piece, $squares);
         }
         if (!empty($squares)) {
             $possibleMoves[$piece->getSquareKey()] = $this->board->squaresToKeys($squares);
         }
     }
     return $possibleMoves;
 }