feat: Game recognized castles as valid moves

This commit is contained in:
overflowerror 2024-01-07 13:05:28 +01:00
parent 5c1bab71df
commit 11d9e04f07
3 changed files with 101 additions and 4 deletions

View file

@ -152,7 +152,7 @@ class Game {
private function getCandidateMovesForPiece(
Piece $piece,
array $opponentPieces,
array &$opponentPieces,
FieldBitMap $occupied,
FieldBitMap $capturableForNonPawn,
FieldBitMap $captureableForPawn,
@ -186,6 +186,33 @@ class Game {
return $futureGame->getGameState(true) != GameState::ILLEGAL;
}
private function getCastleMoves(
array &$ownPieces,
FieldBitMap $occupied,
FieldBitMap $capturable,
FieldBitMap $threatened
): array {
$king = $this->getKing($this->current);
return array_values(
array_map(
fn($r) => new Move(
$king,
new Position(
$king->getPosition()->file + 2 * ($r->getPosition()->file <=> $king->getPosition()->file),
$king->getPosition()->rank,
),
null,
null,
$r,
),
array_filter(
array_filter($ownPieces, fn($p) => $p instanceof Rook),
fn($r) => $king->canCastle($occupied, $capturable, $threatened, $r)
)
)
);
}
private function getLegalMovesParameterized(
array &$ownPieces,
array &$opponentPieces,
@ -207,7 +234,17 @@ class Game {
));
}
return array_values(array_filter($candidates, [$this, "isMoveLegal"]));
$candidates = array_values(array_filter($candidates, [$this, "isMoveLegal"]));
// castle moves should always be legal
$candidates = array_merge($candidates, $this->getCastleMoves(
$ownPieces,
$occupied,
$capturableNonPawn,
$threatened,
));
return $candidates;
}
public function apply(Move $move): Game {

View file

@ -7,12 +7,20 @@ class Move {
public Position $target;
public ?Piece $captures = null;
public ?PieceType $promoteTo = null;
public ?Piece $castleWith = null;
public function __construct(Piece $piece, Position $target, ?Piece $captures = null, ?PieceType $promoteTo = null) {
public function __construct(
Piece $piece,
Position $target,
?Piece $captures = null,
?PieceType $promoteTo = null,
?Piece $castleWith = null,
) {
$this->piece = $piece;
$this->target = $target;
$this->captures = $captures;
$this->promoteTo = $promoteTo;
$this->castleWith = $castleWith;
}
public function equals(Move $move): bool {
@ -22,7 +30,11 @@ class Move {
($this->captures != null && $move->captures != null && $this->captures->equals($move->captures)) ||
($this->captures == null && $move->captures == null)
) &&
$this->promoteTo == $move->promoteTo;
$this->promoteTo == $move->promoteTo &&
(
($this->castleWith != null && $move->castleWith != null && $this->castleWith->equals($move->castleWith)) ||
($this->castleWith == null && $move->castleWith == null)
);
}
public function __toString(): string {

View file

@ -400,4 +400,52 @@ final class GameTest extends TestCase {
new Pawn(new Position(5, 3), Side::WHITE),
), $legalMoves);
}
public function testLegalMoves_castle() {
$subject = new Game(
[
new King(new Position(0, 7), Side::BLACK),
new King(new Position(4, 0), Side::WHITE),
new Queen(new Position(2, 1), Side::BLACK),
new Rook(new Position(7, 0), Side::WHITE),
new Rook(new Position(7, 1), Side::BLACK),
],
Side::WHITE
);
$legalMoves = $subject->getLegalMoves();
echo join("\n", $legalMoves);
$this->assertCount(5, $legalMoves);
$this->assertContainsEqualsOnce(new Move(
new King(new Position(4, 0), Side::BLACK),
new Position(5, 0),
), $legalMoves);
$this->assertContainsEqualsOnce(new Move(
new King(new Position(4, 0), Side::BLACK),
new Position(6, 0),
null,
null,
new Rook(new Position(7, 0), Side::WHITE),
), $legalMoves);
$this->assertContainsEqualsOnce(new Move(
new Rook(new Position(7, 0), Side::WHITE),
new Position(6, 0),
), $legalMoves);
$this->assertContainsEqualsOnce(new Move(
new Rook(new Position(7, 0), Side::WHITE),
new Position(5, 0),
), $legalMoves);
$this->assertContainsEqualsOnce(new Move(
new Rook(new Position(7, 0), Side::WHITE),
new Position(7, 1),
new Rook(new Position(7, 1), Side::BLACK),
), $legalMoves);
}
}