import type { Game, Square, Color } from "./state"; const VALUE: Record = { p: 1, n: 3, b: 3, r: 5, q: 9, k: 100 }; export function pieceValue(type: string): number { return VALUE[type] ?? 0; } export const PIECE_NAME: Record = { p: "pawn", n: "knight", b: "bishop", r: "rook", q: "queen", k: "king", }; // Static exchange evaluation on an occupied square. Returns the material the owner // stands to lose if the enemy starts capturing there (positive = a real loss). export function exchangeLoss(game: Game, square: Square): number { const victim = game.get(square); if (!victim) return 0; const owner = victim.color; const enemy: Color = owner === "w" ? "b" : "w"; const attackers = valuesOf(game, game.attackers(square, enemy)); const defenders = valuesOf(game, game.attackers(square, owner)); if (attackers.length === 0) return 0; return swap(pieceValue(victim.type), attackers, defenders); } function valuesOf(game: Game, squares: Square[]): number[] { return squares .map((sq) => pieceValue(game.get(sq)!.type)) .sort((a, b) => a - b); } // Classic swap-off: least valuable piece captures first, each side may stop when // continuing would lose material. Returns net material for the attacking side. function swap(target: number, attackers: number[], defenders: number[]): number { const gains: number[] = [target]; let onSquare = attackers[0]; let a = 1; let d = 0; let depth = 1; let defenderToMove = true; while (true) { if (defenderToMove) { if (d >= defenders.length) break; gains[depth] = onSquare - gains[depth - 1]; onSquare = defenders[d++]; } else { if (a >= attackers.length) break; gains[depth] = onSquare - gains[depth - 1]; onSquare = attackers[a++]; } depth++; defenderToMove = !defenderToMove; } for (let i = depth - 1; i > 0; i--) { gains[i - 1] = -Math.max(-gains[i - 1], gains[i]); } return gains[0]; } export interface Threat { square: Square; type: string; loss: number; } // Every piece of `color` the opponent can win material from, worst first. export function threats(game: Game, color: Color): Threat[] { const out: Threat[] = []; for (const { square, type } of game.pieces(color)) { if (type === "k") continue; const loss = exchangeLoss(game, square); if (loss > 0) out.push({ square, type, loss }); } return out.sort((a, b) => b.loss - a.loss); }