Chess
bonsai 27b · typescript

Reviews & generation details
Reviews
No human review yet. Unreviewed does not mean unsuccessful.
Generation context
Imported historical artifact. Its exact original instructions and lineage are not known.
Generation prompt · exact text unavailable
See the current prompt for reference. It may differ from the instructions used to generate this artifact.
Inspect original source 21,165 bytes · SHA-256 00066e75eda0
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Chess</title>
<style>
</style>
</head>
<body>
<div id="app"></div>
<script>
(function () {
'use strict';
var __mods = {};
var __cache = {};
var __map = {"src/state.ts":{"./types":"src/types.ts"}};
function __require(id) {
if (__cache[id]) return __cache[id].exports;
var module = __cache[id] = { exports: {} };
var factory = __mods[id];
if (!factory) throw new Error("Module not found: " + id);
factory(module.exports, function (spec) {
var target = (__map[id] && __map[id][spec]) || spec;
return __require(target);
}, module);
return module.exports;
}
// ── module: src/main.ts ──
__mods["src/main.ts"] = function (exports, require, module) {
"use strict";
// Entry point. Runs after the DOM is parsed and after any classic
// <script> imports, so document queries and CDN globals are available.
// Import your other modules from here.
Object.defineProperty(exports, "__esModule", { value: true });
exports.start = start;
function start() {
// Replace this with the real startup sequence.
}
start();
};
// ── module: src/types.ts ──
__mods["src/types.ts"] = function (exports, require, module) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.MOVE_ORDERING_WEIGHTS = exports.FILE_CHARS = exports.BOARD_SIZE = exports.PIECE_SQUARE_TABLES_FLAT_K = exports.PIECE_SQUARE_TABLES_FLAT_Q = exports.PIECE_SQUARE_TABLES_FLAT_N = exports.PIECE_SQUARE_TABLES_FLAT_B = exports.PIECE_SQUARE_TABLES_FLAT_BLACK = exports.PIECE_SQUARE_TABLES_FLAT_WHITE = exports.PIECE_INDICES = exports.PIECE_VALUES = exports.piecesOfColor = void 0;
exports.piecesOfColor = {
P: '♟', R: '♜', N: '♞', B: '♛', Q: '♕', K: '♔',
};
// Piece values for evaluation
exports.PIECE_VALUES = [100, 320, 330, 330, 900, 0];
// Piece indices mapping
exports.PIECE_INDICES = {
P: 0, R: 1, N: 2, B: 3, Q: 4, K: 5,
};
// Piece square tables as a flat array indexed by [pieceIndex][file] * 8 + rank
exports.PIECE_SQUARE_TABLES_FLAT_WHITE = [
// P row 7 (back) file 0-7
28, -33, 75, 196, 347, 400, 346, 287,
// P row 6
-33, 11, 76, 189, 341, 395, 338, 284,
// P row 5
75, -24, 90, 171, 339, 384, 330, 275,
// P row 4
-30, -25, 102, 163, 337, 383, 327, 275,
// P row 3
-22, -19, 107, 158, 330, 381, 324, 269,
// P row 2
-17, -16, 114, 150, 330, 376, 321, 264,
// P row 1
76, -31, 97, 183, 343, 392, 339, 288,
// P row 0 (front)
28, -28, 105, 192, 345, 396, 347, 290,
];
exports.PIECE_SQUARE_TABLES_FLAT_BLACK = [
// R row 7 (back) file 0-7
10, -26, -37, -32, -65, -24, -37, -26,
// R row 6
-26, -28, -46, -42, -89, -26, -46, -28,
// R row 5
-37, -25, -70, -60, -115, -37, -70, -25,
// R row 4
-32, -24, -74, -64, -120, -32, -74, -24,
// R row 3
-65, -22, -80, -72, -129, -65, -80, -22,
// R row 2
-24, -21, -86, -78, -135, -24, -86, -21,
// R row 1
-37, -29, -90, -82, -142, -37, -90, -29,
// R row 0 (front)
-26, -25, -95, -87, -149, -26, -95, -25,
];
exports.PIECE_SQUARE_TABLES_FLAT_B = [
// B row 7 (back) file 0-7
54, 85, 20, 22, 22, 85, 54,
-1, -1, -1, -1, -1, -1, -1, // padding for B row 6 (simplified)
];
exports.PIECE_SQUARE_TABLES_FLAT_N = [
-21, 9, -17, -30, -30, 9, -17, 9,
9, -24, 68, -32, -32, 68, -24, 9,
-17, -25, 85, -30, -30, 85, -25, -17,
-30, -26, 107, -34, -34, 107, -26, -30,
-30, -28, 133, -38, -38, 133, -28, -30,
9, -31, 162, -41, -41, 162, -31, 9,
-17, -33, 188, -45, -45, 188, -33, -17,
9, -36, 210, -47, -47, 210, -36, 9,
];
exports.PIECE_SQUARE_TABLES_FLAT_Q = [
60, 133, 45, 43, 43, 133, 60,
133, 96, 104, 98, 104, 96, 133,
45, 48, 52, 42, 52, 48, 45,
43, 57, 60, 52, 60, 57, 43,
43, 68, 71, 62, 71, 68, 43,
133, 109, 111, 105, 111, 109, 133,
60, 78, 80, 74, 80, 78, 60,
];
exports.PIECE_SQUARE_TABLES_FLAT_K = [
-49, -71, -30, -27, -27, -71, -49,
-71, -87, -53, -46, -53, -87, -71,
-30, -38, -16, -21, -16, -38, -30,
-27, -34, -19, -27, -19, -34, -27,
-27, -46, -25, -41, -25, -46, -27,
-71, -86, -52, -59, -52, -86, -71,
-49, -65, -31, -40, -31, -65, -49,
];
// Board coordinate helpers (internal constants)
exports.BOARD_SIZE = 8;
exports.FILE_CHARS = 'abcdefgh';
// Move ordering weights for AI
exports.MOVE_ORDERING_WEIGHTS = { capture: 30, check: 25, checkmate: 40, pawnPromotion: 10 };
};
// ── module: src/state.ts ──
__mods["src/state.ts"] = function (exports, require, module) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.GameStateManager = void 0;
exports.chessPieceToPiece = chessPieceToPiece;
exports.squareToIndices = squareToIndices;
// FEN constants
const WHITE_FEN = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1";
const BLACK_FEN = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR b KQkq - 0 2";
class GameStateManager {
constructor() {
this.state = this.createInitialState();
}
loadFromFEN(fen) {
this.state = this.parseInitialFromFEN(fen);
this.saveState();
}
createInitialState() {
const flatBoard = [];
// Parse white FEN manually - row by row (from rank 7 to 0)
for (let r = 0; r < 8; r++) {
const filePart = WHITE_FEN.split('/')[r + 1];
for (const ch of filePart) {
if (ch >= 'a' && ch <= 'h') {
flatBoard.push([this.fenCharToPiece(ch), 'white']);
}
else if (ch === 'P' || ch === 'p') {
flatBoard.push([this.fenCharToPiece(ch), 'white']);
}
}
}
const board = this.buildBoard(flatBoard);
return {
board,
currentColor: 'white',
whiteCastling: true,
blackCastling: true,
enPassantSquare: null,
moveCount: 0,
halfMovesSinceLastCapture: 0,
lastMove: null,
};
}
fenCharToPiece(ch) {
if (ch === ' ')
return null;
const pieceType = ch.toLowerCase();
let piece;
switch (pieceType) {
case 'p':
piece = 'P';
break;
case 'r':
piece = 'R';
break;
case 'n':
piece = 'N';
break;
case 'b':
piece = 'B';
break;
case 'q':
piece = 'Q';
break;
case 'k':
piece = 'K';
break;
}
return piece;
}
buildBoard(flat) {
const board = [];
let idx = 0;
for (let r = 0; r < 8; r++) {
const row = [];
for (let c = 0; c < 8 && idx < flat.length; c++) {
const [piece, color] = flat[idx++];
if (piece !== null) {
row.push([piece, color]);
}
else {
// Empty square placeholder - will be filled properly in actual parsing
row.push([null, null]);
}
}
board.push(row);
}
return board;
}
parseInitialFromFEN(fen) {
const parts = fen.split(' ');
const colorChar = parts[9] === 'w' ? 'white' : 'black';
// Parse the board from FEN - row by row (rank 7 to 0)
const flatBoard = [];
for (let r = 0; r < 8; r++) {
const filePart = parts[1 + r];
for (const ch of filePart) {
if (ch >= 'a' && ch <= 'h') {
flatBoard.push([this.fenCharToPiece(ch), colorChar]);
}
else if (ch === 'P' || ch === 'p') {
flatBoard.push([this.fenCharToPiece(ch), colorChar]);
}
else {
// Empty square - fill with placeholder
for (let c = 0; c < 8; c++) {
flatBoard.push([null, null]);
}
}
}
}
return {
board: this.buildBoard(flatBoard),
currentColor: colorChar,
whiteCastling: parts[10] === 'K',
blackCastling: parts[12] === 'k',
enPassantSquare: parts[7],
moveCount: parseInt(parts[8]),
halfMovesSinceLastCapture: parseInt(parts[9]),
lastMove: null,
};
}
makeMove(move) {
const oldState = this.state;
// Get piece info from the board at "from" position
const [fileIdx, rankIdx] = squareToIndices(move.from);
const [toFile, toRank] = squareToIndices(move.to);
const pieceBefore = this.state.board[rankIdx][fileIdx];
if (!this.isValidMoveOnBoard(move)) {
return oldState;
}
// Check for en passant capture
let isEnPassant = false;
if (move.isEnPassant && pieceBefore === 'P') {
const epSquare = this.state.enPassantSquare;
if (epSquare) {
isEnPassant = true;
}
}
// Update board: remove piece from source, place at destination
const boardCopy = this.state.board.map(row => [...row]);
if (move.isPromotion) {
boardCopy[rankIdx][fileIdx] = [null, null];
boardCopy[toRank][toFile] = [move.promotionPiece, oldState.currentColor];
}
else {
boardCopy[rankIdx][fileIdx] = [null, null];
boardCopy[toRank][toFile] = [pieceBefore[0], oldState.currentColor];
}
// Update castling rights
let newWhiteCastling = this.state.whiteCastling;
let newBlackCastling = this.state.blackCastling;
if (move.castling) {
const [fromFile, fromRank] = squareToIndices(move.from);
// King moved or rook moved from starting position -> no castling
newWhiteCastling = !(pieceBefore[1] === 'white' && (fromFile === 0 || fromFile === 7));
newBlackCastling = (!(pieceBefore[1] === 'black' && (fromFile === 0 || fromFile === 7)));
if (move.castling === 'kingSide') {
newWhiteCastling = !(pieceBefore[1] === 'white' && (fromFile === 6 || toFile === 7));
newBlackCastling = (!(pieceBefore[1] === 'black' && (fromFile === 6 || toFile === 7)));
}
else { // queenSide
newWhiteCastling = !(pieceBefore[1] === 'white' && (fromFile === 7 || toFile === 0));
newBlackCastling = (!(pieceBefore[1] === 'black' && (fromFile === 7 || toFile === 0)));
}
}
// Update en passant square
let newEnPassantSquare = this.state.enPassantSquare;
if (move.isPromotion) {
newEnPassantSquare = null;
}
else if (pieceBefore === 'P') {
const [pieceFile, pieceRank] = squareToIndices(move.from);
// If pawn moved 2 squares from starting position -> en passant opportunity
newEnPassantSquare = this.state.currentColor === 'white'
? `${FILE_CHARS[pieceFile]}${String(pieceRank + 1)}`
: `${FILE_CHARS[pieceFile]}${String(pieceRank - 1)}`;
}
else {
newEnPassantSquare = null;
}
// Update halfmoves counter (only if no capture and not pawn moving to promotion square)
let newHalfMoves = this.state.halfMovesSinceLastCapture;
if (!isEnPassant && pieceBefore !== 'P') {
newHalfMoves++;
}
else if (!move.isPromotion) {
// Pawn move - doesn't count as a half-move for the purpose of 50-move rule
newHalfMoves = this.state.halfMovesSinceLastCapture;
}
// Check for checkmate/stalemate and update accordingly
const newGameState = {
board: boardCopy,
currentColor: oldState.currentColor === 'white' ? 'black' : 'white',
whiteCastling: newWhiteCastling,
blackCastling: newBlackCastling,
enPassantSquare: newEnPassantSquare,
moveCount: oldState.moveCount + 1,
halfMovesSinceLastCapture: newHalfMoves,
lastMove: { ...move },
};
// Check for game over conditions
const gameOver = this.checkGameOver(newGameState);
if (gameOver) {
return { ...newGameState, board: boardCopy, currentColor: 'white' };
}
// Save to localStorage
this.saveState();
return newGameState;
}
isValidMoveOnBoard(move) {
const [fromFile, fromRank] = squareToIndices(move.from);
if (fromFile === undefined || fromRank === undefined)
return false;
// Check piece exists at source
if (!this.state.board[fromRank][fromFile])
return false;
// Check color matches current player
const pieceAtFrom = this.state.board[fromRank][fromFile];
return pieceAtFrom[1] === this.state.currentColor;
}
saveState() {
const snapshot = {
board: JSON.stringify(this.state.board),
currentColor: this.state.currentColor,
whiteCastling: this.state.whiteCastling,
blackCastling: this.state.blackCastling,
enPassantSquare: this.state.enPassantSquare,
moveCount: this.state.moveCount,
halfMovesSinceLastCapture: this.state.halfMovesSinceLastCapture,
};
try {
localStorage.setItem('chessGame', JSON.stringify(snapshot));
}
catch (e) {
// Storage might be full or unavailable
}
}
restoreState() {
const saved = localStorage.getItem('chessGame');
if (!saved) {
return this.createInitialState();
}
try {
const snapshot = JSON.parse(saved);
// Reconstruct board from string
const flatBoard = [];
for (let r = 0; r < 8; r++) {
for (let c = 0; c < 8; c++) {
const pieceStr = snapshot.board[r * 8 + c];
if (pieceStr === 'P' || pieceStr === 'p') {
flatBoard.push([pieceStr, snapshot.currentColor === 'white' ? 'white' : 'black']);
}
else if (pieceStr !== '') {
const piece = chessPieceToPiece(pieceStr);
flatBoard.push([piece, snapshot.currentColor === 'white' ? 'white' : 'black']);
}
}
}
return {
board: this.buildBoard(flatBoard),
currentColor: snapshot.currentColor,
whiteCastling: snapshot.whiteCastling,
blackCastling: snapshot.blackCastling,
enPassantSquare: snapshot.enPassantSquare,
moveCount: snapshot.moveCount,
halfMovesSinceLastCapture: snapshot.halfMovesSinceLastCapture,
lastMove: null,
};
}
catch (e) {
console.error('Failed to restore game state:', e);
return this.createInitialState();
}
}
getGameState() {
return this.state;
}
getGameStateSnapshot() {
const snapshot = {
board: JSON.stringify(this.state.board),
currentColor: this.state.currentColor,
whiteCastling: this.state.whiteCastling,
blackCastling: this.state.blackCastling,
enPassantSquare: this.state.enPassantSquare,
moveCount: this.state.moveCount,
halfMovesSinceLastCapture: this.state.halfMovesSinceLastCapture,
};
return snapshot;
}
setGameState(snapshot) {
this.state = {
board: JSON.parse(snapshot.board),
currentColor: snapshot.currentColor,
whiteCastling: snapshot.whiteCastling,
blackCastling: snapshot.blackCastling,
enPassantSquare: snapshot.enPassantSquare,
moveCount: snapshot.moveCount,
halfMovesSinceLastCapture: snapshot.halfMovesSinceLastCapture,
lastMove: null,
};
}
undo() {
const saved = localStorage.getItem('chessGame');
if (!saved)
return this.state;
try {
const savedSnapshot = JSON.parse(saved);
// Restore to previous state (we'd need to store history for proper undo)
return this.createInitialState();
}
catch (e) {
console.error('Failed to undo:', e);
return this.state;
}
}
getLegalMoves() {
const engine = new Engine(this.getGameState());
return engine.generateLegalMoves();
}
getFEN() {
const gs = this.getGameState();
// Build FEN from board
let fenBoard = '';
for (let r = 7; r >= 0; r--) {
let row = '';
for (let c = 0; c < 8; c++) {
const [piece, color] = gs.board[r][c];
if (!piece) {
row += ' ';
continue;
}
// Convert piece to FEN format
let fenPiece = '';
switch (piece) {
case 'P':
fenPiece = 'p';
break;
case 'R':
fenPiece = 'r';
break;
case 'N':
fenPiece = 'n';
break;
case 'B':
fenPiece = 'b';
break;
case 'Q':
fenPiece = 'q';
break;
case 'K':
fenPiece = 'k';
break;
}
row += fenPiece;
}
// Compress consecutive empty squares
let compressed = '';
let count = 0;
for (let i = 0; i < row.length; i++) {
if (row[i] === ' ') {
count++;
if (i === row.length - 1) {
compressed += String(count);
}
else if (count >= 8) {
compressed += '8';
count = 0;
}
}
else {
compressed += row[i];
count = 0;
}
}
fenBoard += compressed + (r > 0 ? '/' : '');
}
const sideToMove = gs.currentColor === 'white' ? 'w' : 'b';
const castling = gs.whiteCastling && gs.blackCastling ? '-' :
gs.whiteCastling || gs.blackCastling ? 'Kk' : '-';
return `${fenBoard} ${sideToMove} ${castling} - 0 1`; // Simplified FEN for now
}
isGameOver() {
const engine = new Engine(this.getGameState());
return !engine.generateLegalMoves().length;
}
getGameOverInfo() {
const engine = new Engine(this.getGameState());
// Check if current player can move (stalemate or checkmate)
const moves = engine.generateLegalMoves();
if (moves.length === 0) {
// Determine reason
const gs = this.getGameState();
const hasPieces = !this.hasPieceCount(gs.board, 'white', 'P');
return {
status: hasPieces ? 'checkmate' : 'draw',
reason: hasPieces ? 'Checkmate!' : 'Insufficient material - Draw!',
};
}
return null;
}
hasPieceCount(board, color, pieceType) {
for (let r = 0; r < 8; r++) {
for (let c = 0; c < 8; c++) {
const [piece, player] = board[r][c];
if (player === color && (pieceType === undefined || piece === pieceType)) {
return true;
}
}
}
return false;
}
getTurn() {
return this.state.currentColor;
}
}
exports.GameStateManager = GameStateManager;
// Helper function to convert FEN char to Piece type
function chessPieceToPiece(ch) {
switch (ch.toLowerCase()) {
case 'p': return 'P';
case 'r': return 'R';
case 'n': return 'N';
case 'b': return 'B';
case 'q': return 'Q';
case 'k': return 'K';
}
throw new Error(`Unknown piece: ${ch}`);
}
// Helper to convert square string (e.g., "e2") to indices
function squareToIndices(square) {
const fileIdx = FILE_CHARS.indexOf(square[0]);
const rankIdx = parseInt(square[1]) - 1;
if (fileIdx === -1 || !rankIdx || rankIdx < 0 || rankIdx >= 7) {
throw new Error(`Invalid square: ${square}`);
}
return [fileIdx, rankIdx];
}
};
// ── entry ──
__require("src/main.ts");
})();
</script>
</body>
</html>