shogiops
Version:
Shogi rules and operations
99 lines (83 loc) • 2.87 kB
text/typescript
import { ROLES } from './constants.js';
import { SquareSet } from './square-set.js';
import type { Color, ColorMap, Piece, Role, RoleMap, Square } from './types.js';
export class Board implements Iterable<[Square, Piece]> {
private constructor(
public occupied: SquareSet,
private colorMap: ColorMap,
private roleMap: RoleMap,
) {}
static empty(): Board {
return new Board(SquareSet.empty(), new Map(), new Map());
}
static from(
occupied: SquareSet,
colorsIter: Iterable<[Color, SquareSet]>,
rolesIter: Iterable<[Role, SquareSet]>,
): Board {
return new Board(occupied, new Map(colorsIter), new Map(rolesIter));
}
clone(): Board {
return Board.from(this.occupied, this.colorMap, this.roleMap);
}
role(role: Role): SquareSet {
return this.roleMap.get(role) || SquareSet.empty();
}
roles(role: Role, ...roles: Role[]): SquareSet {
return roles.reduce((acc, r) => acc.union(this.role(r)), this.role(role));
}
color(color: Color): SquareSet {
return this.colorMap.get(color) || SquareSet.empty();
}
equals(other: Board): boolean {
if (!this.color('gote').equals(other.color('gote'))) return false;
return ROLES.every((role) => this.role(role).equals(other.role(role)));
}
getColor(square: Square): Color | undefined {
if (this.color('sente').has(square)) return 'sente';
if (this.color('gote').has(square)) return 'gote';
return;
}
getRole(square: Square): Role | undefined {
for (const [role, sqs] of this.roleMap) if (sqs.has(square)) return role;
return;
}
get(square: Square): Piece | undefined {
const color = this.getColor(square);
if (!color) return;
const role = this.getRole(square)!;
return { color, role };
}
take(square: Square): Piece | undefined {
const piece = this.get(square);
if (piece) {
this.occupied = this.occupied.without(square);
this.colorMap.set(piece.color, this.color(piece.color).without(square));
this.roleMap.set(piece.role, this.role(piece.role).without(square));
}
return piece;
}
set(square: Square, piece: Piece): Piece | undefined {
const old = this.take(square);
this.occupied = this.occupied.with(square);
this.colorMap.set(piece.color, this.color(piece.color).with(square));
this.roleMap.set(piece.role, this.role(piece.role).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)!];
}
}
presentRoles(): Role[] {
return Array.from(this.roleMap)
.filter(([_, sqs]) => sqs.nonEmpty())
.map(([r]) => r);
}
pieces(color: Color, role: Role): SquareSet {
return this.color(color).intersect(this.role(role));
}
}