feat: Add fifty-move-rule

This commit is contained in:
overflowerror 2024-01-07 16:40:54 +01:00
parent 78f6ba6ef2
commit 1d9f6f5736
2 changed files with 136 additions and 0 deletions

View file

@ -13,6 +13,9 @@ class Game {
private ?array $moveCache = null; private ?array $moveCache = null;
private int $movesSinceLastCapture = 0;
private int $movesSinceLastPawnMove = 0;
public function __construct(array $pieces, Side $current) { public function __construct(array $pieces, Side $current) {
$this->pieces = $pieces; $this->pieces = $pieces;
$this->current = $current; $this->current = $current;
@ -281,6 +284,9 @@ class Game {
public function applyInPlace(Move $move): void { public function applyInPlace(Move $move): void {
$this->tick(); $this->tick();
$this->movesSinceLastPawnMove++;
$this->movesSinceLastCapture++;
if ($move->castleWith) { if ($move->castleWith) {
$king = $this->findPiece($move->piece); $king = $this->findPiece($move->piece);
$rook = $this->findPiece($move->castleWith); $rook = $this->findPiece($move->castleWith);
@ -294,7 +300,12 @@ class Game {
} else { } else {
if ($move->captures) { if ($move->captures) {
$this->removePiece($move->captures); $this->removePiece($move->captures);
$this->movesSinceLastCapture = 0;
} }
if ($move->piece instanceof Pawn) {
$this->movesSinceLastPawnMove = 0;
}
if ($move->promoteTo) { if ($move->promoteTo) {
$this->removePiece($move->piece); $this->removePiece($move->piece);
@ -352,6 +363,15 @@ class Game {
} }
} }
public function _testFiftyMoveRule(int $movesSinceLastCapture, int $movesSinceLastPawnMove) {
$this->movesSinceLastPawnMove = $movesSinceLastPawnMove;
$this->movesSinceLastCapture = $movesSinceLastCapture;
}
private function isFiftyMoveRule(): bool {
return $this->movesSinceLastCapture >= 50 && $this->movesSinceLastPawnMove >= 50;
}
public function getGameState(bool $onlyIsLegal = false): GameState { public function getGameState(bool $onlyIsLegal = false): GameState {
$allOccupied = $this->getAllOccupied(); $allOccupied = $this->getAllOccupied();
@ -371,6 +391,10 @@ class Game {
return GameState::DEAD_POSITION; return GameState::DEAD_POSITION;
} }
if ($this->isFiftyMoveRule()) {
return GameState::FIFTY_MOVE_RULE;
}
$legalMoves = $this->getLegalMoves(); $legalMoves = $this->getLegalMoves();
if ($this->isCheck($allOccupied)) { if ($this->isCheck($allOccupied)) {

View file

@ -351,6 +351,118 @@ final class GameTest extends TestCase {
$this->assertEquals(GameState::DEFAULT, $subject->getGameState()); $this->assertEquals(GameState::DEFAULT, $subject->getGameState());
} }
public function testGameState_fiftyMoveRule() {
$subject = new Game(
[
new King(new Position(2, 7), Side::WHITE, true),
new King(new Position(0, 0), Side::BLACK, true),
new Rook(new Position(1, 0), Side::BLACK),
],
Side::BLACK
);
$subject->_testFiftyMoveRule(49, 49);
$this->assertEquals(GameState::DEFAULT, $subject->getGameState());
$subject->applyInPlace(new Move(
new Rook(new Position(1, 0), Side::BLACK),
new Position(3, 0)
));
echo $subject->visualize();
$this->assertEquals(GameState::FIFTY_MOVE_RULE, $subject->getGameState());
}
public function testGameState_fiftyMoveRule_notLongEnoughSincePawnMove() {
$subject = new Game(
[
new King(new Position(2, 7), Side::WHITE, true),
new King(new Position(0, 0), Side::BLACK, true),
new Rook(new Position(1, 0), Side::BLACK),
],
Side::BLACK
);
$subject->_testFiftyMoveRule(49, 48);
$this->assertEquals(GameState::DEFAULT, $subject->getGameState());
$subject->applyInPlace(new Move(
new Rook(new Position(1, 0), Side::BLACK),
new Position(3, 0)
));
echo $subject->visualize();
$this->assertEquals(GameState::DEFAULT, $subject->getGameState());
}
public function testGameState_fiftyMoveRule_notLongEnoughSinceCapture() {
$subject = new Game(
[
new King(new Position(2, 7), Side::WHITE, true),
new King(new Position(0, 0), Side::BLACK, true),
new Rook(new Position(1, 0), Side::BLACK),
],
Side::BLACK
);
$subject->_testFiftyMoveRule(48, 49);
$this->assertEquals(GameState::DEFAULT, $subject->getGameState());
$subject->applyInPlace(new Move(
new Rook(new Position(1, 0), Side::BLACK),
new Position(3, 0)
));
echo $subject->visualize();
$this->assertEquals(GameState::DEFAULT, $subject->getGameState());
}
public function testGameState_fiftyMoveRule_capture() {
$subject = new Game(
[
new King(new Position(2, 7), Side::WHITE, true),
new King(new Position(0, 0), Side::BLACK, true),
new Rook(new Position(1, 0), Side::BLACK),
new Bishop(new Position(3, 0), Side::WHITE),
],
Side::BLACK
);
$subject->_testFiftyMoveRule(49, 49);
$this->assertEquals(GameState::DEFAULT, $subject->getGameState());
$subject->applyInPlace(new Move(
new Rook(new Position(1, 0), Side::BLACK),
new Position(3, 0),
new Bishop(new Position(3, 0), Side::WHITE)
));
echo $subject->visualize();
$this->assertEquals(GameState::DEFAULT, $subject->getGameState());
}
public function testGameState_fiftyMoveRule_pawnMove() {
$subject = new Game(
[
new King(new Position(2, 7), Side::WHITE, true),
new King(new Position(0, 0), Side::BLACK, true),
new Pawn(new Position(6, 5), Side::BLACK, true)
],
Side::BLACK
);
$subject->_testFiftyMoveRule(49, 49);
$this->assertEquals(GameState::DEFAULT, $subject->getGameState());
$subject->applyInPlace(new Move(
new Pawn(new Position(6, 5), Side::BLACK),
new Position(6, 4),
));
echo $subject->visualize();
$this->assertEquals(GameState::DEFAULT, $subject->getGameState());
}
public function testLegalMoves_pawnPinnedBecauseOfCheckKingRestrictedByQueenAndPawn() { public function testLegalMoves_pawnPinnedBecauseOfCheckKingRestrictedByQueenAndPawn() {
$subject = new Game( $subject = new Game(
[ [