chessops
Version:
Chess and chess variant rules and operations
169 lines (145 loc) • 4.74 kB
text/typescript
import { SquareSet } from './squareSet.js';
import { ByColor, ByRole, Color, COLORS, Piece, Role, ROLES, Square } from './types.js';
/**
* Piece positions on a board.
*
* Properties are sets of squares, like `board.occupied` for all occupied
* squares, `board[color]` for all pieces of that color, and `board[role]`
* for all pieces of that role. When modifying the properties directly, take
* care to keep them consistent.
*/
export class Board implements Iterable<[Square, Piece]>, ByRole<SquareSet>, ByColor<SquareSet> {
/**
* All occupied squares.
*/
occupied: SquareSet;
/**
* All squares occupied by pieces known to be promoted. This information is
* relevant in chess variants like Crazyhouse.
*/
promoted: SquareSet;
white: SquareSet;
black: SquareSet;
pawn: SquareSet;
knight: SquareSet;
bishop: SquareSet;
rook: SquareSet;
queen: SquareSet;
king: SquareSet;
private constructor() {}
static default(): Board {
const board = new Board();
board.reset();
return board;
}
/**
* Resets all pieces to the default starting position for standard chess.
*/
reset(): void {
this.occupied = new SquareSet(0xffff, 0xffff_0000);
this.promoted = SquareSet.empty();
this.white = new SquareSet(0xffff, 0);
this.black = new SquareSet(0, 0xffff_0000);
this.pawn = new SquareSet(0xff00, 0x00ff_0000);
this.knight = new SquareSet(0x42, 0x4200_0000);
this.bishop = new SquareSet(0x24, 0x2400_0000);
this.rook = new SquareSet(0x81, 0x8100_0000);
this.queen = new SquareSet(0x8, 0x0800_0000);
this.king = new SquareSet(0x10, 0x1000_0000);
}
static empty(): Board {
const board = new Board();
board.clear();
return board;
}
clear(): void {
this.occupied = SquareSet.empty();
this.promoted = SquareSet.empty();
for (const color of COLORS) this[color] = SquareSet.empty();
for (const role of ROLES) this[role] = SquareSet.empty();
}
clone(): Board {
const board = new Board();
board.occupied = this.occupied;
board.promoted = this.promoted;
for (const color of COLORS) board[color] = this[color];
for (const role of ROLES) board[role] = this[role];
return board;
}
getColor(square: Square): Color | undefined {
if (this.white.has(square)) return 'white';
if (this.black.has(square)) return 'black';
return;
}
getRole(square: Square): Role | undefined {
for (const role of ROLES) {
if (this[role].has(square)) return role;
}
return;
}
get(square: Square): Piece | undefined {
const color = this.getColor(square);
if (!color) return;
const role = this.getRole(square)!;
const promoted = this.promoted.has(square);
return { color, role, promoted };
}
/**
* Removes and returns the piece from the given `square`, if any.
*/
take(square: Square): Piece | undefined {
const piece = this.get(square);
if (piece) {
this.occupied = this.occupied.without(square);
this[piece.color] = this[piece.color].without(square);
this[piece.role] = this[piece.role].without(square);
if (piece.promoted) this.promoted = this.promoted.without(square);
}
return piece;
}
/**
* Put `piece` onto `square`, potentially replacing an existing piece.
* Returns the existing piece, if any.
*/
set(square: Square, piece: Piece): Piece | undefined {
const old = this.take(square);
this.occupied = this.occupied.with(square);
this[piece.color] = this[piece.color].with(square);
this[piece.role] = this[piece.role].with(square);
if (piece.promoted) this.promoted = this.promoted.with(square);
return old;
}
has(square: Square): boolean {
return this.occupied.has(square);
}
*[Symbol.iterator](): Iterator<[Square, Piece]> {
for (const square of this.occupied) {
yield [square, this.get(square)!];
}
}
pieces(color: Color, role: Role): SquareSet {
return this[color].intersect(this[role]);
}
rooksAndQueens(): SquareSet {
return this.rook.union(this.queen);
}
bishopsAndQueens(): SquareSet {
return this.bishop.union(this.queen);
}
steppers(): SquareSet {
return this.knight.union(this.pawn).union(this.king);
}
sliders(): SquareSet {
return this.bishop.union(this.rook).union(this.queen);
}
/**
* Finds the unique king of the given `color`, if any.
*/
kingOf(color: Color): Square | undefined {
return this.pieces(color, 'king').singleSquare();
}
}
export const boardEquals = (left: Board, right: Board): boolean =>
left.white.equals(right.white)
&& left.promoted.equals(right.promoted)
&& ROLES.every(role => left[role].equals(right[role]));