protected function createPersistableState(GameInterface $game, GameState $gameState, $class) { $persistableGameState = $this->createPersistableGameStateObject($class); $squares = []; foreach ($game->getBoard()->getSquares() as $square) { $squareData = ['position' => $square->getPosition(), 'piece' => null]; if ($piece = $square->getPiece()) { $squareData['piece'] = ['id' => $piece->getId(), 'type' => $piece->getType(), 'color' => $piece->getColor()]; } $squares[] = $squareData; } $persistableGameState->setSquares($squares); //$lastMove = $this->createMoveFromGameState($gameState); $lastMove = $game->getLastMove(); if ($lastMove !== null) { $persistableGameState->setFrom($lastMove->getFrom()); $persistableGameState->setTo($lastMove->getTo()); $persistableGameState->setPiece(['id' => $lastMove->getPiece()->getId(), 'type' => $lastMove->getPiece()->getType(), 'color' => $lastMove->getPiece()->getColor()]); if (null !== ($capture = $lastMove->getCapture())) { $persistableGameState->setCapture(['captured_id' => $capture->getCapturedPiece()->getId(), 'captured_type' => $capture->getCapturedPiece()->getType(), 'captured_color' => $capture->getCapturedPiece()->getColor(), 'capturing_id' => $capture->getPiece()->getId(), 'capturing_type' => $capture->getPiece()->getType(), 'capturing_color' => $capture->getPiece()->getColor(), 'position' => $capture->getPosition()]); } $persistableGameState->setMoveType($lastMove->getType()); $persistableGameState->setColor($lastMove->getPiece()->getColor()); } return $persistableGameState; }
/** * {@inheritdoc} */ public function save(GameInterface $game) { $id = $game->getId(); if ($id === null) { $id = $this->generateId(); } $filePath = $this->getPathToFile($id); $fh = fopen($filePath, 'w+'); fwrite($fh, json_encode($game)); fclose($fh); }
/** * {@inheritdoc} */ public function configureWalker(BoardWalker $walker, GameInterface $game) { $pawnMoves = $game->getMovesByPiece($game->getBoard()->getSquare($walker->getStartingPosition())->getPiece()->getId()); if (empty($pawnMoves)) { if ($game->getCurrentColor() === Color::WHITE && $walker->getRow() == 2 || $game->getCurrentColor() === Color::BLACK && $walker->getRow() == 7) { $newPosition = $walker->peek(BoardWalker::DIRECTION_FORWARD, 1); // allowed to make two steps forward if ($newPosition && !$game->getBoard()->getSquare($newPosition)->getPiece()) { $newPosition = $walker->peek(BoardWalker::DIRECTION_FORWARD, 2); if ($newPosition && !$game->getBoard()->getSquare($newPosition)->getPiece()) { // temporary check because walker does not seem to consider obstructing pieces of own color $walker->jump(BoardWalker::DIRECTION_FORWARD, 2)->restart(); } } } } $walker->forward(1, false)->restart(); $forwardLeftCapture = $walker->peek(BoardWalker::DIRECTION_FORWARDLEFT, 1, null, false, true); $forwardRightCapture = $walker->peek(BoardWalker::DIRECTION_FORWARDRIGHT, 1, null, false, true); if ($forwardLeftCapture !== null) { $walker->forwardLeft(1)->restart(); } if ($forwardRightCapture !== null) { $walker->forwardRight(1)->restart(); } $this->walkEnPassant($walker, $game->getLastMove(false)); }
/** * {@inheritdoc} */ public function save(GameInterface $game) { if ($game->getId() === null || null === ($persistableGame = $this->loadPersistable($game->getId()))) { $persistableGame = $this->createPersistable($game, $this->gameClassName); $this->managerRegistry->getManager()->persist($persistableGame); } $persistableGame->setFinished($game->hasFinished()); $persistableGame->setFinishedReason($game->getFinishedReason()); //$persistableGameState = $this->createPersistableState($game, $this->gameStateClassName, $game->getLastMove(true)); $persistableGameState = $this->createPersistableState($game, $game->getLastState(), $this->gameStateClassName); $persistableGame->addState($persistableGameState); $this->managerRegistry->getManager()->persist($persistableGameState); $this->managerRegistry->getManager()->flush(); return true; }
/** * {@inheritdoc} */ public function configureWalker(BoardWalker $walker, GameInterface $game) { $walker->omnidirectional(1); $kingPosition = BoardHelper::getKingPosition($game->getBoard(), $game->getCurrentColor()); $kingColumn = BoardHelper::getColumnFromPosition($kingPosition); $kingRow = BoardHelper::getRowFromPosition($kingPosition); $board = $game->getBoard(); $king = $board->getSquare($kingPosition)->getPiece(); $track = $kingPosition === SquareInterface::POSITION_E1; // check for possible castle move if (empty($game->getMovesByPiece($king->getId()))) { // see if one of the rooks did not make any moves $rookCriteria = ['piece_type' => PieceInterface::TYPE_ROOK, 'piece_color' => $game->getCurrentColor()]; foreach ($board->getPiecesBy($rookCriteria) as $rookPosition => $rook) { if (empty($game->getMovesByPiece($rook->getId()))) { // see if there are no pieces between the king and this rook $rookColumn = BoardHelper::getColumnFromPosition($rookPosition); $columnDiff = abs($kingColumn - $rookColumn) - 1; $track = $track === true && $columnDiff === 3; if ($columnDiff > 1) { $skip = false; for ($x = 1; $x <= $columnDiff; $x++) { if ($rookColumn > $kingColumn) { $cursor = $rookColumn - $x . $kingRow; } else { $cursor = $rookColumn + $x . $kingRow; } if (null !== ($piece = $board->getSquare($cursor)->getPiece())) { $skip = true; break; } } if ($skip) { continue; } if ($kingColumn > $rookColumn) { if ($game->getCurrentColor() === Color::WHITE) { $walker->jump(BoardWalker::DIRECTION_LEFT, 2); } else { $walker->jump(BoardWalker::DIRECTION_RIGHT, 2); } } else { if ($game->getCurrentColor() === Color::WHITE) { $walker->jump(BoardWalker::DIRECTION_RIGHT, 2); } else { $walker->jump(BoardWalker::DIRECTION_LEFT, 2); } } } } } } }
/** * @param GameInterface $game * * @return string * * @todo Add ascii for history, captures, players and clocks */ public static function toAscii(GameInterface $game) { return BoardDecorator::toAscii($game->getBoard()); }
/** * @param MoveInterface $move * @param GameInterface $game * * @throws InvalidMoveException */ private static function validate(MoveInterface $move, GameInterface $game) { if ($game->hasFinished()) { throw new InvalidMoveException('Game has already finished'); } if (null === ($piece = $move->getPiece())) { if (null === ($piece = $game->getBoard()->getSquare($move->getFrom())->getPiece())) { var_export($game->getBoard()->getSquaresAsString()); throw new InvalidMoveException(sprintf('There must be a piece to move on that position: %s', $move->getFromLabel())); } } if ($game->getCurrentColor() !== $piece->getColor()) { throw new InvalidMoveException(sprintf('You can only move your own pieces: the %s on %s belongs to %s', $piece->getTypeLabel(), $move->getFromLabel(), $game->getOpposingPlayer()->getColor())); } }
/** * @param string $notation * @param GameInterface $game * @param array $parsed * * @return int * * @throws InvalidNotationException */ private function parseFrom($notation, GameInterface $game, array $parsed) { $enPassant = false; $capture = false; switch ($parsed['piece_type']) { case PieceInterface::TYPE_PAWN: if (substr($notation, -4) === 'e.p.') { $enPassant = true; } elseif (substr($notation, 1, 1) === 'x') { // calculate 'from' for capture by pawn (diagonally) $capture = true; } else { // calculate 'from' for forward move by pawn } break; default: // non-pawn calculation, depends on type break; } $finalMove = null; $criteria = ['piece_color' => $parsed['color'], 'piece_type' => $parsed['piece_type']]; if (strlen($notation) > 3 && in_array(substr($notation, 1, 1), ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'])) { $criteria['column'] = BoardHelper::columnLetterToNumber(substr($notation, 1, 1)); } $possiblePieceMoves = $this->moveCalculator->possibleMovesTo($parsed['to'], $game->getBoard(), $criteria); foreach ($possiblePieceMoves as $move) { MoveHelper::enrich($move, $game); if ($enPassant === true && $move->getType() !== MoveInterface::TYPE_CAPTURE_EN_PASSANT) { continue; } elseif ($capture === true && $move->getType() !== MoveInterface::TYPE_CAPTURE) { continue; } if ($finalMove !== null) { throw new InvalidNotationException(sprintf('Multiple moves found starting from %s and ending on %s, you should make your notation more specific (criteria: %s)', $finalMove->getFromLabel(), $finalMove->getToLabel(), json_encode($criteria))); } $finalMove = $move; } if ($finalMove === null) { throw new InvalidNotationException(sprintf('There are no moves to make to this position: %s (criteria: %s)', $parsed['to'], json_encode($criteria, true))); } return $finalMove->getFrom(); }
/** * @param GameInterface $game * * @return array */ private function getExpectedGameArray(GameInterface $game) { return ['uid' => $game->getId(), 'white_player' => $this->getExpectedPlayerArray($game->getWhitePlayer()), 'black_player' => $this->getExpectedPlayerArray($game->getBlackPlayer()), 'finished' => $game->hasFinished(), 'finished_reason' => $game->getFinishedReason(), 'board' => $this->getExpectedBoardArray($game->getBoard()), 'current_color' => $game->getCurrentColor(), 'states' => $this->getExpectedGameStatesArray($game->getStates()->toArray())]; }