From a912adc88c7560455697c20cac920ac7c616ceff Mon Sep 17 00:00:00 2001 From: Maxim Date: Thu, 24 Oct 2024 13:10:39 +0300 Subject: [PATCH 1/3] feat(Board): Incorrect color --- src/Board.php | 24 ++++++++++++++++++------ src/Figure.php | 14 +++++++++++--- 2 files changed, 29 insertions(+), 9 deletions(-) diff --git a/src/Board.php b/src/Board.php index 2b4bc6a..3ab79a8 100644 --- a/src/Board.php +++ b/src/Board.php @@ -1,9 +1,13 @@ figures['a'][1] = new Rook(false); $this->figures['b'][1] = new Knight(false); $this->figures['c'][1] = new Bishop(false); @@ -41,23 +45,31 @@ public function __construct() { $this->figures['h'][8] = new Rook(true); } - public function move($move) { + public function move($move) + { if (!preg_match('/^([a-h])(\d)-([a-h])(\d)$/', $move, $match)) { throw new \Exception("Incorrect move"); } $xFrom = $match[1]; $yFrom = $match[2]; - $xTo = $match[3]; - $yTo = $match[4]; + $xTo = $match[3]; + $yTo = $match[4]; if (isset($this->figures[$xFrom][$yFrom])) { + if ($this->lastMoveFigure === $this->figures[$xFrom][$yFrom]->isBlack()) { + throw new \Exception("Incorrect color"); + } + + $this->lastMoveFigure = $this->figures[$xFrom][$yFrom]->isBlack(); + $this->figures[$xTo][$yTo] = $this->figures[$xFrom][$yFrom]; } unset($this->figures[$xFrom][$yFrom]); } - public function dump() { + public function dump() + { for ($y = 8; $y >= 1; $y--) { echo "$y "; for ($x = 'a'; $x <= 'h'; $x++) { diff --git a/src/Figure.php b/src/Figure.php index 7cebd84..3cf7ba9 100644 --- a/src/Figure.php +++ b/src/Figure.php @@ -1,14 +1,22 @@ isBlack = $isBlack; } /** @noinspection PhpToStringReturnInspection */ - public function __toString() { + public function __toString() + { throw new \Exception("Not implemented"); } + + public function isBlack() + { + return $this->isBlack; + } } From 2975356c79b32c8fd1af330029c338fd595518db Mon Sep 17 00:00:00 2001 From: Maxim Date: Fri, 25 Oct 2024 15:58:44 +0300 Subject: [PATCH 2/3] refactor(Board): --- src/Board.php | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/src/Board.php b/src/Board.php index 3ab79a8..48f3910 100644 --- a/src/Board.php +++ b/src/Board.php @@ -4,7 +4,7 @@ class Board { private $figures = []; - private bool $lastMoveFigure = true; + private ?Figure $lastMoveFigure; public function __construct() { @@ -56,15 +56,22 @@ public function move($move) $xTo = $match[3]; $yTo = $match[4]; - if (isset($this->figures[$xFrom][$yFrom])) { - if ($this->lastMoveFigure === $this->figures[$xFrom][$yFrom]->isBlack()) { - throw new \Exception("Incorrect color"); - } + if (!isset($this->figures[$xFrom][$yFrom])) { + return; + } + + $movingFigure = $this->figures[$xFrom][$yFrom]; - $this->lastMoveFigure = $this->figures[$xFrom][$yFrom]->isBlack(); + $isBlackLastMoveFigure = isset($this->lastMoveFigure) ? $this->lastMoveFigure->isBlack() : true; - $this->figures[$xTo][$yTo] = $this->figures[$xFrom][$yFrom]; + if ($isBlackLastMoveFigure === $movingFigure->isBlack()) { + throw new \Exception("Incorrect color"); } + + $this->lastMoveFigure = $movingFigure; + + $this->figures[$xTo][$yTo] = $movingFigure; + unset($this->figures[$xFrom][$yFrom]); } From 295150882ca4c82a4e2e4d92b785febd8f47eb9b Mon Sep 17 00:00:00 2001 From: Maxim Date: Fri, 25 Oct 2024 20:19:33 +0300 Subject: [PATCH 3/3] feat(Rule): Added rule for the Pawn --- README.md | 28 ++++-- src/Bishop.php | 11 ++- src/Board.php | 11 +++ src/DirectionMovement.php | 18 ++++ src/Figure.php | 4 +- src/King.php | 11 ++- src/Knight.php | 11 ++- src/Path.php | 189 ++++++++++++++++++++++++++++++++++++++ src/Pawn.php | 64 ++++++++++++- src/Queen.php | 5 + src/Rook.php | 11 ++- src/TypeMoving.php | 10 ++ 12 files changed, 354 insertions(+), 19 deletions(-) create mode 100644 src/DirectionMovement.php create mode 100644 src/Path.php create mode 100644 src/TypeMoving.php diff --git a/README.md b/README.md index 0e74902..3b04412 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,10 @@ Например: - $ php chess.php e2-e4 e7-e5 +```bash +php chess.php e2-e4 e7-e5 +``` +``` 8 ♜♞♝♛♚♝♞♜ 7 ♟♟♟♟-♟♟♟ 6 -------- @@ -17,6 +20,8 @@ 2 ♙♙♙♙-♙♙♙ 1 ♖♘♗♕♔♗♘♖ abcdefgh +``` + В текущем виде `chess.php` никак не проверяет правильность ходов. @@ -27,7 +32,9 @@ Чтобы проверить корректность решения, запустите тесты: - $ ./vendor/bin/phpunit --group=rotation +```bash +./vendor/bin/phpunit --group=rotation +``` ## Задача 2 @@ -36,14 +43,19 @@ Чтобы проверить корректность решения, запустите тесты: - $ ./vendor/bin/phpunit --group=pawn +```bash +./vendor/bin/phpunit --group=pawn +``` В тестах проверяются только ходы пешками, для других фигур валидацию ходов делать не нужно. ### Как ходит пешка - * Пешка может ходить вперёд (по вертикали) на одну клетку; - * Если пешка ещё ни разу не ходила, она может пойти вперёд на две клетки; - * Пешка не может перепрыгивать через другие фигуры; - * Пешка может бить фигуры противника только по диагонали вперёд на одну клетку; - * Также существует взятие на проходе, но им можно пренебречь :) +* Пешка может ходить вперёд (по вертикали) на одну клетку; +* Если пешка ещё ни разу не ходила, она может пойти вперёд на две клетки; +* Пешка не может перепрыгивать через другие фигуры; +* Пешка может бить фигуры противника только по диагонали вперёд на одну клетку; +* Также существует взятие на проходе, но им можно пренебречь :) + +## Комментарии к выполнению +Класс Figure изменил на абстрактный класс и добавил абстрактный метод. Сделал так потому что если надо будет добавить правила для других фигур то это решение выглядит лучше чем добавления интерфейса каждой фигуре. Во всех фигурах кроме пешки метод для описания правил пустой. Логика работы метода следующая, если ход соответствует одной из проверок то метод просто завершает работу, если нет то метод выбрасывает исключение. В начале поставил базовую проверку (возможно избыточна) для проверки того что пешка не пошла более чем на 2 клетки. diff --git a/src/Bishop.php b/src/Bishop.php index fe68f85..b2f221a 100644 --- a/src/Bishop.php +++ b/src/Bishop.php @@ -1,7 +1,14 @@ isBlack ? '♝' : '♗'; } + + public function checkMove(Path $path) + { + // TODO: Implement checkMove() method. + } } diff --git a/src/Board.php b/src/Board.php index 48f3910..a0744c4 100644 --- a/src/Board.php +++ b/src/Board.php @@ -60,6 +60,7 @@ public function move($move) return; } + /** @var Figure $movingFigure */ $movingFigure = $this->figures[$xFrom][$yFrom]; $isBlackLastMoveFigure = isset($this->lastMoveFigure) ? $this->lastMoveFigure->isBlack() : true; @@ -68,6 +69,16 @@ public function move($move) throw new \Exception("Incorrect color"); } + $movingFigure->checkMove( + new Path( + figures: $this->figures, + xFrom: $xFrom, + yFrom: $yFrom, + xTo: $xTo, + yTo: $yTo + ) + ); + $this->lastMoveFigure = $movingFigure; $this->figures[$xTo][$yTo] = $movingFigure; diff --git a/src/DirectionMovement.php b/src/DirectionMovement.php new file mode 100644 index 0000000..5582920 --- /dev/null +++ b/src/DirectionMovement.php @@ -0,0 +1,18 @@ +isBlack; } + + abstract public function checkMove(Path $path); } diff --git a/src/King.php b/src/King.php index 3f223d6..817265e 100644 --- a/src/King.php +++ b/src/King.php @@ -1,7 +1,14 @@ isBlack ? '♚' : '♔'; } + + public function checkMove(Path $path) + { + // TODO: Implement checkMove() method. + } } diff --git a/src/Knight.php b/src/Knight.php index 0815830..6e51696 100644 --- a/src/Knight.php +++ b/src/Knight.php @@ -1,7 +1,14 @@ isBlack ? '♞' : '♘'; } + + public function checkMove(Path $path) + { + // TODO: Implement checkMove() method. + } } diff --git a/src/Path.php b/src/Path.php new file mode 100644 index 0000000..4c0ef4c --- /dev/null +++ b/src/Path.php @@ -0,0 +1,189 @@ + 1, + 'b' => 2, + 'c' => 3, + 'd' => 4, + 'e' => 5, + 'f' => 6, + 'g' => 7, + 'h' => 8, + ]; + + private bool $thereIsFigureOnPathMovement; + + private TypeMoving $typeMoving; + + private DirectionMovement $directionMovement; + + private int $numberOfCellsPassed = 0; + + private int $xIntFrom; + + private int $xIntTo; + + private int $xPath; + + private int $yPath; + + private bool $isThereAreFiguresOnWay = false; + + public function __construct( + private array $figures, + private string $xFrom, + private int $yFrom, + private string $xTo, + private int $yTo + ) { + $this->xIntFrom = static::HORIZONTAL[$this->xFrom]; + $this->xIntTo = static::HORIZONTAL[$this->xTo]; + + $this->xPath = abs($this->xIntFrom - $this->xIntTo); + $this->yPath = abs($this->yFrom - $this->yTo); + + $this->setTypeMoving(); + $this->setNumberOfCellsPassed(); + $this->setDirectionMovement(); + $this->setThereAreFiguresOnWay(); + } + + public function isThereAreFiguresOnWay(): bool + { + return $this->isThereAreFiguresOnWay; + } + + public function getNumberOfCellsPassed(): int + { + return $this->numberOfCellsPassed; + } + + public function getDirectionMovement(): DirectionMovement + { + return $this->directionMovement; + } + + private function setTypeMoving() + { + if ($this->xFrom === $this->xTo || $this->yFrom === $this->yTo) { + $this->typeMoving = TypeMoving::Direct; + } elseif ($this->xPath === $this->yPath) { + $this->typeMoving = TypeMoving::Diagonal; + } else { + $this->typeMoving = TypeMoving::Indefinite; + } + } + + public function getXFrom(): string + { + return $this->xFrom; + } + + public function getXTo(): string + { + return $this->xTo; + } + + public function getYFrom(): int + { + return $this->yFrom; + } + + public function getYTo(): int + { + return $this->yTo; + } + + private function setNumberOfCellsPassed() + { + if ($this->typeMoving === TypeMoving::Direct) { + $this->numberOfCellsPassed = max($this->xPath, $this->yPath); + } + if ($this->typeMoving === TypeMoving::Diagonal) { + $this->numberOfCellsPassed = $this->xPath; + } + } + + private function setDirectionMovement() + { + if ($this->typeMoving === TypeMoving::Direct) { + if ($this->xIntFrom > $this->xIntTo) { + $this->directionMovement = DirectionMovement::Left; + } elseif ($this->xIntFrom < $this->xIntTo) { + $this->directionMovement = DirectionMovement::Right; + } elseif ($this->yFrom < $this->yTo) { + $this->directionMovement = DirectionMovement::Up; + } elseif ($this->yFrom > $this->yTo) { + $this->directionMovement = DirectionMovement::Down; + } else { + $this->directionMovement = DirectionMovement::Indefinite; + } + } elseif ($this->typeMoving === TypeMoving::Diagonal) { + if ($this->xIntFrom < $this->xIntTo && $this->yFrom < $this->yTo) { + $this->directionMovement = DirectionMovement::UpRight; + } elseif ($this->xIntFrom > $this->xIntTo && $this->yFrom > $this->yTo) { + $this->directionMovement = DirectionMovement::DownRight; + } elseif ($this->xIntFrom < $this->xIntTo && $this->yFrom > $this->yTo) { + $this->directionMovement = DirectionMovement::DownLeft; + } elseif ($this->xIntFrom > $this->xIntTo && $this->yFrom < $this->yTo) { + $this->directionMovement = DirectionMovement::UpLeft; + } else { + $this->directionMovement = DirectionMovement::Indefinite; + } + } + } + + private function setThereAreFiguresOnWay() + { + if ($this->typeMoving === TypeMoving::Direct) { + if ($this->directionMovement === DirectionMovement::Up) { + for ($i = $this->yFrom + 1; $i <= $this->yTo; $i++) { + if (isset($this->figures[$this->xFrom][$i])) { + $this->isThereAreFiguresOnWay = true; + } + } + }elseif ($this->directionMovement === DirectionMovement::Down) { + for ($i = $this->yFrom - 1; $i >= $this->yTo; $i--) { + if (isset($this->figures[$this->xFrom][$i])) { + $this->isThereAreFiguresOnWay = true; + } + } + } + }elseif ($this->typeMoving === TypeMoving::Diagonal) { + $horizontalKey = array_flip(static::HORIZONTAL); + if ($this->directionMovement === DirectionMovement::UpLeft) { + for ($i = 1; $i <= $this->numberOfCellsPassed; $i++) { + $xValue = $horizontalKey[$this->xIntFrom - $i]; + if (isset($this->figures[$xValue][$this->yFrom + $i])) { + $this->isThereAreFiguresOnWay = true; + } + } + }elseif ($this->directionMovement === DirectionMovement::UpRight) { + for ($i = 1; $i <= $this->numberOfCellsPassed; $i++) { + $xValue = $horizontalKey[$this->xIntFrom + $i]; + if (isset($this->figures[$xValue][$this->yFrom + $i])) { + $this->isThereAreFiguresOnWay = true; + } + } + }elseif ($this->directionMovement === DirectionMovement::DownLeft) { + for ($i = 1; $i <= $this->numberOfCellsPassed; $i++) { + $xValue = $horizontalKey[$this->xIntFrom - $i]; + if (isset($this->figures[$xValue][$this->yFrom - $i])) { + $this->isThereAreFiguresOnWay = true; + } + } + }elseif ($this->directionMovement === DirectionMovement::DownRight) { + for ($i = 1; $i <= $this->numberOfCellsPassed; $i++) { + $xValue = $horizontalKey[$this->xIntFrom + $i]; + if (isset($this->figures[$xValue][$this->yFrom - $i])) { + $this->isThereAreFiguresOnWay = true; + } + } + } + } + } +} diff --git a/src/Pawn.php b/src/Pawn.php index 5c1a178..f545b00 100644 --- a/src/Pawn.php +++ b/src/Pawn.php @@ -1,7 +1,67 @@ isBlack ? '♟' : '♙'; } + + public function checkMove(Path $path) + { + if ($path->getNumberOfCellsPassed() > 2) { + throw new \Exception("A pawn moves a max of 2 squares"); + } + + $isBlackAndDown = $path->getDirectionMovement() === DirectionMovement::Down && $this->isBlack; + $isWhiteAndUp = $path->getDirectionMovement() === DirectionMovement::Up && !$this->isBlack; + $isDirectionSameAsColor = $isBlackAndDown || $isWhiteAndUp; + + //Пешка может ходить вперёд(по вертикали) на одну клетку; + if ($isDirectionSameAsColor + && $path->getNumberOfCellsPassed() === 1 + && !$path->isThereAreFiguresOnWay() + ) { + $this->notMoving = false; + return; + } + + //Если пешка ещё ни разу не ходила, она может пойти вперёд на две клетки; + if ($isDirectionSameAsColor + && $this->notMoving + && $path->getNumberOfCellsPassed() === 2 + && !$path->isThereAreFiguresOnWay() + ) { + $this->notMoving = false; + return; + } + + //Пешка может бить фигуры противника только по диагонали вперёд на одну клетку; + + $isBlackAndDownLeft = $path->getDirectionMovement() === DirectionMovement::DownLeft && $this->isBlack; + $isBlackAndDownRight = $path->getDirectionMovement() === DirectionMovement::DownRight && $this->isBlack; + + $isWhiteAndUpLeft = $path->getDirectionMovement() === DirectionMovement::UpLeft && !$this->isBlack; + $isWhiteAndUpRight = $path->getDirectionMovement() === DirectionMovement::UpRight && !$this->isBlack; + + $isDirectionSameAsColorForPunch = $isBlackAndDownLeft + || $isBlackAndDownRight + || $isWhiteAndUpLeft + || $isWhiteAndUpRight; + + if ( + $isDirectionSameAsColorForPunch + && $path->getNumberOfCellsPassed() === 1 + && $path->isThereAreFiguresOnWay() + ) { + $this->notMoving = false; + return; + } + + throw new \Exception( + "The move {$path->getXFrom()}{$path->getYFrom()}-{$path->getXTo()}{$path->getYTo()} with {$this->__toString()} is impossible" + ); + } } diff --git a/src/Queen.php b/src/Queen.php index 2e51be8..b9ec7cf 100644 --- a/src/Queen.php +++ b/src/Queen.php @@ -4,4 +4,9 @@ class Queen extends Figure { public function __toString() { return $this->isBlack ? '♛' : '♕'; } + + public function checkMove(Path $path) + { + // TODO: Implement checkMove() method. + } } diff --git a/src/Rook.php b/src/Rook.php index da39547..b36e30a 100644 --- a/src/Rook.php +++ b/src/Rook.php @@ -1,7 +1,14 @@ isBlack ? '♜' : '♖'; } + + public function checkMove(Path $path) + { + // TODO: Implement checkMove() method. + } } diff --git a/src/TypeMoving.php b/src/TypeMoving.php new file mode 100644 index 0000000..7725c73 --- /dev/null +++ b/src/TypeMoving.php @@ -0,0 +1,10 @@ +