← Chess results

Chess

bonsai 27b · typescript

Initial view of Chess

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>