feat(chess): add king basic move rules (P2.12)
This commit is contained in:
parent
1013c40e2a
commit
83d99778db
2 changed files with 106 additions and 0 deletions
74
packages/chess/src/rules/king.test.ts
Normal file
74
packages/chess/src/rules/king.test.ts
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
import { describe, it, expect } from "vitest";
|
||||
import { Session } from "@paratype/rete";
|
||||
import type { EntityId } from "@paratype/rete";
|
||||
import { getLegalKingMoves } from "./king.js";
|
||||
|
||||
const mkId = (n: number) => n as EntityId;
|
||||
|
||||
function insertPiece(
|
||||
session: Session,
|
||||
id: number,
|
||||
type: string,
|
||||
color: string,
|
||||
square: number,
|
||||
): EntityId {
|
||||
const eid = mkId(id);
|
||||
session.insert(eid, "PieceType", type);
|
||||
session.insert(eid, "Color", color);
|
||||
session.insert(eid, "Position", square);
|
||||
return eid;
|
||||
}
|
||||
|
||||
describe("getLegalKingMoves", () => {
|
||||
it("king at e4 (28) has 8 moves on empty board", () => {
|
||||
const session = new Session({ autoFire: false });
|
||||
const king = insertPiece(session, 1, "king", "white", 28);
|
||||
expect(getLegalKingMoves(session, king)).toHaveLength(8);
|
||||
});
|
||||
|
||||
it("king at a1 (0) has 3 moves on empty board", () => {
|
||||
const session = new Session({ autoFire: false });
|
||||
const king = insertPiece(session, 1, "king", "white", 0);
|
||||
expect(getLegalKingMoves(session, king)).toHaveLength(3);
|
||||
});
|
||||
|
||||
it("king at h8 (63) has 3 moves on empty board", () => {
|
||||
const session = new Session({ autoFire: false });
|
||||
const king = insertPiece(session, 1, "king", "black", 63);
|
||||
expect(getLegalKingMoves(session, king)).toHaveLength(3);
|
||||
});
|
||||
|
||||
it("king blocked by own pieces", () => {
|
||||
const session = new Session({ autoFire: false });
|
||||
const king = insertPiece(session, 1, "king", "white", 28); // e4
|
||||
insertPiece(session, 2, "pawn", "white", 27); // d4
|
||||
insertPiece(session, 3, "pawn", "white", 29); // f4
|
||||
const moves = getLegalKingMoves(session, king);
|
||||
expect(moves.find(m => m.to === 27)).toBeUndefined();
|
||||
expect(moves.find(m => m.to === 29)).toBeUndefined();
|
||||
expect(moves).toHaveLength(6);
|
||||
});
|
||||
|
||||
it("king can capture enemy piece", () => {
|
||||
const session = new Session({ autoFire: false });
|
||||
const king = insertPiece(session, 1, "king", "white", 28); // e4
|
||||
insertPiece(session, 2, "pawn", "black", 27); // d4
|
||||
const capture = getLegalKingMoves(session, king).find(m => m.to === 27);
|
||||
expect(capture).toBeDefined();
|
||||
expect(capture?.isCapture).toBe(true);
|
||||
});
|
||||
|
||||
it("quiet moves are marked isCapture=false", () => {
|
||||
const session = new Session({ autoFire: false });
|
||||
const king = insertPiece(session, 1, "king", "white", 28);
|
||||
const moves = getLegalKingMoves(session, king);
|
||||
for (const m of moves) {
|
||||
expect(m.isCapture).toBe(false);
|
||||
}
|
||||
});
|
||||
|
||||
it("returns empty list when pieceId has no Position/Color", () => {
|
||||
const session = new Session({ autoFire: false });
|
||||
expect(getLegalKingMoves(session, mkId(999))).toEqual([]);
|
||||
});
|
||||
});
|
||||
32
packages/chess/src/rules/king.ts
Normal file
32
packages/chess/src/rules/king.ts
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
/**
|
||||
* King movement rules for @paratype/chess.
|
||||
*
|
||||
* Basic king moves: 1 square in any of 8 directions.
|
||||
* Excludes:
|
||||
* - Castling (P2.15)
|
||||
* - Check-aware rejection / king-in-check filter (P2.18)
|
||||
*
|
||||
* Rules:
|
||||
* - empty target square → legal quiet move
|
||||
* - enemy on target → legal capture
|
||||
* - ally on target → illegal
|
||||
*/
|
||||
import type { Session, EntityId } from "@paratype/rete";
|
||||
import { kingCandidates } from "./primitives.js";
|
||||
import type { LegalMove } from "./types.js";
|
||||
import { getPiecePosition, getPieceColor, isAllyAt, isPieceAt } from "./board-queries.js";
|
||||
|
||||
/** Get all legal basic king moves (1 square, any direction). */
|
||||
export function getLegalKingMoves(session: Session, pieceId: EntityId): LegalMove[] {
|
||||
const from = getPiecePosition(session, pieceId);
|
||||
const color = getPieceColor(session, pieceId);
|
||||
if (from === null || color === null) return [];
|
||||
|
||||
const moves: LegalMove[] = [];
|
||||
for (const to of kingCandidates(from)) {
|
||||
if (isAllyAt(session, to, color)) continue;
|
||||
const isCapture = isPieceAt(session, to);
|
||||
moves.push({ pieceId, from, to, isCapture });
|
||||
}
|
||||
return moves;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue