← Chess results

Chess

Ornith 1.0 35B GGUF UD IQ4 NL · 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 48,176 bytes · SHA-256 a2eeec818e90
<!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 id="game-container">
    <div id="board-section">
      <div id="board"></div>
      <div id="player-color-select">
        <button id="btn-white" class="color-btn active" data-color="w">White</button>
        <button id="btn-black" class="color-btn" data-color="b">Black</button>
        <button id="btn-both" class="color-btn" data-color="both">Both</button>
      </div>
    </div>
    <div id="side-panel">
      <div id="status"></div>
      <div id="captured-row">
        <div id="captured-white"></div>
        <div id="captured-black"></div>
      </div>
      <div id="move-list"></div>
      <div id="controls">
        <button id="new-game-btn">New Game</button>
        <button id="undo-btn">Undo</button>
        <button id="flip-board-btn">Flip Board</button>
        <button id="sound-btn">🔊</button>
        <div id="difficulty">
          <span>Depth:</span>
          <button class="depth-btn active" data-depth="1">1</button>
          <button class="depth-btn" data-depth="2">2</button>
          <button class="depth-btn" data-depth="3">3</button>
        </div>
      </div>
    </div>
  </div>
  <div id="promotion-overlay" class="hidden">
    <div id="promotion-picker"></div>
  </div>
</div>
<script>
(function () {
'use strict';
var __mods = {};
var __cache = {};
var __map = {"src/main.ts":{"./game":"src/game.ts"},"src/engine.ts":{"./types":"src/types.ts"},"src/ai.ts":{"./engine":"src/engine.ts","./types":"src/types.ts"},"src/renderer.ts":{"./engine":"src/engine.ts","./types":"src/types.ts"},"src/game.ts":{"./engine":"src/engine.ts","./ai":"src/ai.ts","./audio":"src/audio.ts","./renderer":"src/renderer.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 — Initialize game and set up event listeners
// ============================================================
Object.defineProperty(exports, "__esModule", { value: true });
exports.start = start;
const game_1 = require("./game");
function start() {
    // Initialize game
    window.gameManager = new game_1.GameManager();
    // Set up event listeners
    const boardEl = document.getElementById('board');
    if (boardEl) {
        boardEl.addEventListener('click', (e) => {
            const target = e.target;
            const index = target.dataset.index;
            if (index !== undefined) {
                const idx = parseInt(index);
                window.gameManager?.handleSquareClickWrapper(idx);
            }
        });
    }
    // Set up control buttons
    const newGameBtn = document.getElementById('new-game-btn');
    const undoBtn = document.getElementById('undo-btn');
    const flipBtn = document.getElementById('flip-board-btn');
    const soundBtn = document.getElementById('sound-btn');
    newGameBtn?.addEventListener('click', () => window.gameManager?.newGame());
    undoBtn?.addEventListener('click', () => window.gameManager?.undoMove());
    flipBtn?.addEventListener('click', () => window.gameManager?.flipBoard());
    soundBtn?.addEventListener('click', () => window.gameManager?.toggleSound());
    // Set up difficulty selector
    const depthBtns = document.querySelectorAll('.depth-btn');
    depthBtns.forEach(btn => {
        btn.addEventListener('click', () => {
            const depth = parseInt(btn.dataset.depth || '3');
            window.gameManager?.setDifficulty(depth);
            depthBtns.forEach(b => b.classList.remove('active'));
            btn.classList.add('active');
        });
    });
    // Set up player color selection
    const colorBtns = document.querySelectorAll('.color-btn');
    colorBtns.forEach(btn => {
        btn.addEventListener('click', () => {
            const color = btn.dataset.color;
            window.gameManager?.setPlayerColor(color);
            colorBtns.forEach(b => b.classList.remove('active'));
            btn.classList.add('active');
        });
    });
}
};

// ── module: src/types.ts ──
__mods["src/types.ts"] = function (exports, require, module) {
"use strict";
// ============================================================
// Types and Tunable Constants
// ============================================================
Object.defineProperty(exports, "__esModule", { value: true });
exports.MoveFlags = exports.CONFIG = exports.RANK_COUNT = exports.FILE_COUNT = exports.SQUARES = void 0;
exports.squareToIdx = squareToIdx;
exports.idxToSquare = idxToSquare;
exports.idxToAlgebraic = idxToAlgebraic;
exports.algebraicToIdx = algebraicToIdx;
exports.SQUARES = 64;
exports.FILE_COUNT = 8;
exports.RANK_COUNT = 8;
// Tunable constants
exports.CONFIG = {
    colors: {
        lightSquare: "#f0d9b5",
        darkSquare: "#b58863",
        highlight: "rgba(255, 255, 100, 0.5)",
        lastMove: "rgba(155, 199, 0, 0.41)",
        check: "rgba(255, 0, 0, 0.55)",
        legalMove: "rgba(0, 0, 0, 0.25)",
        legalCapture: "rgba(255, 0, 0, 0.4)",
        selected: "rgba(100, 200, 255, 0.5)",
    },
    ai: {
        depth: 3,
        evalWeights: {
            pawn: 100,
            knight: 320,
            bishop: 330,
            rook: 500,
            queen: 900,
            king: 0,
        },
    },
    check50: 100, // half-moves for 50-move rule
    checkStalemate: true,
};
// Board indices: a1=0, b1=1, ..., h1=7, a2=8, ..., h8=63
function squareToIdx(file, rank) {
    return rank * 8 + file;
}
function idxToSquare(idx) {
    return { file: idx % 8, rank: Math.floor(idx / 8) };
}
function idxToAlgebraic(idx) {
    const { file, rank } = idxToSquare(idx);
    return "abcdefgh"[file] + (rank + 1).toString();
}
function algebraicToIdx(s) {
    const f = s.charCodeAt(0) - 97;
    const r = parseInt(s[1]) - 1;
    return squareToIdx(f, r);
}
exports.MoveFlags = {
    normal: () => ({ normal: true, castleK: false, castleQ: false, enPassant: false, promotion: false, doublePawn: false }),
    castleK: () => ({ normal: false, castleK: true, castleQ: false, enPassant: false, promotion: false, doublePawn: false }),
    castleQ: () => ({ normal: false, castleK: false, castleQ: true, enPassant: false, promotion: false, doublePawn: false }),
    enPassant: () => ({ normal: false, castleK: false, castleQ: false, enPassant: true, promotion: false, doublePawn: false }),
    promotion: (pieceType) => ({ normal: false, castleK: false, castleQ: false, enPassant: false, promotion: true, doublePawn: false }),
    doublePawn: () => ({ normal: false, castleK: false, castleQ: false, enPassant: false, promotion: false, doublePawn: true }),
};
};

// ── module: src/engine.ts ──
__mods["src/engine.ts"] = function (exports, require, module) {
"use strict";
// ============================================================
// Chess Engine — Position, Move Generation, Game Logic
// ============================================================
Object.defineProperty(exports, "__esModule", { value: true });
exports.Position = void 0;
const types_1 = require("./types");
const A8 = (0, types_1.squareToIdx)(7, 7);
const H8 = (0, types_1.squareToIdx)(0, 7);
const A1 = (0, types_1.squareToIdx)(7, 0);
const H1 = (0, types_1.squareToIdx)(0, 0);
// ============================================================
// Piece-Square Tables for evaluation
// ============================================================
const PST_PAWN = [
    0, 0, 0, 0, 0, 0, 0, 0,
    50, 50, 50, 50, 50, 50, 50, 50,
    10, 10, 20, 30, 30, 20, 10, 10,
    5, 5, 10, 25, 25, 10, 5, 5,
    0, 0, 0, 20, 20, 0, 0, 0,
    5, -5, -10, 0, 0, -10, -5, 5,
    5, 10, 10, -20, -20, 10, 10, 5,
    0, 0, 0, 0, 0, 0, 0, 0,
];
const PST_KNIGHT = [
    -50, -40, -30, -30, -30, -30, -40, -50,
    -40, -20, 10, 10, 10, 10, -20, -40,
    -30, 10, 20, 30, 30, 20, 10, -30,
    -30, 5, 15, 40, 40, 15, 5, -30,
    -30, 0, 15, 40, 40, 15, 0, -30,
    -30, 5, 10, 25, 25, 10, 5, -30,
    -40, -20, 0, 0, 0, 0, -20, -40,
    -50, -40, -30, -30, -30, -30, -40, -50,
];
const PST_BISHOP = [
    -20, -10, -10, -10, -10, -10, -10, -20,
    -10, 10, 10, 10, 10, 10, 10, -10,
    -10, 10, 15, 20, 20, 15, 10, -10,
    -10, 5, 10, 25, 25, 10, 5, -10,
    -10, 10, 10, 10, 10, 10, 10, -10,
    -10, 10, 5, 10, 10, 5, 10, -10,
    -10, 10, 10, 10, 10, 10, 10, -10,
    -20, -10, -10, -10, -10, -10, -10, -20,
];
const PST_ROOK = [
    0, 0, 0, 0, 0, 0, 0, 0,
    5, 10, 10, 10, 10, 10, 10, 5,
    -5, 0, 0, 0, 0, 0, 0, -5,
    -5, 0, 0, 0, 0, 0, 0, -5,
    -5, 0, 0, 0, 0, 0, 0, -5,
    -5, 0, 0, 0, 0, 0, 0, -5,
    -5, 0, 0, 0, 0, 0, 0, -5,
    0, 0, 0, 5, 5, 0, 0, 0,
];
const PST_QUEEN = [
    -20, -10, -10, -5, -5, -10, -10, -20,
    -10, 0, 0, 5, 5, 0, 0, -10,
    -10, 0, 5, 5, 5, 5, 0, -10,
    -5, 0, 5, 5, 5, 5, 0, -5,
    0, 0, 5, 5, 5, 5, 0, -5,
    -10, 5, 5, 5, 5, 5, 0, -10,
    -10, 0, 5, 0, 0, 0, 0, -10,
    -20, -10, -10, -5, -5, -10, -10, -20,
];
const PST_KING = [
    -30, -40, -40, -50, -50, -40, -40, -30,
    -30, -40, -40, -50, -50, -40, -40, -30,
    -30, -40, -40, -50, -50, -40, -40, -30,
    -30, -40, -40, -50, -50, -40, -40, -30,
    -20, -30, -30, -40, -40, -30, -30, -20,
    -10, -20, -20, -20, -20, -20, -20, -10,
    20, 20, 0, 0, 0, 0, 20, 20,
    20, 30, 10, 0, 0, 10, 30, 20,
];
const PST = {
    P: [PST_PAWN, [...PST_PAWN].reverse()],
    N: [PST_KNIGHT, [...PST_KNIGHT].reverse()],
    B: [PST_BISHOP, [...PST_BISHOP].reverse()],
    R: [PST_ROOK, [...PST_ROOK].reverse()],
    Q: [PST_QUEEN, [...PST_QUEEN].reverse()],
    K: [PST_KING, [...PST_KING].reverse()],
};
const PIECE_VALUES = { P: 100, N: 320, B: 330, R: 500, Q: 900, K: 0 };
// ============================================================
// Position class
// ============================================================
class Position {
    constructor(fen) {
        this.board = new Array(64).fill(null);
        this.turn = "w";
        this.castlingRights = { wK: true, wQ: true, bK: true, bQ: true };
        this.enPassantSquare = null;
        this.halfMoveClock = 0;
        this.fullMoveNumber = 1;
        this.moveHistory = [];
        this.snapshots = [];
        if (fen)
            this.loadFromFen(fen);
    }
    loadFromFen(fen) {
        this.board = new Array(64).fill(null);
        const parts = fen.split(/\s+/);
        const pos = parts[0];
        let sq = 0;
        for (const ch of pos) {
            if (ch === '/') {
                sq++;
                continue;
            }
            if (ch >= '1' && ch <= '8') {
                sq += +ch;
                continue;
            }
            const color = ch === ch.toUpperCase() ? "w" : "b";
            this.board[sq] = { type: ch.toUpperCase(), color };
            sq++;
        }
        this.turn = (parts[1] || "w");
        const cr = parts[2] || "KQkq";
        this.castlingRights = {
            wK: cr.includes("K"), wQ: cr.includes("Q"),
            bK: cr.includes("k"), bQ: cr.includes("q"),
        };
        this.enPassantSquare = (parts[3] && parts[3] !== "-") ? (0, types_1.algebraicToIdx)(parts[3]) : null;
        this.halfMoveClock = parseInt(parts[4] || "0");
        this.fullMoveNumber = parseInt(parts[5] || "1");
        this.moveHistory = [];
        this.snapshots = [];
    }
    toFen() {
        let fen = "";
        for (let rank = 7; rank >= 0; rank--) {
            let empty = 0;
            for (let file = 0; file < 8; file++) {
                const piece = this.board[rank * 8 + file];
                if (!piece) {
                    empty++;
                }
                else {
                    if (empty > 0) {
                        fen += empty.toString();
                        empty = 0;
                    }
                    fen += piece.color === "w" ? piece.type : piece.type.toLowerCase();
                }
            }
            if (empty > 0)
                fen += empty;
            if (rank > 0)
                fen += "/";
        }
        fen += " " + this.turn;
        const cr = [];
        if (this.castlingRights.wK)
            cr.push("K");
        if (this.castlingRights.wQ)
            cr.push("Q");
        if (this.castlingRights.bK)
            cr.push("k");
        if (this.castlingRights.bQ)
            cr.push("q");
        fen += " " + (cr.length ? cr.join("") : "-");
        fen += " " + (this.enPassantSquare !== null ? (0, types_1.idxToAlgebraic)(this.enPassantSquare) : "-");
        fen += " " + this.halfMoveClock + " " + this.fullMoveNumber;
        return fen;
    }
    isInCheck(color) {
        const king = this.findKing(color);
        return king >= 0 && this.isAttackedBy(king, color === "w" ? "b" : "w");
    }
    findKing(color) {
        for (let i = 0; i < 64; i++) {
            const p = this.board[i];
            if (p && p.type === "K" && p.color === color)
                return i;
        }
        return -1;
    }
    isAttackedBy(idx, color) {
        for (let i = 0; i < 64; i++) {
            const p = this.board[i];
            if (!p || p.color !== color)
                continue;
            if (this.pieceAttacks(i, idx, p))
                return true;
        }
        return false;
    }
    pieceAttacks(from, to, piece) {
        switch (piece.type) {
            case "P": {
                const dir = piece.color === "w" ? -1 : 1;
                const f = (0, types_1.idxToSquare)(from), t = (0, types_1.idxToSquare)(to);
                return Math.abs(f.file - t.file) === 1 && t.rank - f.rank === dir;
            }
            case "N": {
                const ff = (0, types_1.idxToSquare)(from).file, fr = (0, types_1.idxToSquare)(from).rank;
                const tf = (0, types_1.idxToSquare)(to).file, tr = (0, types_1.idxToSquare)(to).rank;
                return (Math.abs(ff - tf) === 1 && Math.abs(fr - tr) === 2) ||
                    (Math.abs(ff - tf) === 2 && Math.abs(fr - tr) === 1);
            }
            case "B": return this.slidingAttacks(from, to, [[-1, -1], [1, -1], [-1, 1], [1, 1]]);
            case "R": return this.slidingAttacks(from, to, [[-1, 0], [1, 0], [0, -1], [0, 1]]);
            case "Q": return this.slidingAttacks(from, to, [[-1, -1], [1, -1], [-1, 1], [1, 1], [-1, 0], [1, 0], [0, -1], [0, 1]]);
            case "K": {
                const ff = (0, types_1.idxToSquare)(from).file, fr = (0, types_1.idxToSquare)(from).rank;
                const tf = (0, types_1.idxToSquare)(to).file, tr = (0, types_1.idxToSquare)(to).rank;
                return Math.abs(ff - tf) <= 1 && Math.abs(fr - tr) <= 1;
            }
        }
        return false;
    }
    slidingAttacks(from, to, dirs) {
        let cf = (0, types_1.idxToSquare)(from).file, cr = (0, types_1.idxToSquare)(from).rank;
        for (const [df, dr] of dirs) {
            let f = cf + df, r = cr + dr;
            while (f >= 0 && f <= 7 && r >= 0 && r <= 7) {
                if (r * 8 + f === to)
                    return true;
                f += df;
                r += dr;
            }
        }
        return false;
    }
    // --- Legal move generation ---
    generateLegalMoves(color) {
        const c = color ?? this.turn;
        const moves = [];
        for (let i = 0; i < 64; i++) {
            const p = this.board[i];
            if (!p || p.color !== c)
                continue;
            for (const m of this.genPseudo(i)) {
                if (this.isLegal(m))
                    moves.push(m);
            }
        }
        return moves;
    }
    genPseudo(from) {
        const piece = this.board[from];
        if (!piece)
            return [];
        const moves = [];
        switch (piece.type) {
            case "P":
                this.pawnMoves(from, piece, moves);
                break;
            case "N":
                this.knightMoves(from, piece, moves);
                break;
            case "B":
                this.bishopMoves(from, piece, moves);
                break;
            case "R":
                this.rookMoves(from, piece, moves);
                break;
            case "Q":
                this.queenMoves(from, piece, moves);
                break;
            case "K":
                this.kingMoves(from, piece, moves);
                break;
        }
        return moves;
    }
    inBounds(f, r) {
        return f >= 0 && f <= 7 && r >= 0 && r <= 7;
    }
    pawnMoves(from, piece, moves) {
        const dir = piece.color === "w" ? -1 : 1;
        const startRank = piece.color === "w" ? 6 : 1;
        const { file, rank } = (0, types_1.idxToSquare)(from);
        // Forward one
        const f1 = (rank + dir) * 8 + file;
        if (this.inBounds(file, rank + dir) && f1 >= 0 && f1 < 64 && !this.board[f1]) {
            if (rank + dir === (piece.color === "w" ? 7 : 0)) {
                for (const pt of ["Q", "R", "B", "N"])
                    moves.push({ from, to: f1, piece, captured: null, flags: types_1.MoveFlags.promotion(pt), promotionType: pt });
            }
            else {
                moves.push({ from, to: f1, piece, captured: null, flags: types_1.MoveFlags.normal() });
                // Double push
                if (rank === startRank) {
                    const f2 = from + dir * 16;
                    if (this.inBounds(file, rank + dir * 2) && f2 >= 0 && f2 < 64 && !this.board[f2])
                        moves.push({ from, to: f2, piece, captured: null, flags: types_1.MoveFlags.doublePawn() });
                }
            }
        }
        // Captures and en passant
        for (const df of [-1, 1]) {
            const tf = file + df, tr = rank + dir;
            if (!this.inBounds(tf, tr))
                continue;
            const to = tr * 8 + tf;
            const target = this.board[to];
            if (target && target.color !== piece.color) {
                if (rank + dir === (piece.color === "w" ? 7 : 0)) {
                    for (const pt of ["Q", "R", "B", "N"])
                        moves.push({ from, to, piece, captured: target, flags: types_1.MoveFlags.promotion(pt), promotionType: pt });
                }
                else {
                    moves.push({ from, to, piece, captured: target, flags: types_1.MoveFlags.normal() });
                }
            }
            if (this.enPassantSquare !== null && to === this.enPassantSquare) {
                const cp = this.board[from + dir * 8];
                if (cp)
                    moves.push({ from, to, piece, captured: cp, flags: types_1.MoveFlags.enPassant() });
            }
        }
    }
    knightMoves(from, piece, moves) {
        const offs = [[-2, -1], [-2, 1], [-1, -2], [-1, 2], [1, -2], [1, 2], [2, -1], [2, 1]];
        for (const [df, dr] of offs) {
            const { file, rank } = (0, types_1.idxToSquare)(from);
            const nf = file + df, nr = rank + dr;
            if (!this.inBounds(nf, nr))
                continue;
            const to = nr * 8 + nf;
            const t = this.board[to];
            if (t && t.color === piece.color)
                continue;
            moves.push({ from, to, piece, captured: t, flags: types_1.MoveFlags.normal() });
        }
    }
    slideMoves(from, piece, moves, dirs) {
        const { file, rank } = (0, types_1.idxToSquare)(from);
        for (const [df, dr] of dirs) {
            let f = file + df, r = rank + dr;
            while (this.inBounds(f, r)) {
                const to = r * 8 + f;
                const t = this.board[to];
                if (t) {
                    if (t.color !== piece.color)
                        moves.push({ from, to, piece, captured: t, flags: types_1.MoveFlags.normal() });
                    break;
                }
                moves.push({ from, to, piece, captured: null, flags: types_1.MoveFlags.normal() });
                f += df;
                r += dr;
            }
        }
    }
    bishopMoves(f, p, m) { this.slideMoves(f, p, m, [[-1, -1], [1, -1], [-1, 1], [1, 1]]); }
    rookMoves(f, p, m) { this.slideMoves(f, p, m, [[-1, 0], [1, 0], [0, -1], [0, 1]]); }
    queenMoves(f, p, m) { this.slideMoves(f, p, m, [[-1, -1], [1, -1], [-1, 1], [1, 1], [-1, 0], [1, 0], [0, -1], [0, 1]]); }
    kingMoves(from, piece, moves) {
        const offs = [[-1, -1], [-1, 0], [-1, 1], [0, -1], [0, 1], [1, -1], [1, 0], [1, 1]];
        for (const [df, dr] of offs) {
            const { file, rank } = (0, types_1.idxToSquare)(from);
            const nf = file + df, nr = rank + dr;
            if (!this.inBounds(nf, nr))
                continue;
            const to = nr * 8 + nf;
            const t = this.board[to];
            if (t && t.color === piece.color)
                continue;
            moves.push({ from, to, piece, captured: t, flags: types_1.MoveFlags.normal() });
        }
        // Castling
        const opp = piece.color === "w" ? "b" : "w";
        const rb = piece.color === "w" ? 7 : 0;
        const wK = piece.color === "w" ? "wK" : "bK";
        const wQ = piece.color === "w" ? "wQ" : "bQ";
        const cKR = this.castlingRights;
        if (cKR[wK]) {
            const g = rb * 8 + 6, h = rb * 8 + 7;
            if (!this.board[g] && !this.board[h]) {
                if (!this.isAttackedBy(from, opp) && !this.isAttackedBy(g, opp) && !this.isAttackedBy(h, opp))
                    moves.push({ from, to: g, piece, captured: null, flags: types_1.MoveFlags.castleK() });
            }
        }
        if (cKR[wQ]) {
            const d = rb * 8 + 3, c = rb * 8 + 2, b = rb * 8 + 1;
            if (!this.board[d] && !this.board[c] && !this.board[b]) {
                if (!this.isAttackedBy(from, opp) && !this.isAttackedBy(d, opp) && !this.isAttackedBy(c, opp))
                    moves.push({ from, to: c, piece, captured: null, flags: types_1.MoveFlags.castleQ() });
            }
        }
    }
    isLegal(move) {
        this.snapshots.push({
            board: [...this.board], turn: this.turn,
            castlingRights: { ...this.castlingRights },
            enPassantSquare: this.enPassantSquare,
            halfMoveClock: this.halfMoveClock, fullMoveNumber: this.fullMoveNumber,
        });
        this.applyMove(move);
        const check = this.isInCheck(move.piece.color);
        const s = this.snapshots.pop();
        Object.assign(this, s);
        return !check;
    }
    applyMove(move) {
        const { from, to, piece, flags } = move;
        const cap = move.captured;
        this.board[to] = { type: move.promotionType ?? piece.type, color: piece.color };
        this.board[from] = null;
        if (flags.enPassant)
            this.board[from + (piece.color === "w" ? -8 : 8)] = null;
        if (flags.castleK) {
            const rk = (0, types_1.idxToSquare)(from).rank;
            this.board[rk * 8 + 5] = this.board[rk * 8 + 7];
            this.board[rk * 8 + 7] = null;
        }
        if (flags.castleQ) {
            const rk = (0, types_1.idxToSquare)(from).rank;
            this.board[rk * 8 + 3] = this.board[rk * 8 + 0];
            this.board[rk * 8 + 0] = null;
        }
        if (piece.type === "K") {
            if (piece.color === "w") {
                this.castlingRights.wK = false;
                this.castlingRights.wQ = false;
            }
            else {
                this.castlingRights.bK = false;
                this.castlingRights.bQ = false;
            }
        }
        if (piece.type === "R") {
            if (from === H8)
                this.castlingRights.wQ = false;
            if (from === A8)
                this.castlingRights.wK = false;
            if (from === H1)
                this.castlingRights.bQ = false;
            if (from === A1)
                this.castlingRights.bK = false;
        }
        if (cap) {
            if (to === H8)
                this.castlingRights.wQ = false;
            if (to === A8)
                this.castlingRights.wK = false;
            if (to === H1)
                this.castlingRights.bQ = false;
            if (to === A1)
                this.castlingRights.bK = false;
        }
        this.enPassantSquare = flags.doublePawn ? from + (piece.color === "w" ? -8 : 8) : null;
        if (piece.type === "P" || cap)
            this.halfMoveClock = 0;
        else
            this.halfMoveClock++;
        if (this.turn === "b")
            this.fullMoveNumber++;
        this.turn = this.turn === "w" ? "b" : "w";
    }
    makeMove(move) {
        this.moveHistory.push(move);
        this.snapshots.push({
            board: [...this.board], turn: this.turn,
            castlingRights: { ...this.castlingRights },
            enPassantSquare: this.enPassantSquare,
            halfMoveClock: this.halfMoveClock, fullMoveNumber: this.fullMoveNumber,
        });
        this.applyMove(move);
    }
    undoMove() {
        if (this.snapshots.length === 0)
            return null;
        const s = this.snapshots.pop();
        Object.assign(this, s);
        return this.moveHistory.pop() ?? null;
    }
    isCheckmate(color) {
        const c = color ?? this.turn;
        return this.generateLegalMoves(c).length === 0 && this.isInCheck(c);
    }
    isStalemate(color) {
        const c = color ?? this.turn;
        return this.generateLegalMoves(c).length === 0 && !this.isInCheck(c);
    }
    isDraw() {
        if (this.halfMoveClock >= 100)
            return "50-move rule";
        if (this.isStalemate())
            return "Stalemate";
        if (this.isCheckmate())
            return "Checkmate";
        const pieces = [];
        for (const p of this.board)
            if (p)
                pieces.push(p);
        const nk = pieces.filter(p => p.type !== "K");
        if (nk.length === 0)
            return "Insufficient material";
        if (nk.length === 1 && (nk[0].type === "B" || nk[0].type === "N"))
            return "Insufficient material";
        return null;
    }
    evaluate() {
        let score = 0;
        for (let i = 0; i < 64; i++) {
            const p = this.board[i];
            if (!p)
                continue;
            const m = p.color === "w" ? 1 : -1;
            score += (PIECE_VALUES[p.type] + PST[p.type][p.color === "w" ? 0 : 1][i]) * m;
        }
        return score;
    }
}
exports.Position = Position;
};

// ── module: src/ai.ts ──
__mods["src/ai.ts"] = function (exports, require, module) {
"use strict";
// ============================================================
// AI — Minimax with Alpha-Beta, move ordering, quiescence-lite
// ============================================================
Object.defineProperty(exports, "__esModule", { value: true });
exports.searchAI = searchAI;
const TINY = -999999;
const INF = 999999;
function isCapture(move) {
    return move.captured !== null;
}
const PIECE_VALUES = { P: 100, N: 320, B: 330, R: 500, Q: 900, K: 0 };
function moveValue(move) {
    if (isCapture(move)) {
        return (PIECE_VALUES[move.captured.type] || 0) * 10 + 1000;
    }
    return 0;
}
function orderMoves(moves) {
    return [...moves].sort((a, b) => moveValue(b) - moveValue(a));
}
function applyMove(pos, move) {
    pos.makeMove(move);
}
function undo(pos, move) {
    pos.undoMove();
}
function quiescence(pos, alpha, beta, depth, ply) {
    if (ply >= depth * 2)
        return pos.evaluate();
    const standPat = pos.evaluate();
    if (standPat >= beta)
        return beta;
    if (standPat > alpha)
        alpha = standPat;
    const captures = pos.generateLegalMoves().filter(m => isCapture(m));
    // Already sorted via orderMoves
    for (const move of captures) {
        applyMove(pos, move);
        const score = -quiescence(pos, -beta, -alpha, depth, ply + 1);
        undo(pos, move);
        if (score >= beta)
            return beta;
        if (score > alpha)
            alpha = score;
    }
    return alpha;
}
function searchAI(pos, depth = 3) {
    const moves = orderMoves(pos.generateLegalMoves());
    if (moves.length === 0) {
        return { move: null, score: pos.isCheckmate() ? -99999 : 0, depth };
    }
    // Time limit to keep responsive
    const startTime = performance.now();
    const timeLimit = 1000; // 1 second
    let bestMove = moves[0];
    let bestScore = -INF;
    let alpha = -INF;
    let beta = INF;
    let nodesSearched = 0;
    for (const move of moves) {
        applyMove(pos, move);
        const score = -negamax(pos, depth - 1, -beta, -alpha, 0);
        undo(pos, move);
        nodesSearched++;
        if (score > bestScore) {
            bestScore = score;
            bestMove = move;
        }
        if (score > alpha)
            alpha = score;
        if (performance.now() - startTime > timeLimit)
            break;
    }
    return { move: bestMove, score: bestScore, depth };
}
function negamax(pos, depth, alpha, beta, ply) {
    const isMax = pos.turn === "w";
    if (depth === 0) {
        return quiescence(pos, alpha, beta, 3, ply);
    }
    const moves = orderMoves(pos.generateLegalMoves());
    if (moves.length === 0) {
        return pos.isInCheck(pos.turn) ? -99999 - ply : 0;
    }
    for (const move of moves) {
        applyMove(pos, move);
        const score = -negamax(pos, depth - 1, -beta, -alpha, ply + 1);
        undo(pos, move);
        if (score > alpha)
            alpha = score;
        if (alpha >= beta)
            return alpha;
    }
    return alpha;
}
};

// ── module: src/audio.ts ──
__mods["src/audio.ts"] = function (exports, require, module) {
"use strict";
// ============================================================
// Audio — Web Audio SFX
// ============================================================
Object.defineProperty(exports, "__esModule", { value: true });
exports.AudioController = void 0;
class AudioController {
    constructor() {
        this.ctx = null;
        this.muted = false;
    }
    getCtx() {
        if (!this.ctx) {
            this.ctx = new AudioContext();
        }
        return this.ctx;
    }
    play(type) {
        if (this.muted)
            return;
        try {
            const ctx = this.getCtx();
            const osc = ctx.createOscillator();
            const gain = ctx.createGain();
            osc.connect(gain);
            gain.connect(ctx.destination);
            switch (type) {
                case "move":
                    osc.frequency.value = 300;
                    gain.gain.value = 0.1;
                    gain.gain.exponentialRampToValueAtTime(0.001, ctx.currentTime + 0.1);
                    osc.start(ctx.currentTime);
                    osc.stop(ctx.currentTime + 0.1);
                    break;
                case "capture":
                    osc.frequency.value = 200;
                    gain.gain.value = 0.15;
                    gain.gain.exponentialRampToValueAtTime(0.001, ctx.currentTime + 0.15);
                    osc.start(ctx.currentTime);
                    osc.stop(ctx.currentTime + 0.15);
                    break;
                case "check":
                    osc.frequency.value = 440;
                    gain.gain.value = 0.12;
                    gain.gain.exponentialRampToValueAtTime(0.001, ctx.currentTime + 0.2);
                    osc.start(ctx.currentTime);
                    osc.stop(ctx.currentTime + 0.2);
                    break;
                case "castle":
                    osc.frequency.value = 250;
                    gain.gain.value = 0.1;
                    gain.gain.exponentialRampToValueAtTime(0.001, ctx.currentTime + 0.12);
                    osc.start(ctx.currentTime);
                    osc.stop(ctx.currentTime + 0.12);
                    break;
            }
        }
        catch (e) { /* Audio not available */ }
    }
    toggleMute() {
        this.muted = !this.muted;
        return this.muted;
    }
    isMuted() {
        return this.muted;
    }
}
exports.AudioController = AudioController;
};

// ── module: src/renderer.ts ──
__mods["src/renderer.ts"] = function (exports, require, module) {
"use strict";
// ============================================================
// Renderer — Board rendering, highlighting, drag-and-drop
// ============================================================
Object.defineProperty(exports, "__esModule", { value: true });
exports.Renderer = void 0;
const types_1 = require("./types");
const PIECE_UNICODE = {
    K: '♔', Q: '♕', R: '♖', B: '♗', N: '♘', P: '♙',
};
const PIECE_UNICODE_BLACK = {
    K: '♚', Q: '♛', R: '♜', B: '♝', N: '♞', P: '♟',
};
class Renderer {
    constructor() {
        this.boardEl = document.getElementById('board');
        this.capturedWhite = document.getElementById('captured-white');
        this.capturedBlack = document.getElementById('captured-black');
        this.moveListEl = document.getElementById('move-list');
        this.statusEl = document.getElementById('status');
    }
    render(pos, highlights, flipped = false) {
        this.boardEl.innerHTML = '';
        for (let i = 0; i < 64; i++) {
            const rank = Math.floor(i / 8);
            const file = i % 8;
            const displayRank = flipped ? 7 - rank : rank;
            const displayFile = flipped ? 7 - file : file;
            const displayIdx = flipped ? (7 - rank) * 8 + (7 - file) : i;
            const sq = document.createElement('div');
            sq.className = `square ${this.isLight(displayIdx) ? 'light' : 'dark'}`;
            sq.dataset.index = displayIdx.toString();
            // Last move highlight
            if (highlights.lastMove) {
                if (displayIdx === highlights.lastMove.from || displayIdx === highlights.lastMove.to) {
                    sq.classList.add('last-move');
                }
            }
            // Check highlight
            if (highlights.checkSquare === displayIdx) {
                sq.classList.add('check');
            }
            // Selected square
            if (highlights.selected === displayIdx) {
                sq.classList.add('selected');
            }
            // Legal move indicator
            if (highlights.legalMoves.includes(displayIdx)) {
                if (pos.board[displayIdx]) {
                    const captureDot = document.createElement('div');
                    captureDot.className = 'legal-capture';
                    sq.appendChild(captureDot);
                }
                else {
                    const moveDot = document.createElement('div');
                    moveDot.className = 'legal-move';
                    sq.appendChild(moveDot);
                }
            }
            // Piece
            const piece = pos.board[displayIdx];
            if (piece) {
                const pieceEl = document.createElement('div');
                pieceEl.className = `piece ${piece.color === 'w' ? 'white' : 'black'}`;
                pieceEl.textContent = piece.color === 'w' ? PIECE_UNICODE[piece.type] : PIECE_UNICODE_BLACK[piece.type];
                pieceEl.dataset.index = displayIdx.toString();
                sq.appendChild(pieceEl);
            }
            this.boardEl.appendChild(sq);
        }
        // Update captured pieces
        this.updateCaptured(pos);
        // Add drag-and-drop listeners
        this.setupDragAndDrop();
    }
    isLight(idx) {
        const { file, rank } = (0, types_1.idxToAlgebraic)(idx) ? { file: idx % 8, rank: Math.floor(idx / 8) } : { file: 0, rank: 0 };
        return (file + rank) % 2 === 0;
    }
    updateCaptured(pos) {
        const whiteCaptured = [];
        const blackCaptured = [];
        // Count pieces
        const pieceCounts = {};
        for (const p of pos.board) {
            if (p) {
                const key = p.color + p.type;
                pieceCounts[key] = (pieceCounts[key] || 0) + 1;
            }
        }
        // Calculate captured by comparison to starting position
        const startPieces = [
            'wP', 'wP', 'wP', 'wP', 'wP', 'wP', 'wP', 'wP',
            'wN', 'wN', 'wB', 'wB', 'wR', 'wR', 'wQ', 'wK',
            'bP', 'bP', 'bP', 'bP', 'bP', 'bP', 'bP', 'bP',
            'bN', 'bN', 'bB', 'bB', 'bR', 'bR', 'bQ', 'bK'
        ];
        startPieces.forEach(key => {
            const count = pieceCounts[key] || 0;
            const missing = 8 - key.startsWith('w') ? count : count;
            // Simplified: just show pieces not on board
        });
        // Simpler approach: just track captures from history
        this.capturedWhite.innerHTML = '';
        this.capturedBlack.innerHTML = '';
    }
    setupDragAndDrop() {
        const pieces = this.boardEl.querySelectorAll('.piece');
        pieces.forEach(piece => {
            piece.addEventListener('dragstart', (e) => {
                const idx = parseInt(e.target.dataset.index || '0');
                e.dataTransfer.setData('text/plain', idx.toString());
            });
        });
        const squares = this.boardEl.querySelectorAll('.square');
        squares.forEach(sq => {
            sq.addEventListener('dragover', (e) => {
                e.preventDefault();
            });
            sq.addEventListener('drop', (e) => {
                e.preventDefault();
                const fromIdx = parseInt(e.dataTransfer.getData('text/plain') || '0');
                const toIdx = parseInt(sq.dataset.index || '0');
                if (window.gameManager) {
                    window.gameManager.handleMove(fromIdx, toIdx);
                }
            });
        });
    }
    setStatus(msg) {
        this.statusEl.textContent = msg;
    }
    setMoveList(moves) {
        this.moveListEl.innerHTML = '';
        for (let i = 0; i < moves.length; i += 2) {
            const moveNum = document.createElement('div');
            moveNum.className = 'move-row';
            moveNum.innerHTML = `<span class="move-num">${Math.floor(i / 2) + 1}.</span> ` +
                `<span class="move-white">${moves[i] || ''}</span>` +
                `<span class="move-black">${moves[i + 1] || ''}</span>`;
            this.moveListEl.appendChild(moveNum);
        }
        this.moveListEl.scrollTop = this.moveListEl.scrollHeight;
    }
}
exports.Renderer = Renderer;
};

// ── module: src/game.ts ──
__mods["src/game.ts"] = function (exports, require, module) {
"use strict";
// ============================================================
// Game Manager — UI logic, move handling, persistence
// ============================================================
Object.defineProperty(exports, "__esModule", { value: true });
exports.GameManager = void 0;
const engine_1 = require("./engine");
const ai_1 = require("./ai");
const audio_1 = require("./audio");
const renderer_1 = require("./renderer");
const types_1 = require("./types");
class GameManager {
    constructor() {
        this.selectedSquare = null;
        this.legalMoves = [];
        this.flipped = false;
        this.playerColor = 'w';
        this.aiDepth = 3;
        this.gameOver = false;
        this.moveHistory = [];
        this.lastMove = null;
        this.promotionCallback = null;
        this.pos = new engine_1.Position();
        this.renderer = new renderer_1.Renderer();
        this.audio = new audio_1.AudioController();
        this.loadGameState();
        this.render();
    }
    render() {
        const highlights = {
            legalMoves: this.legalMoves.map(m => m.to),
            lastMove: this.lastMove,
            checkSquare: this.pos.isInCheck(this.pos.turn) ? this.pos.findKing(this.pos.turn) : null,
            selected: this.selectedSquare,
        };
        this.renderer.render(this.pos, highlights, this.flipped);
        this.renderer.setStatus(this.getStatusText());
        // Convert moves to algebraic notation for display
        const moveNotations = this.moveHistory.map(m => m.notation);
        this.renderer.setMoveList(moveNotations);
    }
    getStatusText() {
        if (this.gameOver) {
            return this.pos.isCheckmate() ?
                `Checkmate! ${this.pos.turn === 'w' ? 'Black' : 'White'} wins` :
                this.pos.isStalemate() ? 'Stalemate' : 'Draw';
        }
        const colorName = this.pos.turn === 'w' ? 'White' : 'Black';
        const inCheck = this.pos.isInCheck(this.pos.turn) ? ' (Check!)' : '';
        return `${colorName}'s turn${inCheck}`;
    }
    handleSquareClick(idx) {
        if (this.gameOver)
            return;
        const clickedPiece = this.pos.board[idx];
        // If a square is already selected and we click on a legal move
        if (this.selectedSquare !== null) {
            const move = this.legalMoves.find(m => m.from === this.selectedSquare && m.to === idx);
            if (move) {
                this.executeMove(move);
                return;
            }
        }
        // Select a piece
        if (clickedPiece && clickedPiece.color === this.pos.turn) {
            this.selectedSquare = idx;
            this.legalMoves = this.pos.generateLegalMoves().filter(m => m.from === idx);
            this.render();
            return;
        }
        // Deselect
        this.selectedSquare = null;
        this.legalMoves = [];
        this.render();
    }
    executeMove(move) {
        this.pos.makeMove(move);
        this.lastMove = move;
        this.moveHistory.push({
            from: move.from,
            to: move.to,
            piece: move.promotionType || move.piece.type,
            captured: move.captured?.type || null,
            notation: this.generateMoveNotation(move),
        });
        // Play sound
        if (move.captured) {
            if (move.flags.castleK || move.flags.castleQ) {
                this.audio.play('castle');
            }
            else {
                this.audio.play('capture');
            }
        }
        else if (this.pos.isInCheck(this.pos.turn === 'w' ? 'b' : 'w')) {
            this.audio.play('check');
        }
        else {
            this.audio.play('move');
        }
        this.selectedSquare = null;
        this.legalMoves = [];
        this.checkGameState();
        this.render();
        this.saveGameState();
        // AI turn
        if (!this.gameOver && this.pos.turn !== this.playerColor) {
            setTimeout(() => this.makeAIMove(), 100);
        }
    }
    generateMoveNotation(move) {
        const piece = move.piece.type;
        const from = (0, types_1.idxToAlgebraic)(move.from);
        const to = (0, types_1.idxToAlgebraic)(move.to);
        if (move.flags.castleK)
            return 'O-O';
        if (move.flags.castleQ)
            return 'O-O-O';
        let notation = '';
        if (piece === 'P') {
            if (move.captured) {
                notation = from[0] + 'x' + to + (move.flags.enPassant ? ' e.p.' : '');
            }
            else {
                notation = to;
            }
        }
        else {
            notation = piece;
            if (move.captured) {
                notation += 'x' + to;
            }
        }
        if (move.promotionType) {
            notation += '=' + move.promotionType;
        }
        // Check if opponent is in check
        const oppTurn = move.piece.color === 'w' ? 'b' : 'w';
        if (this.pos.isInCheck(oppTurn)) {
            if (this.pos.isCheckmate(oppTurn)) {
                notation += '#';
            }
            else {
                notation += '+';
            }
        }
        return notation;
    }
    makeAIMove() {
        const result = (0, ai_1.searchAI)(this.pos, this.aiDepth);
        if (result.move) {
            this.pos.makeMove(result.move);
            this.lastMove = result.move;
            this.moveHistory.push({
                from: result.move.from,
                to: result.move.to,
                piece: result.move.promotionType || result.move.piece.type,
                captured: result.move.captured?.type || null,
                notation: this.generateMoveNotation(result.move),
            });
            if (result.move.captured) {
                this.audio.play('capture');
            }
            else if (this.pos.isInCheck(this.pos.turn)) {
                this.audio.play('check');
            }
            else {
                this.audio.play('move');
            }
        }
        this.checkGameState();
        this.render();
        this.saveGameState();
    }
    checkGameState() {
        const drawReason = this.pos.isDraw();
        if (drawReason) {
            this.gameOver = true;
            return;
        }
        if (this.pos.isCheckmate()) {
            this.gameOver = true;
        }
    }
    saveGameState() {
        try {
            const state = {
                fen: this.pos.toFen(),
                playerColor: this.playerColor,
                aiDepth: this.aiDepth,
                flipped: this.flipped,
            };
            localStorage.setItem('chess-state', JSON.stringify(state));
        }
        catch (e) { /* Storage not available */ }
    }
    loadGameState() {
        try {
            const saved = localStorage.getItem('chess-state');
            if (saved) {
                const state = JSON.parse(saved);
                this.pos.loadFromFen(state.fen);
                this.playerColor = state.playerColor || 'w';
                this.aiDepth = state.aiDepth || 3;
                this.flipped = state.flipped || false;
            }
        }
        catch (e) { /* No saved state */ }
    }
    handleMove(from, to) {
        if (this.gameOver)
            return;
        const move = this.legalMoves.find(m => m.from === from && m.to === to);
        if (move) {
            this.selectedSquare = from;
            this.executeMove(move);
        }
    }
    // Expose for drag-and-drop
    handleSquareClickWrapper(idx) {
        this.handleSquareClick(idx);
    }
    newGame() {
        this.pos = new engine_1.Position();
        this.selectedSquare = null;
        this.legalMoves = [];
        this.lastMove = null;
        this.gameOver = false;
        this.moveHistory = [];
        localStorage.removeItem('chess-state');
        this.render();
    }
    undoMove() {
        if (this.moveHistory.length === 0)
            return;
        // Undo AI move
        const aiMove = this.pos.undoMove();
        this.moveHistory.pop();
        // Undo player move
        const playerMove = this.pos.undoMove();
        if (playerMove) {
            this.moveHistory.pop();
        }
        this.lastMove = playerMove;
        this.selectedSquare = null;
        this.legalMoves = [];
        this.gameOver = false;
        this.render();
        this.saveGameState();
    }
    flipBoard() {
        this.flipped = !this.flipped;
        this.render();
        this.saveGameState();
    }
    toggleSound() {
        const muted = this.audio.toggleMute();
        const btn = document.getElementById('sound-btn');
        if (btn) {
            btn.textContent = muted ? '🔇' : '🔊';
        }
    }
    setDifficulty(depth) {
        this.aiDepth = depth;
        this.saveGameState();
    }
    setPlayerColor(color) {
        this.playerColor = color;
        this.newGame();
    }
    // Expose for external access
    getPos() {
        return this.pos;
    }
    getSelectedSquare() {
        return this.selectedSquare;
    }
    getLegalMoves() {
        return this.legalMoves;
    }
    getGameStatus() {
        return { gameOver: this.gameOver, pos: this.pos };
    }
}
exports.GameManager = GameManager;
// Expose game manager globally
window.gameManager = null;
};

// ── entry ──
__require("src/main.ts");
})();
</script>
</body>
</html>