From c43de25f9ec3bd994da3c0011ff841ed93bc1ab1 Mon Sep 17 00:00:00 2001 From: overflowerror Date: Sat, 6 Jan 2024 21:43:02 +0100 Subject: [PATCH] fix: En passant only works the move after --- src/Game/Game.php | 28 ++++++++---- src/Game/Pawn.php | 2 +- tests/Game/GameTest.php | 99 +++++++++++++++++++++++++++++++++++++++++ tests/Game/PawnTest.php | 33 ++++++++++++++ 4 files changed, 153 insertions(+), 9 deletions(-) diff --git a/src/Game/Game.php b/src/Game/Game.php index d0ddb1c..d3db711 100644 --- a/src/Game/Game.php +++ b/src/Game/Game.php @@ -171,7 +171,7 @@ class Game { if ($this->isCapture($target, $captureableForPawn)) { $candidate->captures = $this->findCapturedPiece($piece, $opponentPieces, $target); } - if ($piece->canPromote($target)) { + if ($piece instanceof Pawn && $piece->promotes($target)) { $candidates = array_merge($candidates, $this->generatePromotionMoves($candidate)); } else { $candidates[] = $candidate; @@ -216,23 +216,35 @@ class Game { $this->current, ); + $game->applyInPlace($move); + + return $game; + } + + private function tick(): void { + foreach ($this->pieces as $piece) { + $piece->tick(); + } + } + + public function applyInPlace(Move $move): void { + $this->tick(); + if ($move->captures) { - $game->removePiece($move->captures); + $this->removePiece($move->captures); } if ($move->promoteTo) { - $game->removePiece($move->piece); + $this->removePiece($move->piece); $promoted = $move->piece->promote($move->promoteTo); $promoted->move($move->target); - $game->pieces[] = $promoted; + $this->pieces[] = $promoted; } else { - $piece = $game->findPiece($move->piece); + $piece = $this->findPiece($move->piece); $piece->move($move->target); } - $game->current = $game->current->getNext(); - - return $game; + $this->current = $this->current->getNext(); } public function getGameState(bool $onlyIsLegal = false): GameState { diff --git a/src/Game/Pawn.php b/src/Game/Pawn.php index bc83e9c..0a8464a 100644 --- a/src/Game/Pawn.php +++ b/src/Game/Pawn.php @@ -71,7 +71,7 @@ class Pawn extends Piece { return $result; } - public function canPromote(Position $position): bool { + public function promotes(Position $position): bool { return ($this->side == Side::WHITE) ? ($position->rank == 7) : ($position->rank == 0); } } \ No newline at end of file diff --git a/tests/Game/GameTest.php b/tests/Game/GameTest.php index 5c36f51..7ec259f 100644 --- a/tests/Game/GameTest.php +++ b/tests/Game/GameTest.php @@ -301,4 +301,103 @@ final class GameTest extends TestCase { new Queen(new Position(1, 1), Side::WHITE), null, ), $legalMoves); } + + public function testLegalMoves_enPassantNotPossibleBecauseMoveInBetween() { + $subject = new Game( + [ + new King(new Position(0, 1), Side::BLACK), + new King(new Position(0, 7), Side::WHITE), + new Queen(new Position(1, 3), Side::WHITE), + new Pawn(new Position(5, 1), Side::WHITE), + new Pawn(new Position(6, 2), Side::WHITE), + new Pawn(new Position(6, 3), Side::BLACK, true), + ], + Side::WHITE + ); + $subject->applyInPlace(new Move( + new Pawn(new Position(5, 1), Side::WHITE), + new Position(5, 3) + )); + $subject->applyInPlace(new Move( + new King(new Position(0, 1), Side::BLACK), + new Position(0, 0) + )); + $subject->applyInPlace(new Move( + new Queen(new Position(1, 3), Side::WHITE), + new Position(1, 4) + )); + + $legalMoves = $subject->getLegalMoves(); + + $this->assertCount(1, $legalMoves); + + $this->assertContainsEqualsOnce(new Move( + new King(new Position(0, 0), Side::BLACK), + new Position(0, 1), + ), $legalMoves); + } + + public function testLegalMoves_enPassantNotPossibleBecausePawnDidntMove2Squares() { + $subject = new Game( + [ + new King(new Position(0, 0), Side::BLACK), + new King(new Position(0, 7), Side::WHITE), + new Queen(new Position(1, 3), Side::WHITE), + new Pawn(new Position(5, 1), Side::WHITE), + new Pawn(new Position(6, 2), Side::WHITE), + new Pawn(new Position(6, 3), Side::BLACK, true), + ], + Side::WHITE + ); + $subject->applyInPlace(new Move( + new Pawn(new Position(5, 1), Side::WHITE), + new Position(5, 2) + )); + $subject->applyInPlace(new Move( + new King(new Position(0, 0), Side::BLACK), + new Position(0, 1) + )); + $subject->applyInPlace(new Move( + new Pawn(new Position(5, 2), Side::WHITE), + new Position(5, 3) + )); + + $legalMoves = $subject->getLegalMoves(); + + $this->assertCount(1, $legalMoves); + + $this->assertContainsEqualsOnce(new Move( + new King(new Position(0, 1), Side::BLACK), + new Position(0, 0), + ), $legalMoves); + } + + public function testLegalMoves_enPassantPossible() { + $subject = new Game( + [ + new King(new Position(0, 0), Side::BLACK), + new King(new Position(0, 7), Side::WHITE), + new Queen(new Position(1, 2), Side::WHITE), + new Pawn(new Position(5, 1), Side::WHITE), + new Pawn(new Position(6, 2), Side::WHITE), + new Pawn(new Position(6, 3), Side::BLACK, true), + ], + Side::WHITE + ); + + $subject->applyInPlace(new Move( + new Pawn(new Position(5, 1), Side::WHITE), + new Position(5, 3) + )); + + $legalMoves = $subject->getLegalMoves(); + + $this->assertCount(1, $legalMoves); + + $this->assertContainsEqualsOnce(new Move( + new Pawn(new Position(6, 3), Side::BLACK), + new Position(5, 2), + new Pawn(new Position(5, 3), Side::WHITE), + ), $legalMoves); + } } \ No newline at end of file diff --git a/tests/Game/PawnTest.php b/tests/Game/PawnTest.php index e3c2454..e2065f1 100644 --- a/tests/Game/PawnTest.php +++ b/tests/Game/PawnTest.php @@ -243,4 +243,37 @@ final class PawnTest extends TestCase { ])) ); } + + public function testPromotes_no() { + $subject = new Pawn( + new Position(3, 4), + Side::WHITE, + ); + + $this->assertFalse( + $subject->promotes(new Position(3, 5)) + ); + } + + public function testPromotes_white() { + $subject = new Pawn( + new Position(3, 6), + Side::WHITE, + ); + + $this->assertTrue( + $subject->promotes(new Position(3, 7)) + ); + } + + public function testPromotes_black() { + $subject = new Pawn( + new Position(3, 1), + Side::BLACK, + ); + + $this->assertTrue( + $subject->promotes(new Position(3, 0)) + ); + } } \ No newline at end of file