mirror of
https://github.com/sigmasternchen/php-chess
synced 2025-03-15 07:58:54 +00:00
feat: Add dead positions to game system
This commit is contained in:
parent
b15e5fc922
commit
51d01b0953
3 changed files with 143 additions and 1 deletions
|
@ -33,7 +33,7 @@ class Game {
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getPieces(Side $side): array {
|
public function getPieces(Side $side): array {
|
||||||
return array_filter($this->pieces, fn($p) => $p->getSide() == $side);
|
return array_values(array_filter($this->pieces, fn($p) => $p->getSide() == $side));
|
||||||
}
|
}
|
||||||
|
|
||||||
private function &findPiece(Piece $needle): Piece {
|
private function &findPiece(Piece $needle): Piece {
|
||||||
|
@ -312,6 +312,46 @@ class Game {
|
||||||
$this->history->add($this);
|
$this->history->add($this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function isDeadPosition(): bool {
|
||||||
|
$ownPieces = $this->getPieces($this->current);
|
||||||
|
$opponentPieces = $this->getPieces($this->current->getNext());
|
||||||
|
|
||||||
|
$getRemaining = function (array $pieces): Piece {
|
||||||
|
return ($pieces[0] instanceof King ? $pieces[1] : $pieces[0]);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (count($ownPieces) > 2) {
|
||||||
|
return false;
|
||||||
|
} else if (count($opponentPieces) > 2) {
|
||||||
|
return false;
|
||||||
|
} else if (count($ownPieces) == 1 && count($opponentPieces) == 1) {
|
||||||
|
return true;
|
||||||
|
} else if (count($ownPieces) == 1) {
|
||||||
|
$opponentRemaining = $getRemaining($opponentPieces);
|
||||||
|
return !(
|
||||||
|
$opponentRemaining instanceof Queen ||
|
||||||
|
$opponentRemaining instanceof Rook ||
|
||||||
|
$opponentRemaining instanceof Pawn
|
||||||
|
);
|
||||||
|
} else if (count($opponentPieces) == 1) {
|
||||||
|
$ownRemaining = $getRemaining($ownPieces);
|
||||||
|
return !(
|
||||||
|
$ownRemaining instanceof Queen ||
|
||||||
|
$ownRemaining instanceof Rook ||
|
||||||
|
$ownRemaining instanceof Pawn
|
||||||
|
);
|
||||||
|
} else { // both have 1 piece left besides king
|
||||||
|
$ownRemaining = $getRemaining($ownPieces);
|
||||||
|
$opponentRemaining = $getRemaining($opponentPieces);
|
||||||
|
|
||||||
|
return (
|
||||||
|
$ownRemaining instanceof Bishop &&
|
||||||
|
$opponentRemaining instanceof Bishop &&
|
||||||
|
$ownRemaining->getPosition()->getSquareColor() == $opponentRemaining->getPosition()->getSquareColor()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public function getGameState(bool $onlyIsLegal = false): GameState {
|
public function getGameState(bool $onlyIsLegal = false): GameState {
|
||||||
$allOccupied = $this->getAllOccupied();
|
$allOccupied = $this->getAllOccupied();
|
||||||
|
|
||||||
|
@ -327,6 +367,10 @@ class Game {
|
||||||
return GameState::THREEFOLD_REPETITION;
|
return GameState::THREEFOLD_REPETITION;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($this->isDeadPosition()) {
|
||||||
|
return GameState::DEAD_POSITION;
|
||||||
|
}
|
||||||
|
|
||||||
$legalMoves = $this->getLegalMoves();
|
$legalMoves = $this->getLegalMoves();
|
||||||
|
|
||||||
if ($this->isCheck($allOccupied)) {
|
if ($this->isCheck($allOccupied)) {
|
||||||
|
|
|
@ -17,6 +17,10 @@ class Position {
|
||||||
$this->rank >= 0 && $this->rank < 8;
|
$this->rank >= 0 && $this->rank < 8;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getSquareColor(): Side {
|
||||||
|
return ($this->rank % 2) ^ ($this->file % 2) ? Side::WHITE : Side::BLACK;
|
||||||
|
}
|
||||||
|
|
||||||
public function __toString(): string {
|
public function __toString(): string {
|
||||||
return ["a", "b", "c", "d", "e", "f", "g", "h"][$this->file] . ($this->rank + 1);
|
return ["a", "b", "c", "d", "e", "f", "g", "h"][$this->file] . ($this->rank + 1);
|
||||||
}
|
}
|
||||||
|
|
|
@ -140,6 +140,7 @@ final class GameTest extends TestCase {
|
||||||
[
|
[
|
||||||
new King(new Position(1, 1), Side::BLACK, true),
|
new King(new Position(1, 1), Side::BLACK, true),
|
||||||
new King(new Position(7, 6), Side::WHITE, true),
|
new King(new Position(7, 6), Side::WHITE, true),
|
||||||
|
new Pawn(new Position(3, 6), Side::WHITE, true), // to avoid dead position
|
||||||
],
|
],
|
||||||
Side::BLACK
|
Side::BLACK
|
||||||
);
|
);
|
||||||
|
@ -257,6 +258,99 @@ final class GameTest extends TestCase {
|
||||||
$this->assertEquals(GameState::DEFAULT, $subject->getGameState());
|
$this->assertEquals(GameState::DEFAULT, $subject->getGameState());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testGameState_deadPosition_onlyKings() {
|
||||||
|
$subject = new Game(
|
||||||
|
[
|
||||||
|
new King(new Position(0, 7), Side::BLACK),
|
||||||
|
new King(new Position(0, 5), Side::WHITE),
|
||||||
|
],
|
||||||
|
Side::BLACK
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->assertEquals(GameState::DEAD_POSITION, $subject->getGameState());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGameState_deadPosition_kingVsKingAndBishop() {
|
||||||
|
$subject = new Game(
|
||||||
|
[
|
||||||
|
new King(new Position(0, 7), Side::BLACK),
|
||||||
|
new King(new Position(0, 5), Side::WHITE),
|
||||||
|
new Bishop(new Position(1, 0), Side::WHITE),
|
||||||
|
],
|
||||||
|
Side::BLACK
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->assertEquals(GameState::DEAD_POSITION, $subject->getGameState());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGameState_deadPosition_kingVsKingAndKnight() {
|
||||||
|
$subject = new Game(
|
||||||
|
[
|
||||||
|
new King(new Position(0, 7), Side::BLACK),
|
||||||
|
new King(new Position(0, 5), Side::WHITE),
|
||||||
|
new Knight(new Position(1, 0), Side::WHITE),
|
||||||
|
],
|
||||||
|
Side::BLACK
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->assertEquals(GameState::DEAD_POSITION, $subject->getGameState());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGameState_deadPosition_kingAndBishopVsKingAndBishopSameColor() {
|
||||||
|
$subject = new Game(
|
||||||
|
[
|
||||||
|
new King(new Position(0, 7), Side::BLACK),
|
||||||
|
new King(new Position(0, 5), Side::WHITE),
|
||||||
|
new Bishop(new Position(1, 0), Side::WHITE),
|
||||||
|
new Bishop(new Position(2, 1), Side::BLACK),
|
||||||
|
],
|
||||||
|
Side::BLACK
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->assertEquals(GameState::DEAD_POSITION, $subject->getGameState());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGameState_deadPosition_kingAndBishopVsKingAndBishopDifferentColor_notDead() {
|
||||||
|
$subject = new Game(
|
||||||
|
[
|
||||||
|
new King(new Position(0, 7), Side::BLACK),
|
||||||
|
new King(new Position(0, 5), Side::WHITE),
|
||||||
|
new Bishop(new Position(1, 0), Side::WHITE),
|
||||||
|
new Bishop(new Position(1, 1), Side::BLACK),
|
||||||
|
],
|
||||||
|
Side::BLACK
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->assertEquals(GameState::DEFAULT, $subject->getGameState());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGameState_deadPosition_kingAndKnightVsKing_notDead() {
|
||||||
|
$subject = new Game(
|
||||||
|
[
|
||||||
|
new King(new Position(0, 7), Side::BLACK),
|
||||||
|
new King(new Position(0, 5), Side::WHITE),
|
||||||
|
new Bishop(new Position(1, 0), Side::WHITE),
|
||||||
|
new Knight(new Position(1, 1), Side::BLACK),
|
||||||
|
],
|
||||||
|
Side::BLACK
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->assertEquals(GameState::DEFAULT, $subject->getGameState());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGameState_deadPosition_kingAndPawnVsKing_notDead() {
|
||||||
|
$subject = new Game(
|
||||||
|
[
|
||||||
|
new King(new Position(0, 7), Side::BLACK),
|
||||||
|
new King(new Position(0, 5), Side::WHITE),
|
||||||
|
new Pawn(new Position(1, 1), Side::BLACK, true),
|
||||||
|
],
|
||||||
|
Side::BLACK
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->assertEquals(GameState::DEFAULT, $subject->getGameState());
|
||||||
|
}
|
||||||
|
|
||||||
public function testLegalMoves_pawnPinnedBecauseOfCheckKingRestrictedByQueenAndPawn() {
|
public function testLegalMoves_pawnPinnedBecauseOfCheckKingRestrictedByQueenAndPawn() {
|
||||||
$subject = new Game(
|
$subject = new Game(
|
||||||
[
|
[
|
||||||
|
|
Loading…
Reference in a new issue