chessops
Version:
Chess and chess variant rules and operations
190 lines (155 loc) • 4.88 kB
text/typescript
import { Board, boardEquals } from './board.js';
import { SquareSet } from './squareSet.js';
import { ByColor, ByRole, Color, Role, ROLES, Square } from './types.js';
export class MaterialSide implements ByRole<number> {
pawn: number;
knight: number;
bishop: number;
rook: number;
queen: number;
king: number;
private constructor() {}
static empty(): MaterialSide {
const m = new MaterialSide();
for (const role of ROLES) m[role] = 0;
return m;
}
static fromBoard(board: Board, color: Color): MaterialSide {
const m = new MaterialSide();
for (const role of ROLES) m[role] = board.pieces(color, role).size();
return m;
}
clone(): MaterialSide {
const m = new MaterialSide();
for (const role of ROLES) m[role] = this[role];
return m;
}
equals(other: MaterialSide): boolean {
return ROLES.every(role => this[role] === other[role]);
}
add(other: MaterialSide): MaterialSide {
const m = new MaterialSide();
for (const role of ROLES) m[role] = this[role] + other[role];
return m;
}
subtract(other: MaterialSide): MaterialSide {
const m = new MaterialSide();
for (const role of ROLES) m[role] = this[role] - other[role];
return m;
}
nonEmpty(): boolean {
return ROLES.some(role => this[role] > 0);
}
isEmpty(): boolean {
return !this.nonEmpty();
}
hasPawns(): boolean {
return this.pawn > 0;
}
hasNonPawns(): boolean {
return this.knight > 0 || this.bishop > 0 || this.rook > 0 || this.queen > 0 || this.king > 0;
}
size(): number {
return this.pawn + this.knight + this.bishop + this.rook + this.queen + this.king;
}
}
export class Material implements ByColor<MaterialSide> {
constructor(
public white: MaterialSide,
public black: MaterialSide,
) {}
static empty(): Material {
return new Material(MaterialSide.empty(), MaterialSide.empty());
}
static fromBoard(board: Board): Material {
return new Material(MaterialSide.fromBoard(board, 'white'), MaterialSide.fromBoard(board, 'black'));
}
clone(): Material {
return new Material(this.white.clone(), this.black.clone());
}
equals(other: Material): boolean {
return this.white.equals(other.white) && this.black.equals(other.black);
}
add(other: Material): Material {
return new Material(this.white.add(other.white), this.black.add(other.black));
}
subtract(other: Material): Material {
return new Material(this.white.subtract(other.white), this.black.subtract(other.black));
}
count(role: Role): number {
return this.white[role] + this.black[role];
}
size(): number {
return this.white.size() + this.black.size();
}
isEmpty(): boolean {
return this.white.isEmpty() && this.black.isEmpty();
}
nonEmpty(): boolean {
return !this.isEmpty();
}
hasPawns(): boolean {
return this.white.hasPawns() || this.black.hasPawns();
}
hasNonPawns(): boolean {
return this.white.hasNonPawns() || this.black.hasNonPawns();
}
}
export class RemainingChecks implements ByColor<number> {
constructor(
public white: number,
public black: number,
) {}
static default(): RemainingChecks {
return new RemainingChecks(3, 3);
}
clone(): RemainingChecks {
return new RemainingChecks(this.white, this.black);
}
equals(other: RemainingChecks): boolean {
return this.white === other.white && this.black === other.black;
}
}
/**
* A not necessarily legal chess or chess variant position.
*/
export interface Setup {
board: Board;
pockets: Material | undefined;
turn: Color;
castlingRights: SquareSet;
epSquare: Square | undefined;
remainingChecks: RemainingChecks | undefined;
halfmoves: number;
fullmoves: number;
}
export const defaultSetup = (): Setup => ({
board: Board.default(),
pockets: undefined,
turn: 'white',
castlingRights: SquareSet.corners(),
epSquare: undefined,
remainingChecks: undefined,
halfmoves: 0,
fullmoves: 1,
});
export const setupClone = (setup: Setup): Setup => ({
board: setup.board.clone(),
pockets: setup.pockets?.clone(),
turn: setup.turn,
castlingRights: setup.castlingRights,
epSquare: setup.epSquare,
remainingChecks: setup.remainingChecks?.clone(),
halfmoves: setup.halfmoves,
fullmoves: setup.fullmoves,
});
export const setupEquals = (left: Setup, right: Setup): boolean =>
boardEquals(left.board, right.board)
&& ((right.pockets && left.pockets?.equals(right.pockets)) || (!left.pockets && !right.pockets))
&& left.turn === right.turn
&& left.castlingRights.equals(right.castlingRights)
&& left.epSquare === right.epSquare
&& ((right.remainingChecks && left.remainingChecks?.equals(right.remainingChecks))
|| (!left.remainingChecks && !right.remainingChecks))
&& left.halfmoves === right.halfmoves
&& left.fullmoves === right.fullmoves;