← Chess results

Chess

nex agi Nex N2.5 mini GGUF · 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 44,939 bytes · SHA-256 91fada2c90e8
<!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/app.ts":{"./engine":"src/engine.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/engine.ts ──
__mods["src/engine.ts"] = function (exports, require, module) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Engine = exports.PieceType = exports.Color = void 0;
var Color;
(function (Color) {
    Color[Color["White"] = 0] = "White";
    Color[Color["Black"] = 1] = "Black";
})(Color || (exports.Color = Color = {}));
var PieceType;
(function (PieceType) {
    PieceType[PieceType["None"] = 0] = "None";
    PieceType[PieceType["Pawn"] = 1] = "Pawn";
    PieceType[PieceType["Knight"] = 2] = "Knight";
    PieceType[PieceType["Bishop"] = 3] = "Bishop";
    PieceType[PieceType["Rook"] = 4] = "Rook";
    PieceType[PieceType["Queen"] = 5] = "Queen";
    PieceType[PieceType["King"] = 6] = "King";
})(PieceType || (exports.PieceType = PieceType = {}));
const ROW = (a) => a >>> 4;
const COL = (a) => a & 15;
const SQ = (r, c) => (r << 4) | c;
const inside = (sq) => {
    const r = ROW(sq), c = COL(sq);
    return r >= 0 && r < 8 && c >= 0 && c < 8;
};
const other = (color) => color ^ 1;
const pawnStep = (color) => (color === Color.White ? -1 : 1);
const homeRank = (color) => (color === Color.White ? 7 : 0);
const startRank = (color) => (color === Color.White ? 6 : 1);
const PIECE_CHARS = ['', 'p', 'n', 'b', 'r', 'q', 'k'];
const PIECE_TYPES = { p: 1, n: 2, b: 3, r: 4, q: 5, k: 6 };
const WHITE_KING_CASTLE = 1;
const WHITE_QUEEN_CASTLE = 2;
const BLACK_KING_CASTLE = 4;
const BLACK_QUEEN_CASTLE = 8;
const KING_SIDE = 16;
const QUEEN_SIDE = 32;
const PROMOTION = 64;
const MATERIAL = {
    [PieceType.Pawn]: 100, [PieceType.Knight]: 320, [PieceType.Bishop]: 330,
    [PieceType.Rook]: 500, [PieceType.Queen]: 900, [PieceType.King]: 20000
};
const WHITE_PST = [
    [0, 0, 0, 0, 0, 0, 0, 0, 5, 5, 5, 5, 5, 5, 5, 5, 1, 1, 2, 3, 3, 2, 1, 1, 2, 2, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 5, 5, 5, 5, 5, 5, 5, 5, 0, 0, 0, 0, 0, 0, 0, 0]
];
const BLACK_PST = WHITE_PST.slice().reverse();
const ALL_DELTAS = [[1, 0], [-1, 0], [0, 1], [0, -1], [1, 1], [1, -1], [-1, 1], [-1, -1]];
const KNIGHT_DELTAS = [[2, 1], [2, -1], [-2, 1], [-2, -1], [1, 2], [1, -2], [-1, 2], [-1, -2]];
const BISHOP_DELTAS = [[1, 1], [1, -1], [-1, 1], [-1, -1]];
const ROOK_DELTAS = [[1, 0], [-1, 0], [0, 1], [0, -1]];
class Engine {
    constructor() {
        this.board = new Int8Array(64).fill(0);
        this.side = Color.White;
        this.castling = WHITE_KING_CASTLE | WHITE_QUEEN_CASTLE | BLACK_KING_CASTLE | BLACK_QUEEN_CASTLE;
        this.epSquare = -1;
        this.halfmove = 0;
        this.moveNumber = 1;
        this.moveList = [];
        this.lastMove = null;
        this.selected = -1;
        this.promoSquare = -1;
        this.gameOver = false;
        this.winner = null;
        this.drawReason = '';
        this.positionHashes = [];
    }
    fen() {
        let s = '';
        for (let r = 0; r < 8; r++) {
            let empties = 0;
            for (let c = 0; c < 8; c++) {
                const piece = this.board[SQ(r, c)];
                if (piece === 0)
                    empties++;
                else {
                    if (empties)
                        s += empties;
                    empties = 0;
                    s += PIECE_CHARS[Math.abs(piece)];
                }
            }
            if (empties)
                s += empties;
            s += '/';
        }
        s += this.side === Color.White ? 'w' : 'b';
        let rights = '';
        if (this.castling & WHITE_KING_CASTLE)
            rights += 'K';
        if (this.castling & WHITE_QUEEN_CASTLE)
            rights += 'Q';
        if (this.castling & BLACK_KING_CASTLE)
            rights += 'a';
        if (this.castling & BLACK_QUEEN_CASTLE)
            rights += 'q';
        s += rights || '-';
        s += ' ' + (this.epSquare >= 0 ? 'abcdefgh'[COL(this.epSquare)] + (8 - ROW(this.epSquare)) : '-') + ' ' + this.halfmove + ' ' + this.moveNumber;
        return s;
    }
    kingSquare(color) {
        const expected = color === Color.White ? 6 : -6;
        for (let i = 0; i < 64; i++)
            if (this.board[i] === expected)
                return i;
        return -1;
    }
    pawnAttacks(sq, by) {
        const r = ROW(sq), c = COL(sq), result = [];
        for (const dc of [-1, 1]) {
            const nr = r + pawnStep(by), nc = c + dc;
            if (inside(SQ(nr, nc)))
                result.push(SQ(nr, nc));
        }
        return result;
    }
    squareAttackedBy(sq, by) {
        const r = ROW(sq), c = COL(sq);
        for (const p of this.pawnAttacks(sq, by))
            if (this.board[p] === (by === Color.White ? 1 : -1))
                return true;
        for (const [dr, dc] of KNIGHT_DELTAS) {
            const nr = r + dr, nc = c + dc;
            if (inside(SQ(nr, nc)) && this.board[SQ(nr, nc)] === (by === Color.White ? 2 : -2))
                return true;
        }
        for (const [dr, dc] of ALL_DELTAS) {
            const nr = r + dr, nc = c + dc;
            if (inside(SQ(nr, nc)) && this.board[SQ(nr, nc)] === (by === Color.White ? 6 : -6))
                return true;
        }
        for (const [dr, dc] of BISHOP_DELTAS) {
            let nr = r + dr, nc = c + dc;
            while (inside(SQ(nr, nc))) {
                const piece = this.board[SQ(nr, nc)];
                if (piece !== 0) {
                    const wanted = by === Color.White ? 3 : -3;
                    if (piece === wanted || piece === (by === Color.White ? 5 : -5))
                        return true;
                    break;
                }
                nr += dr;
                nc += dc;
            }
        }
        for (const [dr, dc] of ROOK_DELTAS) {
            let nr = r + dr, nc = c + dc;
            while (inside(SQ(nr, nc))) {
                const piece = this.board[SQ(nr, nc)];
                if (piece !== 0) {
                    const wanted = by === Color.White ? 4 : -4;
                    if (piece === wanted || piece === (by === Color.White ? 5 : -5))
                        return true;
                    break;
                }
                nr += dr;
                nc += dc;
            }
        }
        return false;
    }
    inCheck(side = this.side) {
        return this.squareAttackedBy(this.kingSquare(side), other(side));
    }
    makeMove(from, to, promotion, doublePush = false, epCapture = -1, ep = false, castle = false, rookFrom = -1, rookTo = -1) {
        return {
            from, to, promotion,
            flags: (doublePush ? 2 : 0) | (ep ? 4 : 0) | (castle && this.board[from] > 0 ? KING_SIDE : castle && this.board[from] < 0 ? QUEEN_SIDE : 0) | (promotion ? PROMOTION : 0),
            captured: ep ? this.board[from + pawnStep(this.side)] : this.board[to],
            doublePush, epCapture, rookFrom: rookFrom >= 0 ? rookFrom : from, rookTo: rookTo >= 0 ? rookTo : to,
            prevHalfmove: this.halfmove, prevCastling: this.castling
        };
    }
    pseudoMovesFor(sq, color) {
        const result = [];
        const piece = this.board[sq];
        if (piece === 0 || (piece > 0) !== (color === Color.White))
            return result;
        const r = ROW(sq), c = COL(sq), step = pawnStep(color), home = homeRank(color), goal = startRank(color);
        if (Math.abs(piece) === PieceType.Pawn) {
            const one = sq + step * 8;
            if (inside(one) && this.board[one] === 0) {
                if (ROW(one) === goal)
                    for (const promo of ['Q', 'R', 'B', 'N'])
                        result.push(this.makeMove(sq, one, promo));
                else {
                    result.push(this.makeMove(sq, one));
                    if (r === home)
                        result.push(this.makeMove(sq, one + step * 8, undefined, true));
                }
            }
            for (const dc of [-1, 1]) {
                const nr = r + step, nc = c + dc;
                if (!inside(SQ(nr, nc)))
                    continue;
                const target = this.board[SQ(nr, nc)];
                if (target !== 0 && (target > 0) === (color === Color.White)) {
                    if (ROW(nr) === goal)
                        for (const promo of ['Q', 'R', 'B', 'N'])
                            result.push(this.makeMove(sq, SQ(nr, nc), promo));
                    else
                        result.push(this.makeMove(sq, SQ(nr, nc)));
                }
                else if (ROW(nr) === 5 && sq === this.epSquare)
                    result.push(this.makeMove(sq, SQ(nr, nc), undefined, false, SQ(nr, nc), true));
            }
        }
        else if (Math.abs(piece) === PieceType.Knight) {
            for (const [dr, dc] of KNIGHT_DELTAS) {
                const nr = r + dr, nc = c + dc;
                if (!inside(SQ(nr, nc)))
                    continue;
                const target = this.board[SQ(nr, nc)];
                if (target === 0 || (target > 0) === (color === Color.White))
                    result.push(this.makeMove(sq, SQ(nr, nc)));
            }
        }
        else if (Math.abs(piece) === PieceType.King) {
            for (const [dr, dc] of ALL_DELTAS) {
                const nr = r + dr, nc = c + dc;
                if (!inside(SQ(nr, nc)))
                    continue;
                const target = this.board[SQ(nr, nc)];
                if (target === 0 || (target > 0) === (color === Color.White))
                    result.push(this.makeMove(sq, SQ(nr, nc)));
            }
            const rook = color === Color.White ? 4 : -4, king = color === Color.White ? 6 : -6;
            if (this.board[sq] === king && this.board[SQ(r, c + 1)] === rook && this.board[SQ(r, c + 2)] === rook && this.board[SQ(r, c + 3)] === rook && this.board[SQ(r, c + 1)] === 0 && this.board[SQ(r, c + 2)] === 0 && this.board[SQ(r, c + 3)] === 0 && this.castling & (color === Color.White ? WHITE_KING_CASTLE : BLACK_KING_CASTLE) && !this.squareAttackedBy(sq, other(color)) && !this.squareAttackedBy(SQ(r, c + 1), other(color)) && !this.squareAttackedBy(SQ(r, c + 2), other(color)))
                result.push(this.makeMove(sq, SQ(r, c + 2), undefined, false, -1, false, true, SQ(r, 7), SQ(r, 5)));
            if (this.board[sq] === king && this.board[SQ(r, c - 1)] === rook && this.board[SQ(r, c - 2)] === rook && this.board[SQ(r, c - 3)] === rook && this.board[SQ(r, c - 1)] === 0 && this.board[SQ(r, c - 2)] === 0 && this.board[SQ(r, c - 3)] === 0 && this.castling & (color === Color.White ? WHITE_QUEEN_CASTLE : BLACK_QUEEN_CASTLE) && !this.squareAttackedBy(sq, other(color)) && !this.squareAttackedBy(SQ(r, c - 1), other(color)) && !this.squareAttackedBy(SQ(r, c - 2), other(color)))
                result.push(this.makeMove(sq, SQ(r, c - 2), undefined, false, -1, false, true, SQ(r, 0), SQ(r, 3)));
        }
        else {
            const dirs = Math.abs(piece) === PieceType.Bishop ? BISHOP_DELTAS : Math.abs(piece) === PieceType.Rook ? ROOK_DELTAS : ALL_DELTAS;
            for (const [dr, dc] of dirs) {
                let nr = r + dr, nc = c + dc;
                while (inside(SQ(nr, nc))) {
                    const target = this.board[SQ(nr, nc)];
                    if (target === 0)
                        result.push(this.makeMove(sq, SQ(nr, nc)));
                    else {
                        if ((target > 0) === (color === Color.White))
                            result.push(this.makeMove(sq, SQ(nr, nc)));
                        break;
                    }
                    nr += dr;
                    nc += dc;
                }
            }
        }
        return result;
    }
    legalMoves() {
        const side = this.side, result = [];
        for (let sq = 0; sq < 64; sq++) {
            const piece = this.board[sq];
            if (piece === 0 || (piece > 0) !== (side === Color.White))
                continue;
            for (const move of this.pseudoMovesFor(sq, side)) {
                const clone = this.clone();
                clone.applyMove(move, false);
                if (!clone.inCheck(side))
                    result.push(move);
            }
        }
        return result;
    }
    legalMoveCount() { return this.legalMoves().length; }
    clone() {
        const copy = new Engine();
        copy.board = this.board.slice();
        copy.side = this.side;
        copy.castling = this.castling;
        copy.epSquare = this.epSquare;
        copy.halfmove = this.halfmove;
        copy.moveNumber = this.moveNumber;
        copy.moveList = this.moveList.slice();
        copy.lastMove = this.lastMove;
        copy.selected = this.selected;
        copy.promoSquare = this.promoSquare;
        copy.gameOver = this.gameOver;
        copy.winner = this.winner;
        copy.drawReason = this.drawReason;
        copy.positionHashes = this.positionHashes.slice();
        return copy;
    }
    applyMoveToEngine(move) { this.applyMove(move); }
    applyMove(move, updateState = true) {
        const piece = this.board[move.from];
        if (piece === 0)
            return;
        const color = this.side, r = ROW(move.from), c = COL(move.from);
        this.board[move.from] = 0;
        this.board[move.to] = move.promotion ? (move.promotion === 'N' ? 2 : move.promotion === 'B' ? 3 : move.promotion === 'R' ? 4 : 5) : piece;
        if (move.flags & 4)
            this.board[move.from + pawnStep(color)] = 0;
        if (move.flags & KING_SIDE) {
            if (color === Color.White) {
                this.board[SQ(7, 4)] = piece;
                this.board[SQ(7, 7)] = 0;
                this.board[SQ(7, 5)] = 4;
                this.board[SQ(7, 6)] = -4;
            }
            else {
                this.board[SQ(0, 4)] = piece;
                this.board[SQ(0, 7)] = 0;
                this.board[SQ(0, 5)] = -4;
                this.board[SQ(0, 6)] = 4;
            }
        }
        else if (move.flags & QUEEN_SIDE) {
            if (color === Color.White) {
                this.board[SQ(7, 4)] = piece;
                this.board[SQ(7, 0)] = 0;
                this.board[SQ(7, 3)] = 4;
                this.board[SQ(7, 2)] = -4;
            }
            else {
                this.board[SQ(0, 4)] = piece;
                this.board[SQ(0, 0)] = 0;
                this.board[SQ(0, 3)] = -4;
                this.board[SQ(0, 2)] = 4;
            }
        }
        if (piece === 6 || piece === -6)
            this.castling = 0;
        if (move.from === SQ(7, 0) || move.to === SQ(7, 0))
            this.castling &= ~WHITE_KING_CASTLE;
        if (move.from === SQ(7, 7) || move.to === SQ(7, 7))
            this.castling &= ~WHITE_QUEEN_CASTLE;
        if (move.from === SQ(0, 0) || move.to === SQ(0, 0))
            this.castling &= ~BLACK_KING_CASTLE;
        if (move.from === SQ(0, 7) || move.to === SQ(0, 7))
            this.castling &= ~BLACK_QUEEN_CASTLE;
        this.halfmove = (Math.abs(piece) === PieceType.Pawn || (move.captured && move.captured !== 0)) ? 0 : this.halfmove + 1;
        this.epSquare = move.doublePush ? (move.from + move.to) >> 1 : -1;
        this.side = other(color);
        if (color === Color.White)
            this.moveNumber += 1;
        if (updateState) {
            this.lastMove = move;
            this.moveList.push(move);
            this.selected = -1;
            this.promoSquare = -1;
            this.positionHashes.push(this.hash());
            if (this.halfmove >= 100 || this.insufficientMaterial() || this.positionHashes.filter(h => h === this.hash()).length >= 3) {
                this.gameOver = true;
                this.drawReason = this.halfmove >= 100 ? 'fifty-move rule' : this.insufficientMaterial() ? 'insufficient material' : 'threefold repetition';
                return;
            }
            const moves = this.legalMoves();
            if (moves.length === 0) {
                this.gameOver = true;
                this.drawReason = this.inCheck() ? 'checkmate' : 'stalemate';
                if (this.inCheck())
                    this.winner = other(this.side);
            }
        }
    }
    insufficientMaterial() {
        const pieces = [];
        for (let i = 0; i < 64; i++)
            if (this.board[i] !== 0 && Math.abs(this.board[i]) !== PieceType.King)
                pieces.push(Math.abs(this.board[i]));
        if (pieces.length === 0)
            return true;
        if (pieces.length === 1 && (pieces[0] === PieceType.Bishop || pieces[0] === PieceType.Knight))
            return true;
        return false;
    }
    hash() {
        let value = 0;
        for (let i = 0; i < 64; i++)
            if (this.board[i] !== 0)
                value ^= (i + 1) * 131 + this.board[i] * 17;
        return value;
    }
    static pieceValue(type) { return MATERIAL[type] ?? 0; }
    static parseSquare(s) {
        if (!/^[a-h][1-8]$/.test(s || ''))
            return -1;
        return SQ(8 - (+s[1]), 'abcdefgh'.indexOf(s[0]));
    }
    move(ref) {
        const text = ref.trim().toLowerCase();
        const from = Engine.parseSquare(text.slice(0, 2)), to = Engine.parseSquare(text.slice(2, 4));
        if (from < 0 || to < 0)
            return null;
        const found = this.pseudoMovesFor(from, this.side).find(m => m.to === to && (!m.promotion || m.promotion.toUpperCase() === text[4]));
        if (!found)
            return null;
        this.applyMove(found);
        return found;
    }
    static fromFen(fen) {
        const engine = new Engine();
        const parts = fen.trim().split(/\s+/);
        let index = 0;
        for (const row of parts[0].split('/')) {
            for (const ch of row) {
                if (/\d/.test(ch))
                    index += +ch;
                else {
                    const color = ch === ch.toUpperCase() ? Color.White : Color.Black;
                    const type = PIECE_TYPES[ch.toLowerCase()];
                    engine.board[SQ(Math.floor(index / 8), index % 8)] = type === PieceType.Pawn ? (color === Color.White ? 1 : -1) : (color === Color.White ? type : -type);
                    index++;
                }
            }
        }
        engine.board = Int8Array.from(engine.board);
        engine.side = parts[1] === 'w' ? Color.White : Color.Black;
        engine.castling = 0;
        for (const ch of parts[2] || '') {
            if (ch === 'K')
                engine.castling |= WHITE_KING_CASTLE;
            if (ch === 'Q')
                engine.castling |= WHITE_QUEEN_CASTLE;
            if (ch === 'a')
                engine.castling |= BLACK_KING_CASTLE;
            if (ch === 'q')
                engine.castling |= BLACK_QUEEN_CASTLE;
        }
        engine.epSquare = parts[3] && parts[3] !== '-' ? SQ(8 - (+parts[3][1]), 'abcdefgh'.indexOf(parts[3][0])) : -1;
        engine.halfmove = +(parts[4] || '0');
        engine.moveNumber = +(parts[5] || '1');
        engine.positionHashes = [engine.hash()];
        return engine;
    }
}
exports.Engine = Engine;
};

// ── module: src/app.ts ──
__mods["src/app.ts"] = function (exports, require, module) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.App = void 0;
exports.bestAiMove = bestAiMove;
exports.evaluate = evaluate;
const engine_1 = require("./engine");
const STORAGE_KEY = 'vanilla-chess-state-v1';
const PIECE_GLYPHS = { kp: '♟', kn: '♞', kb: '♝', kr: '♜', kq: '♛', kk: '♚', wp: '♙', wn: '♘', wb: '♗', wr: '♖', wq: '♕', wk: '♔' };
const squareName = (sq) => 'abcdefgh'[sq & 15] + (8 - (sq >>> 4));
const sqFromName = (name) => (/^[a-h][1-8]$/.test(name || '') ? engine_1.Engine.parseSquare(name) : null);
const pieceGlyph = (engine, sq) => { const p = engine.board[sq]; return p === 0 ? '' : PIECE_GLYPHS[(p > 0 ? 'w' : 'b') + Math.abs(p)]; };
const shortMove = (engine, move) => {
    const p = Math.abs(engine.board[move.from]);
    const capture = move.captured !== undefined && move.captured !== 0;
    let text = p === 1 ? squareName(move.from)[0] + (capture ? 'x' : '') + squareName(move.to) : ('PNBR'[p - 2] + (capture ? 'x' : '') + squareName(move.to));
    if (move.promotion)
        text += '=' + move.promotion;
    if (move.flags & 16)
        text = 'O-O';
    if (move.flags & 32)
        text = 'O-O-O';
    return text;
};
class App {
    constructor(playerColor = engine_1.Color.White, aiColor = engine_1.Color.Black, aiDepth = 2, muted = false) {
        this.aiBusy = false;
        this.undoStack = [];
        this.lastMove = [];
        this.selected = '';
        this.promoSquare = null;
        this.engine = engine_1.Engine.fromFen('rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR');
        this.playerColor = playerColor;
        this.aiColor = aiColor;
        this.aiDepth = aiDepth;
        this.flipped = false;
        this.muted = muted;
        this.gameOver = false;
        this.winner = null;
        this.drawReason = '';
        this.status = { over: false, result: 'White to move' };
        this.soundEnabled = !muted;
        this.pieces = { board: Array.from(this.engine.board), side: this.engine.side, castling: this.engine.castling, epSquare: this.engine.epSquare, halfmove: this.engine.halfmove, moveNumber: this.engine.moveNumber, moveList: [], gameover: false, winner: null, drawReason: '', playerColor, aiColor, aiDepth, flipped: false, muted: false, lastMove: [], selected: '-1', promoSquare: null };
    }
    start(root) {
        this.root = root;
        this.pieces = this.loadPieces();
        this.restore(this.pieces);
        this.buildDom();
        this.bindDom();
        this.render();
        if (this.isAiTurn())
            queueMicrotask(() => this.aiMove());
    }
    loadPieces() { try {
        const raw = localStorage.getItem(STORAGE_KEY);
        if (!raw)
            return this.pieces;
        const parsed = JSON.parse(raw);
        return parsed && parsed.app ? parsed.app : this.pieces;
    }
    catch {
        return this.pieces;
    } }
    save() { try {
        localStorage.setItem(STORAGE_KEY, JSON.stringify({ app: this.pieces }));
    }
    catch { } }
    restore(app) {
        this.playerColor = app.playerColor;
        this.aiColor = app.aiColor;
        this.aiDepth = app.aiDepth;
        this.flipped = app.flipped;
        this.muted = app.muted;
        this.gameOver = app.gameover;
        this.winner = app.winner === null ? null : app.winner;
        this.drawReason = app.drawReason;
        this.soundEnabled = !app.muted;
        this.engine = engine_1.Engine.fromFen('');
        this.engine.side = app.side;
        this.engine.castling = app.castling;
        this.engine.epSquare = app.epSquare;
        this.engine.halfmove = app.halfmove;
        this.engine.moveNumber = app.moveNumber;
        this.engine.positionHashes = [];
        for (let i = 0; i < 64; i++)
            this.engine.board[i] = app.board[i];
        for (const moveRef of app.moveList)
            this.engine.move(moveRef);
        this.lastMove = app.lastMove.slice();
        this.promoSquare = app.promoSquare || null;
        this.gameOver = app.gameover;
        this.winner = app.winner;
        this.drawReason = app.drawReason;
    }
    buildDom() {
        if (!this.root)
            return;
        this.root.innerHTML = `<div class="app-shell">
      <header class="topbar"><div class="brand"><span class="brand-mark">◇</span><div><h1>Aurum Chess</h1><p>Classic rules · engine-backed play</p></div></div><div class="game-controls"><label>Play as <select id="player-color"><option value="0">White</option><option value="1">Black</option></select></label><label>AI depth <select id="ai-depth"><option value="1">Quick</option><option value="2">Balanced</option><option value="3">Strong</option></select></label><button id="new-game" class="button button-primary">New game</button><button id="undo" class="button">Undo</button><button id="flip" class="button icon-button" title="Flip board">⇅</button><button id="mute" class="button icon-button" title="Toggle sound">♪</button></div></header>
      <main class="game-layout"><section class="board-column"><div class="board-frame"><div id="board" class="board" aria-label="Chess board"></div><div id="promotion" class="promotion-overlay" hidden><p>Select promotion</p><div class="promo-pieces"></div></div><div id="game-banner" class="game-banner" hidden></div></div><div class="board-caption"><span class="dot-dot"></span>Drag pieces or click a highlighted square. Captures, checks, castling, en passant, and promotion are enforced by the rule engine.</div></section><aside class="side-panel"><section class="panel-block turn-card"><div class="eyebrow">Current turn</div><div id="turn" class="turn-value">White to move</div><div id="status" class="status-line">Legal moves on board</div></section><section class="panel-block"><div class="tray-label">Captured by White</div><div id="captured-white" class="tray"></div><div class="tray-label above">Captured by Black</div><div id="captured-black" class="tray"></div></section><section class="panel-block move-block"><div class="section-heading"><span>Move list</span><span class="move-count" id="move-count">0</span></div><div id="move-list" class="move-list"><div class="empty-moves">The game is ready — White opens.</div></div></section><section class="panel-block tips"><div class="eyebrow">Table</div><div class="tips-copy">FEN is preserved locally. Use <kbd>game.fen()</kbd> and <kbd>game.move("e2e4")</kbd> in the console to inspect or drive the position.</div></section></aside></main></div>`;
        this.boardEl = document.getElementById('board');
        this.moveListEl = document.getElementById('move-list');
        this.turnEl = document.getElementById('turn');
        this.statusEl = document.getElementById('status');
        this.capturedWhiteEl = document.getElementById('captured-white');
        this.capturedBlackEl = document.getElementById('captured-black');
        this.promotionEl = document.getElementById('promotion');
        const playerSelect = document.getElementById('player-color');
        if (playerSelect) {
            playerSelect.value = String(this.playerColor);
            playerSelect.addEventListener('change', () => { this.playerColor = +playerSelect.value; this.aiColor = 1 - this.playerColor; this.newGame(); });
        }
        const depthSelect = document.getElementById('ai-depth');
        if (depthSelect) {
            depthSelect.value = String(this.aiDepth);
            depthSelect.addEventListener('change', () => { this.aiDepth = +depthSelect.value; });
        }
        document.getElementById('new-game')?.addEventListener('click', () => this.newGame());
        document.getElementById('undo')?.addEventListener('click', () => this.undo());
        document.getElementById('flip')?.addEventListener('click', () => { this.flipped = !this.flipped; this.render(); });
        document.getElementById('mute')?.addEventListener('click', () => this.toggleMute());
    }
    bindDom() {
        if (!this.boardEl)
            return;
        let dragSquare = null;
        this.boardEl.addEventListener('pointerdown', (event) => { const square = this.squareFromEvent(event); if (square == null)
            return; if (this.gameOver || this.isAiTurn())
            return; dragSquare = square; });
        this.boardEl.addEventListener('pointerup', (event) => { const square = this.squareFromEvent(event); if (square == null)
            return; if (dragSquare == null || this.gameOver || this.isAiTurn()) {
            dragSquare = null;
            return;
        } this.tryMove(dragSquare, square); dragSquare = null; });
        this.boardEl.addEventListener('pointermove', (event) => { if (dragSquare == null)
            return; const target = this.squareFromEvent(event); if (target != null && target !== dragSquare)
            this.tryMove(dragSquare, target); });
    }
    squareFromEvent(event) { const target = event.target; return target?.dataset?.square ? sqFromName(target.dataset.square) : null; }
    render() { this.renderBoard(); this.renderSidePanel(); this.renderPromotion(); this.renderBanner(); this.save(); }
    renderBoard() {
        if (!this.boardEl)
            return;
        const legal = new Set(this.engine.legalMoves().map(m => m.to));
        this.boardEl.innerHTML = '';
        for (let rank = 0; rank < 8; rank++)
            for (let file = 0; file < 8; file++) {
                const index = (this.flipped ? 7 - rank : rank) * 8 + (this.flipped ? 7 - file : file);
                const square = index;
                const isLight = (Math.floor(square / 8) + square % 8) % 2 === 0;
                const cell = document.createElement('button');
                cell.type = 'button';
                cell.className = 'square' + (isLight ? ' light' : ' dark');
                cell.dataset.square = squareName(square);
                cell.setAttribute('aria-label', squareName(square));
                if (this.lastMove.length === 2 && (this.lastMove[0] === squareName(square) || this.lastMove[1] === squareName(square)))
                    cell.classList.add('last-move');
                const selected = this.engine.selected === square || this.selected === squareName(square);
                if (selected)
                    cell.classList.add('selected');
                if (legal.has(square))
                    cell.classList.add('legal');
                const piece = this.engine.board[square];
                if (piece !== 0) {
                    const pieceEl = document.createElement('span');
                    pieceEl.className = 'piece ' + (piece > 0 ? 'white' : 'black');
                    pieceEl.textContent = pieceGlyph(this.engine, square);
                    cell.appendChild(pieceEl);
                }
                const marker = document.createElement('span');
                marker.className = 'square-marker';
                if (legal.has(square))
                    marker.textContent = this.engine.board[square] !== 0 ? '' : '●';
                cell.appendChild(marker);
                this.boardEl.appendChild(cell);
            }
        if (this.engine.inCheck(this.engine.side)) {
            const king = this.engine.kingSquare(this.engine.side);
            if (king >= 0) {
                const cell = this.boardEl.querySelector(`[data-square="${squareName(king)}"]`);
                if (cell)
                    cell.classList.add('in-check');
            }
        }
    }
    renderSidePanel() {
        if (!this.turnEl || !this.statusEl || !this.moveListEl || !this.capturedWhiteEl || !this.capturedBlackEl)
            return;
        const side = this.engine.side;
        const sideName = side === engine_1.Color.White ? 'White' : 'Black';
        this.turnEl.textContent = sideName + ' to move';
        this.statusEl.textContent = this.gameOver ? (this.drawReason || 'Game over') : this.engine.inCheck(side) ? 'Check!' : 'Legal moves on board';
        this.capturedWhiteEl.innerHTML = '';
        this.capturedBlackEl.innerHTML = '';
        const whiteCaptured = [];
        const blackCaptured = [];
        for (let i = 0; i < 64; i++) {
            const p = this.engine.board[i];
            if (p !== 0 && Math.abs(p) !== 6 && Math.abs(p) !== 1) {
                const glyph = (p > 0 ? 'wp' : 'bp') + Math.abs(p);
                (p > 0 ? whiteCaptured : blackCaptured).push(glyph);
            }
        }
        for (const glyph of whiteCaptured) {
            const el = document.createElement('span');
            el.className = 'captured captured-black';
            el.textContent = PIECE_GLYPHS[glyph];
            this.capturedWhiteEl.appendChild(el);
        }
        for (const glyph of blackCaptured) {
            const el = document.createElement('span');
            el.className = 'captured captured-white';
            el.textContent = PIECE_GLYPHS[glyph];
            this.capturedBlackEl.appendChild(el);
        }
        this.moveListEl.innerHTML = '';
        const moves = this.engine.moveList;
        if (!moves.length) {
            const empty = document.createElement('div');
            empty.className = 'empty-moves';
            empty.textContent = 'The game is ready — White opens.';
            this.moveListEl.appendChild(empty);
        }
        for (let i = 0; i < moves.length; i += 2) {
            const row = document.createElement('div');
            row.className = 'move-row';
            const num = document.createElement('span');
            num.className = 'move-number';
            num.textContent = (i / 2 + 1) + '.';
            row.appendChild(num);
            const white = document.createElement('span');
            white.className = 'move-text';
            white.textContent = i < moves.length ? shortMove(this.engine, moves[i]) : '';
            row.appendChild(white);
            const black = document.createElement('span');
            black.className = 'move-text';
            if (i + 1 < moves.length)
                black.textContent = shortMove(this.engine, moves[i + 1]) + (this.moveResultSuffix(i + 1) || '');
            row.appendChild(black);
            this.moveListEl.appendChild(row);
        }
        this.moveListEl.scrollTop = this.moveListEl.scrollHeight;
        const count = document.getElementById('move-count');
        if (count)
            count.textContent = String(Math.ceil(moves.length / 2));
    }
    moveResultSuffix(index) { const after = this.engine.clone(); after.applyMove(this.engine.moveList[index], false); return after.legalMoves().length === 0 && after.inCheck(after.side) ? '#' : after.inCheck(after.side) ? '+' : ''; }
    renderPromotion() { if (!this.promotionEl)
        return; const active = this.promoSquare != null; this.promotionEl.hidden = !active; if (!active || this.promoSquare == null)
        return; const square = sqFromName(this.promoSquare); if (square == null)
        return; const color = this.engine.board[square] > 0 ? engine_1.Color.White : engine_1.Color.Black; const picker = this.promotionEl.querySelector('.promo-pieces'); if (!picker)
        return; picker.innerHTML = ''; for (const promo of ['Q', 'R', 'B', 'N']) {
        const button = document.createElement('button');
        button.type = 'button';
        button.className = 'promo-piece';
        button.textContent = ['♕', '♖', '♗', '♘'][['Q', 'R', 'B', 'N'].indexOf(promo)];
        button.addEventListener('click', () => this.choosePromotion(square, promo));
        picker.appendChild(button);
    } }
    renderBanner() { const banner = document.getElementById('game-banner'); if (!banner)
        return; if (!this.gameOver) {
        banner.hidden = true;
        banner.textContent = '';
        return;
    } banner.hidden = false; banner.textContent = this.drawReason === 'checkmate' ? (this.winner === engine_1.Color.White ? 'White wins by checkmate' : 'Black wins by checkmate') : this.drawReason === 'stalemate' ? 'Draw — stalemate' : this.drawReason === 'fifty-move rule' ? 'Draw — fifty-move rule' : this.drawReason === 'insufficient material' ? 'Draw — insufficient material' : 'Draw — threefold repetition'; }
    choosePromotion(square, promo) { if (this.promoSquare === null)
        return; const move = this.engine.legalMoves().find(m => m.from === this.engine.selected && m.to === square && m.promotion === promo); if (!move)
        return; this.engine.promoSquare = -1; this.promoSquare = null; this.applyPlayerMove(move); }
    tryMove(from, to) {
        if (this.engine.selected >= 0 && this.selected && from === this.engine.selected) {
            this.engine.selected = -1;
            this.selected = '';
            this.render();
            return;
        }
        const piece = this.engine.board[from];
        if (piece !== 0 && ((piece > 0) === (this.engine.side === this.playerColor))) {
            const moves = this.engine.legalMoves().filter(m => m.from === from);
            if (moves.length) {
                this.engine.selected = from;
                this.selected = squareName(from);
                if (moves.some(m => m.to === to && m.promotion)) {
                    this.promoSquare = squareName(to);
                    this.engine.promoSquare = to;
                    this.render();
                    return;
                }
                this.applyPlayerMove(moves.find(m => m.to === to) || moves[0]);
            }
            else {
                this.engine.selected = from;
                this.selected = squareName(from);
                this.render();
            }
        }
        else if (this.engine.selected >= 0) {
            const move = this.engine.legalMoves().find(m => m.from === this.engine.selected && m.to === to);
            if (move)
                this.applyPlayerMove(move);
            else {
                this.engine.selected = this.engine.board[from] !== 0 ? from : -1;
                this.selected = squareName(this.engine.selected);
                this.render();
            }
        }
    }
    applyPlayerMove(move) { this.engine.applyMove(move); this.lastMove = [squareName(move.from), squareName(move.to)]; this.undoStack.push({ fen: this.engine.fen(), lastMove: this.lastMove.slice() }); this.status = { over: this.engine.gameOver, result: this.statusResult() }; if (this.statusEl)
        this.statusEl.textContent = this.engine.gameOver ? (this.drawReason || 'Game over') : 'Your move'; this.render(); this.playSound(move); if (!this.engine.gameOver && this.isAiTurn())
        queueMicrotask(() => this.aiMove()); }
    statusResult() { return this.engine.gameOver ? (this.drawReason === 'checkmate' ? (this.winner === engine_1.Color.White ? 'White wins' : 'Black wins') : this.drawReason) : ''; }
    isAiTurn() { return !this.gameOver && this.engine.side === this.aiColor && this.playerColor !== this.aiColor; }
    aiMove() { if (!this.root || this.aiBusy || this.gameOver || this.engine.side !== this.aiColor)
        return; this.aiBusy = true; if (this.statusEl)
        this.statusEl.textContent = 'AI is thinking…'; queueMicrotask(() => { const moves = this.engine.legalMoves(); if (!moves.length) {
        this.aiBusy = false;
        this.render();
        return;
    } const chosen = bestAiMove(this.engine, this.aiDepth); if (chosen)
        this.applyPlayerMove(chosen); this.aiBusy = false; this.render(); }); }
    undo() { if (!this.undoStack.length || this.aiBusy)
        return; const previous = this.undoStack.pop(); if (!previous)
        return; this.engine = engine_1.Engine.fromFen(previous.fen); this.lastMove = previous.lastMove.slice(); this.selected = ''; this.engine.selected = -1; this.promoSquare = null; this.engine.promoSquare = -1; this.gameOver = false; this.winner = null; this.drawReason = ''; this.engine.gameOver = false; this.engine.winner = null; this.engine.drawReason = ''; this.render(); }
    newGame() { const playerColor = this.playerColor; this.engine = engine_1.Engine.fromFen('rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR'); this.lastMove = []; this.selected = ''; this.engine.selected = -1; this.promoSquare = null; this.gameOver = false; this.winner = null; this.drawReason = ''; this.undoStack = []; this.aiColor = 1 - playerColor; this.pieces = { board: Array.from(this.engine.board), side: this.engine.side, castling: this.engine.castling, epSquare: this.engine.epSquare, halfmove: this.engine.halfmove, moveNumber: this.engine.moveNumber, moveList: [], gameover: false, winner: null, drawReason: '', playerColor, aiColor: this.aiColor, aiDepth: this.aiDepth, flipped: this.flipped, muted: this.muted, lastMove: [], selected: '', promoSquare: null }; this.render(); this.save(); if (this.isAiTurn())
        queueMicrotask(() => this.aiMove()); }
    toggleMute() { this.muted = !this.muted; this.soundEnabled = !this.muted; this.render(); const mute = document.getElementById('mute'); if (mute)
        mute.textContent = this.muted ? '×' : '♪'; }
    playSound(move) { if (!this.soundEnabled)
        return; try {
        this.audio ?? (this.audio = new (window.AudioContext || window.webkitAudioContext)());
        const context = this.audio;
        const now = context.currentTime;
        const oscillator = context.createOscillator();
        const gain = context.createGain();
        oscillator.type = move.captured !== undefined && move.captured !== 0 ? 'triangle' : 'sine';
        oscillator.frequency.setValueAtTime(move.captured !== undefined && move.captured !== 0 ? 185 : 330, now);
        oscillator.frequency.exponentialRampToValueAtTime(move.captured !== undefined && move.captured !== 0 ? 95 : 440, now + .13);
        gain.gain.setValueAtTime(.0001, now);
        gain.gain.exponentialRampToValueAtTime(.16, now + .015);
        gain.gain.exponentialRampToValueAtTime(.0001, now + .18);
        oscillator.connect(gain);
        gain.connect(context.destination);
        oscillator.start(now);
        oscillator.stop(now + .2);
    }
    catch { } }
    fen() { return this.engine.fen(); }
    move(ref) { if (this.gameOver || this.isAiTurn())
        return false; const normalized = ref.toLowerCase().replace(/[^a-h0-9]/g, ''); const move = this.engine.legalMoves().find(m => squareName(m.from) + squareName(m.to) === normalized); if (!move)
        return false; this.applyPlayerMove(move); return true; }
    legalMoves() { return this.engine.legalMoves(); }
    makeDebugSnapshot() { return { fen: this.fen(), moves: this.legalMoves(), status: this.status }; }
}
exports.App = App;
const PIECE_VALUE = { 1: 100, 2: 320, 3: 330, 4: 500, 5: 900, 6: 20000 };
const WHITE_PST = [
    [0, 0, 0, 0, 0, 0, 0, 0, 5, 5, 5, 5, 5, 5, 5, 5, 1, 1, 2, 3, 3, 2, 1, 1, 2, 2, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 5, 5, 5, 5, 5, 5, 5, 5, 0, 0, 0, 0, 0, 0, 0, 0]
];
const BLACK_PST = WHITE_PST.slice().reverse();
function evaluate(engine, side) { let total = 0; for (let sq = 0; sq < 64; sq++) {
    const p = engine.board[sq];
    if (!p)
        continue;
    const type = Math.abs(p), color = p > 0 ? engine_1.Color.White : engine_1.Color.Black;
    total += color === side ? PIECE_VALUE[type] + (color === engine_1.Color.White ? WHITE_PST[Math.floor(sq / 8)][sq % 8] : BLACK_PST[Math.floor(sq / 8)][sq % 8]) : -(PIECE_VALUE[type] + (color === engine_1.Color.White ? WHITE_PST[Math.floor(sq / 8)][sq % 8] : BLACK_PST[Math.floor(sq / 8)][sq % 8]));
} return total; }
function bestAiMove(engine, depth) { const moves = engine.legalMoves().sort((a, b) => ((b.captured || 0) * 10) - ((a.captured || 0) * 10)); if (!moves.length)
    return null; const start = performance.now(); let best = null; let bestScore = -Infinity; for (const move of moves) {
    const clone = engine.clone();
    clone.applyMove(move, false);
    const score = -quiescence(clone, depth - 1, -Infinity, Infinity, performance.now() - start + 850);
    if (score > bestScore) {
        bestScore = score;
        best = move;
    }
} return performance.now() - start > 850 ? best : best; }
function quiescence(engine, depth, alpha, beta, deadline) { if (depth <= 0 || performance.now() > deadline)
    return evaluate(engine, engine.side); const side = engine.side, current = evaluate(engine, side); let best = current; const moves = engine.legalMoves().sort((a, b) => ((b.captured || 0) * 10) - ((a.captured || 0) * 10)); for (const move of moves) {
    const clone = engine.clone();
    clone.applyMove(move, false);
    const score = -quiescence(clone, depth - 1, -beta, -alpha, deadline);
    best = Math.max(best, score);
    if (best > alpha)
        alpha = best;
    if (alpha >= beta)
        break;
} return best; }
};

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