protected function assertGamePgn($pgn) { $dumper = new PgnDumper(); $dumped = $dumper->dumpGame($this->game); $pgn = str_replace('%date%', date('Y.m.d'), $pgn); $this->assertEquals($pgn, $dumped); }
/** * 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; }