mirror of
https://github.com/sigmasternchen/php-chess
synced 2025-03-14 23:58:53 +00:00
feat: Recognize game states checkmate and stalemate
This commit is contained in:
parent
2f3d5a3340
commit
e6fa6d8589
3 changed files with 86 additions and 8 deletions
|
@ -9,6 +9,8 @@ class Game {
|
|||
|
||||
private Side $current;
|
||||
|
||||
private ?array $moveCache = null;
|
||||
|
||||
public function __construct(array $pieces, Side $current) {
|
||||
$this->pieces = $pieces;
|
||||
$this->current = $current;
|
||||
|
@ -101,13 +103,17 @@ class Game {
|
|||
|
||||
|
||||
public function getLegalMoves(): array {
|
||||
if ($this->moveCache) {
|
||||
return $this->moveCache;
|
||||
}
|
||||
|
||||
$ownPieces = $this->getPieces($this->current);
|
||||
$opponentPieces = $this->getPieces($this->current->getNext());
|
||||
|
||||
$occupied = $this->getOccupied($ownPieces);
|
||||
$threatened = $this->getThreatened($opponentPieces, $occupied);
|
||||
|
||||
return $this->getLegalMovesCached(
|
||||
$this->moveCache = $this->getLegalMovesParameterized(
|
||||
$ownPieces,
|
||||
$opponentPieces,
|
||||
$occupied,
|
||||
|
@ -115,6 +121,8 @@ class Game {
|
|||
$this->getCaptureable($opponentPieces, true),
|
||||
$threatened,
|
||||
);
|
||||
|
||||
return $this->moveCache;
|
||||
}
|
||||
|
||||
private function generatePromotionMoves(Move $candidate): array {
|
||||
|
@ -175,10 +183,10 @@ class Game {
|
|||
|
||||
private function isMoveLegal(Move $move) {
|
||||
$futureGame = $this->apply($move);
|
||||
return $futureGame->getGameState() != GameState::ILLEGAL;
|
||||
return $futureGame->getGameState(true) != GameState::ILLEGAL;
|
||||
}
|
||||
|
||||
private function getLegalMovesCached(
|
||||
private function getLegalMovesParameterized(
|
||||
array &$ownPieces,
|
||||
array &$opponentPieces,
|
||||
FieldBitMap $occupied,
|
||||
|
@ -227,17 +235,29 @@ class Game {
|
|||
return $game;
|
||||
}
|
||||
|
||||
public function getGameState(): GameState {
|
||||
public function getGameState(bool $onlyIsLegal = false): GameState {
|
||||
$allOccupied = $this->getAllOccupied();
|
||||
|
||||
if ($this->isIllegal($allOccupied)) {
|
||||
return GameState::ILLEGAL;
|
||||
}
|
||||
|
||||
if ($this->isCheck($allOccupied)) {
|
||||
// TODO: check for checkmate
|
||||
if ($onlyIsLegal) {
|
||||
return GameState::UNKNOWN_VALID;
|
||||
}
|
||||
|
||||
return GameState::CHECK;
|
||||
$legalMoves = $this->getLegalMoves();
|
||||
|
||||
if ($this->isCheck($allOccupied)) {
|
||||
if (!$legalMoves) {
|
||||
return GameState::CHECKMATE;
|
||||
} else {
|
||||
return GameState::CHECK;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$legalMoves) {
|
||||
return GameState::STALEMATE;
|
||||
}
|
||||
|
||||
return GameState::DEFAULT;
|
||||
|
@ -267,7 +287,8 @@ class Game {
|
|||
$result .= "\033[30m";
|
||||
}
|
||||
|
||||
$result .= $piece->getShort() . " ";
|
||||
$short = $piece->getShort();
|
||||
$result .= ($short ?: "p") . " ";
|
||||
} else {
|
||||
$result .= " ";
|
||||
}
|
||||
|
|
|
@ -11,4 +11,5 @@ enum GameState {
|
|||
case THREEFOLD_REPETITION;
|
||||
case FIFTY_MOVE_RULE;
|
||||
case ILLEGAL;
|
||||
case UNKNOWN_VALID;
|
||||
}
|
|
@ -78,6 +78,62 @@ final class GameTest extends TestCase {
|
|||
$this->assertEquals(GameState::CHECK, $subject->getGameState());
|
||||
}
|
||||
|
||||
public function testGameState_checkmate_white() {
|
||||
$subject = new Game(
|
||||
[
|
||||
new King(new Position(0, 0), Side::WHITE),
|
||||
new Queen(new Position(1, 1), Side::BLACK),
|
||||
new King(new Position(2, 2), Side::BLACK),
|
||||
],
|
||||
Side::WHITE
|
||||
);
|
||||
|
||||
$this->assertEquals(GameState::CHECKMATE, $subject->getGameState());
|
||||
}
|
||||
|
||||
public function testGameState_checkmate_black() {
|
||||
$subject = new Game(
|
||||
[
|
||||
new King(new Position(0, 4), Side::BLACK),
|
||||
new Rook(new Position(0, 1), Side::WHITE),
|
||||
new King(new Position(2, 4), Side::WHITE),
|
||||
],
|
||||
Side::BLACK
|
||||
);
|
||||
|
||||
$this->assertEquals(GameState::CHECKMATE, $subject->getGameState());
|
||||
}
|
||||
|
||||
public function testGameState_stalemate_white() {
|
||||
$subject = new Game(
|
||||
[
|
||||
new King(new Position(4, 3), Side::WHITE),
|
||||
new Pawn(new Position(3, 3), Side::WHITE),
|
||||
new Rook(new Position(2, 3), Side::BLACK),
|
||||
new King(new Position(6, 3), Side::BLACK),
|
||||
new Queen(new Position(7, 4), Side::BLACK),
|
||||
new Knight(new Position(2, 0), Side::BLACK),
|
||||
new Bishop(new Position(3, 1), Side::BLACK),
|
||||
],
|
||||
Side::WHITE
|
||||
);
|
||||
|
||||
$this->assertEquals(GameState::STALEMATE, $subject->getGameState());
|
||||
}
|
||||
|
||||
public function testGameState_stalemate_black() {
|
||||
$subject = new Game(
|
||||
[
|
||||
new King(new Position(0, 7), Side::BLACK),
|
||||
new Rook(new Position(1, 1), Side::WHITE),
|
||||
new King(new Position(0, 5), Side::WHITE),
|
||||
],
|
||||
Side::BLACK
|
||||
);
|
||||
|
||||
$this->assertEquals(GameState::STALEMATE, $subject->getGameState());
|
||||
}
|
||||
|
||||
public function testLegalMoves_pawnPinnedBecauseOfCheckKingRestrictedByQueenAndPawn() {
|
||||
$subject = new Game(
|
||||
[
|
||||
|
|
Loading…
Reference in a new issue