← Chess results

Chess

GPT 5.6 Luna Pro · 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 24,635 bytes · SHA-256 9601d939a87c
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Royal Gambit — Chess vs Computer</title>
<style>
/* ── css: base ── */
:root{--ink:#241f1b;--muted:#84766a;--paper:#f5efe4;--panel:#fffaf2;--line:#ded2c2;--gold:#c59447;--gold2:#f0cf88;--wood1:#dfc092;--wood2:#aa774d;--danger:#c85d4d;--shadow:0 18px 50px #51351918}*{box-sizing:border-box}body{margin:0;background:radial-gradient(circle at 20% 0%,#fffaf1 0,#efe5d6 42%,#e3d6c6 100%);color:var(--ink);font-family:Georgia,'Times New Roman',serif}.app-shell{min-height:100vh;max-width:1180px;margin:auto;padding:28px 34px 40px}.topbar{display:flex;justify-content:space-between;align-items:end;margin-bottom:22px}.eyebrow,.section-label{font:700 10px/1.2 Arial,sans-serif;letter-spacing:.17em;color:var(--muted)}h1{font-size:36px;letter-spacing:-.03em;margin:5px 0 0;font-weight:500}.top-actions{display:flex;gap:8px;flex-wrap:wrap}button,select{font:600 12px Arial,sans-serif;color:var(--ink);background:#fffaf3;border:1px solid var(--line);border-radius:7px;padding:10px 13px;cursor:pointer;transition:.15s}button:hover{border-color:var(--gold);transform:translateY(-1px)}.game-layout{display:grid;grid-template-columns:minmax(420px,680px) 300px;gap:28px;align-items:start;justify-content:center}.board-wrap{position:relative}.turn-badge{display:inline-flex;background:var(--ink);color:#fff7ea;padding:8px 13px;border-radius:999px;font:700 11px Arial,sans-serif;letter-spacing:.05em;margin:0 0 12px 2px}.board{width:min(680px,calc(100vw - 420px));aspect-ratio:1;display:grid;grid-template-columns:repeat(8,1fr);border:10px solid #765037;border-radius:4px;box-shadow:var(--shadow),inset 0 0 0 1px #f4d4a0;overflow:hidden}.square{position:relative;display:grid;place-items:center;user-select:none}.light{background:var(--wood1)}.dark{background:var(--wood2)}.square.selected{box-shadow:inset 0 0 0 4px var(--gold2)}.square.last{background-image:linear-gradient(#d6a64a55,#d6a64a55)}.square.check{background-image:radial-gradient(circle,#d84f3fbb 0 38%,transparent 40%)}.piece{font-family:'Times New Roman',serif;font-size:clamp(38px,6.8vw,67px);line-height:1;position:relative;z-index:2;cursor:grab;text-shadow:1px 2px 1px #3b241855}.white-piece{color:#fffdf7;-webkit-text-stroke:1px #4b3324;text-shadow:0 2px 1px #4b332488}.black-piece{color:#29211d;text-shadow:0 2px 1px #fff1d122}.dot{width:17%;height:17%;background:#5c402c99;border-radius:50%;position:absolute}.capture-ring{position:absolute;inset:10%;border:5px solid #5c402c99;border-radius:50%}.coord{position:absolute;font:700 10px Arial;color:#5e412d88;bottom:4px;left:5px}.dark .coord{color:#f7dfb188}.status{min-height:24px;margin:12px 3px;color:var(--muted);font:13px Arial}.side-panel{background:#fffaf2cc;border:1px solid #fff;border-radius:12px;box-shadow:var(--shadow);overflow:hidden}.panel-section{padding:17px 18px;border-bottom:1px solid var(--line)}.section-head{display:flex;justify-content:space-between;align-items:center}.segmented{display:flex;margin-top:10px}.segmented button{flex:1;border-radius:0;padding:8px 5px}.segmented button:first-child{border-radius:6px 0 0 6px}.segmented button:last-child{border-radius:0 6px 6px 0}.segmented .active{background:var(--ink);color:white;border-color:var(--ink)}.material{color:#9b7135;font:bold 16px Georgia}.captured{min-height:29px;margin-top:10px;font-size:22px;letter-spacing:2px}.moves-section{height:310px}.moves{height:270px;overflow:auto;margin-top:10px;font:14px/1.8 Arial}.move-row{display:grid;grid-template-columns:34px 1fr 1fr;border-bottom:1px solid #eadfce}.move-no{color:#a69380}.move-cell{font-weight:600}.muted,.hint{color:var(--muted);font:12px Arial}.controls select{width:100%;margin-top:10px}.hint{line-height:1.5;margin:12px 0 0}.promotion{position:fixed;inset:0;background:#241f1b55;display:grid;place-items:center;z-index:10}.promo-card{background:var(--paper);padding:18px;border-radius:12px;box-shadow:var(--shadow);text-align:center}.promo-choices{display:flex;gap:8px;margin-top:12px}.promo-choices button{font-size:34px;width:58px;padding:6px}@media(max-width:850px){.app-shell{padding:18px 12px}.topbar{align-items:start;gap:12px;flex-direction:column}h1{font-size:30px}.game-layout{display:block}.board{width:min(100%,680px);margin:auto}.side-panel{margin:22px auto 0;max-width:680px}.top-actions{gap:5px}.top-actions button{padding:8px 9px}.moves-section{height:240px}.moves{height:200px}}
</style>
</head>
<body>
<main class="app-shell" id="app"><header class="topbar"><div><div class="eyebrow">THE ROYAL GAME</div><h1>Royal Gambit</h1></div><div class="top-actions"><button id="newGame">New game</button><button id="undo">Undo</button><button id="flip">Flip board</button><button id="mute" aria-label="Toggle sound">Sound on</button></div></header><section class="game-layout"><div class="board-wrap"><div id="turnBadge" class="turn-badge">White to move</div><div id="board" class="board" role="grid"></div><div id="status" class="status">Choose a move</div></div><aside class="side-panel"><div class="panel-section"><div class="section-label">PLAY AS</div><div class="segmented"><button class="side-choice active" data-side="white">White</button><button class="side-choice" data-side="black">Black</button><button class="side-choice" data-side="both">Both</button></div></div><div class="panel-section"><div class="section-head"><span class="section-label">CAPTURED</span><span id="material" class="material">+0</span></div><div id="captured" class="captured"></div></div><div class="panel-section moves-section"><div class="section-head"><span class="section-label">MOVES</span><span id="moveCount" class="muted">0</span></div><div id="moves" class="moves"></div></div><div class="panel-section controls"><label class="section-label" for="difficulty">COMPUTER STRENGTH</label><select id="difficulty"><option value="1">Casual · depth 1</option><option value="2" selected="">Club · depth 2</option><option value="3">Master · depth 3</option></select><p class="hint">Click a piece or drag it to move. Gold dots show legal destinations.</p></div></aside></section><div id="promotion" class="promotion" hidden=""><div class="promo-card"><div class="section-label">PROMOTE TO</div><div id="promoChoices" class="promo-choices"></div></div></div></main>
<script>
(function () {
'use strict';
var __mods = {};
var __cache = {};
var __map = {};
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";
Object.defineProperty(exports, "__esModule", { value: true });
// ROYAL GAMBIT — rules engine, UI, AI, persistence
const GLYPH = { P: '♙', N: '♘', B: '♗', R: '♖', Q: '♕', K: '♔', p: '♟', n: '♞', b: '♝', r: '♜', q: '♛', k: '♚' };
const VAL = { P: 100, N: 320, B: 330, R: 500, Q: 900, K: 20000, p: -100, n: -320, b: -330, r: -500, q: -900, k: -20000 };
const files = 'abcdefgh';
const initial = () => { const b = Array(64).fill(null); const back = 'RNBQKBNR'; for (let x = 0; x < 8; x++) {
    b[x] = { t: back[x], c: 'w' };
    b[8 + x] = { t: 'P', c: 'w' };
    b[48 + x] = { t: 'p', c: 'b' };
    b[56 + x] = { t: back[x].toLowerCase(), c: 'b' };
} return { b, turn: 'w', castle: 'KQkq', ep: null, half: 0, full: 1, last: null, history: [], captured: { w: [], b: [] }, snap: [] }; };
let s = load() || initial();
let flipped = false, selected = null, legal = [];
let human = 'w';
let muted = false;
let audio = null;
const el = (q) => document.querySelector(q);
const boardEl = el('#board');
function copy(x) { return { b: x.b.map(p => p ? { ...p } : null), turn: x.turn, castle: x.castle, ep: x.ep, half: x.half, full: x.full, last: x.last ? { ...x.last } : null, history: [...x.history], captured: { w: [...x.captured.w], b: [...x.captured.b] }, snap: [] }; }
function xy(i) { return [i % 8, Math.floor(i / 8)]; }
function ix(x, y) { return y * 8 + x; }
function enemy(c) { return c === 'w' ? 'b' : 'w'; }
function king(st, c) { return st.b.findIndex(p => p?.t === 'K' && p.c === c); }
function attacked(st, sq, by) { const [x, y] = xy(sq); for (let i = 0; i < 64; i++) {
    const p = st.b[i];
    if (!p || p.c !== by)
        continue;
    const [a, d] = xy(i), dx = x - a, dy = y - d;
    if (p.t.toUpperCase() === 'P' && Math.abs(dx) === 1 && dy === (by === 'w' ? 1 : -1))
        return true;
    if (p.t.toUpperCase() === 'N' && [[1, 2], [2, 1], [-1, 2], [-2, 1], [1, -2], [2, -1], [-1, -2], [-2, -1]].some(v => v[0] === dx && v[1] === dy))
        return true;
    if (p.t.toUpperCase() === 'K' && Math.max(Math.abs(dx), Math.abs(dy)) === 1)
        return true;
    const diag = Math.abs(dx) === Math.abs(dy), straight = dx === 0 || dy === 0;
    if ((p.t.toUpperCase() === 'B' && diag) || (p.t.toUpperCase() === 'R' && straight) || (p.t.toUpperCase() === 'Q' && (diag || straight))) {
        const sx = Math.sign(dx), sy = Math.sign(dy);
        let xx = a + sx, yy = d + sy, clear = true;
        while (xx !== x || yy !== y) {
            if (st.b[ix(xx, yy)])
                clear = false;
            xx += sx;
            yy += sy;
        }
        if (clear)
            return true;
    }
} return false; }
function pseudo(st) {
    const out = [];
    const c = st.turn;
    for (let i = 0; i < 64; i++) {
        const p = st.b[i];
        if (!p || p.c !== c)
            continue;
        const [x, y] = xy(i), typ = p.t.toUpperCase();
        const add = (to, extra = {}) => { if (to < 0 || to >= 64)
            return; const [tx, ty] = xy(to); if (Math.abs(tx - x) > 2 || Math.abs(ty - y) > 2)
            return; if (!st.b[to] || st.b[to].c !== c)
            out.push({ from: i, to, ...extra }); };
        if (typ === 'P') {
            const dir = c === 'w' ? 1 : -1, start = c === 'w' ? 1 : 6, end = c === 'w' ? 7 : 0;
            let to = ix(x, y + dir);
            if (to >= 0 && to < 64 && !st.b[to]) {
                if (y + dir === end)
                    ['Q', 'R', 'B', 'N'].forEach(prom => out.push({ from: i, to, prom }));
                else
                    out.push({ from: i, to });
                if (y === start && !st.b[ix(x, y + 2 * dir)])
                    out.push({ from: i, to: ix(x, y + 2 * dir) });
            }
            for (const dx of [-1, 1]) {
                if (x + dx < 0 || x + dx > 7)
                    continue;
                to = ix(x + dx, y + dir);
                const q = st.b[to];
                if (q && q.c !== c) {
                    if (y + dir === end)
                        ['Q', 'R', 'B', 'N'].forEach(prom => out.push({ from: i, to, prom }));
                    else
                        out.push({ from: i, to });
                }
                if (st.ep === to)
                    out.push({ from: i, to, ep: true });
            }
        }
        else if (typ === 'N') {
            [[1, 2], [2, 1], [-1, 2], [-2, 1], [1, -2], [2, -1], [-1, -2], [-2, -1]].forEach(v => { if (x + v[0] >= 0 && x + v[0] < 8 && y + v[1] >= 0 && y + v[1] < 8)
                add(ix(x + v[0], y + v[1])); });
        }
        else if (typ === 'K') {
            for (let dx = -1; dx <= 1; dx++)
                for (let dy = -1; dy <= 1; dy++)
                    if ((dx || dy) && x + dx >= 0 && x + dx < 8 && y + dy >= 0 && y + dy < 8)
                        add(ix(x + dx, y + dy));
            const rank = c === 'w' ? 0 : 7;
            if (i === ix(4, rank) && !attacked(st, i, enemy(c))) {
                if (st.castle.includes(c === 'w' ? 'K' : 'k') && !st.b[ix(5, rank)] && !st.b[ix(6, rank)] && !attacked(st, ix(5, rank), enemy(c)) && !attacked(st, ix(6, rank), enemy(c)))
                    out.push({ from: i, to: ix(6, rank), castle: true });
                if (st.castle.includes(c === 'w' ? 'Q' : 'q') && !st.b[ix(1, rank)] && !st.b[ix(2, rank)] && !st.b[ix(3, rank)] && !attacked(st, ix(3, rank), enemy(c)) && !attacked(st, ix(2, rank), enemy(c)))
                    out.push({ from: i, to: ix(2, rank), castle: true });
            }
        }
        else {
            const dirs = typ === 'B' ? [[1, 1], [-1, 1], [1, -1], [-1, -1]] : typ === 'R' ? [[1, 0], [-1, 0], [0, 1], [0, -1]] : [[1, 1], [-1, 1], [1, -1], [-1, -1], [1, 0], [-1, 0], [0, 1], [0, -1]];
            for (const d of dirs) {
                let xx = x + d[0], yy = y + d[1];
                while (xx >= 0 && xx < 8 && yy >= 0 && yy < 8) {
                    const to = ix(xx, yy);
                    if (!st.b[to])
                        out.push({ from: i, to });
                    else {
                        if (st.b[to].c !== c)
                            out.push({ from: i, to });
                        break;
                    }
                    xx += d[0];
                    yy += d[1];
                }
            }
        }
    }
    return out;
}
function apply(st, m) { const n = copy(st), p = n.b[m.from]; const cap = m.ep ? n.b[m.to + (p.c === 'w' ? -8 : 8)] : n.b[m.to]; n.b[m.from] = null; n.b[m.to] = { t: m.prom || (p.c === 'w' ? p.t.toUpperCase() : p.t.toLowerCase()), c: p.c }; if (m.ep)
    n.b[m.to + (p.c === 'w' ? -8 : 8)] = null; if (m.castle) {
    const r = m.to > m.from ? m.to + 1 : m.to - 2;
    const rt = m.to > m.from ? m.to - 1 : m.to + 1;
    n.b[rt] = n.b[r];
    n.b[r] = null;
} if (p.t.toUpperCase() === 'K')
    n.castle = n.castle.replace(p.c === 'w' ? /[KQ]/g : /[kq]/g, ''); if (p.t.toUpperCase() === 'R') {
    if (m.from === 0)
        n.castle = n.castle.replace('Q', '');
    if (m.from === 7)
        n.castle = n.castle.replace('K', '');
    if (m.from === 56)
        n.castle = n.castle.replace('q', '');
    if (m.from === 63)
        n.castle = n.castle.replace('k', '');
} if (cap && cap.t.toUpperCase() === 'R') {
    if (m.to === 0)
        n.castle = n.castle.replace('Q', '');
    if (m.to === 7)
        n.castle = n.castle.replace('K', '');
    if (m.to === 56)
        n.castle = n.castle.replace('q', '');
    if (m.to === 63)
        n.castle = n.castle.replace('k', '');
} n.ep = null; if (p.t.toUpperCase() === 'P' && Math.abs(m.to - m.from) === 16)
    n.ep = (m.to + m.from) / 2; n.half = (p.t.toUpperCase() === 'P' || cap) ? 0 : n.half + 1; if (st.turn === 'b')
    n.full++; n.turn = enemy(st.turn); n.last = m; if (cap)
    n.captured[p.c].push(cap.t); return n; }
function legalMoves(st = s) { return pseudo(st).filter(m => { const n = apply(st, m); return !attacked(n, king(n, st.turn), enemy(st.turn)); }); }
function san(st, m) { const p = st.b[m.from]; if (m.castle)
    return m.to > m.from ? 'O-O' : 'O-O-O'; let out = p.t.toUpperCase() === 'P' ? (st.b[m.to] || m.ep ? files[m.from % 8] + 'x' : '') : p.t.toUpperCase(); if (p.t.toUpperCase() !== 'P') {
    const peers = legalMoves(st).filter(q => q.to === m.to && q.from !== m.from && st.b[q.from]?.t === p.t);
    if (peers.length)
        out += files[m.from % 8];
} if (st.b[m.to] || m.ep)
    out += 'x'; out += files[m.to % 8] + (Math.floor(m.to / 8) + 1); if (m.prom)
    out += '=' + m.prom; const n = apply(st, m); if (attacked(n, king(n, n.turn), n.turn === 'w' ? 'b' : 'w'))
    out += legalMoves(n).length ? ' +' : '#'; return out; }
function doMove(m, record = true) { const notation = san(s, m); const stack = s.snap; const before = copy(s); before.snap = []; if (record)
    stack.push(before); s = apply(s, m); s.snap = stack; if (record) {
    s.history.push(notation);
    save();
} selected = null; legal = []; render(); tone(s.history[s.history.length - 1]?.includes('x') ? 'capture' : notation.includes('+') ? 'check' : 'move'); if (!gameOver() && ((human !== 'both' && s.turn !== human))) {
    setTimeout(aiMove, 80);
} }
function gameOver() { const lm = legalMoves(s); let msg = ''; if (!lm.length) {
    if (attacked(s, king(s, s.turn), enemy(s.turn)))
        msg = `Checkmate · ${s.turn === 'w' ? 'Black' : 'White'} wins`;
    else
        msg = 'Stalemate · Draw';
}
else if (s.half >= 100)
    msg = 'Draw · 50-move rule';
else {
    const pieces = s.b.filter(Boolean);
    if (pieces.every(p => p.t.toUpperCase() === 'K' || p.t.toUpperCase() === 'B' || p.t.toUpperCase() === 'N') && pieces.filter(p => p.t.toUpperCase() !== 'K').length <= 1)
        msg = 'Draw · insufficient material';
} if (msg) {
    el('#status').textContent = msg;
    return true;
} return false; }
function render() { boardEl.innerHTML = ''; const lm = legalMoves(s), check = attacked(s, king(s, s.turn), enemy(s.turn)); el('#turnBadge').textContent = (s.turn === 'w' ? 'White' : 'Black') + ' to move' + (check ? ' · Check' : ''); for (let row = 0; row < 8; row++)
    for (let col = 0; col < 8; col++) {
        const x = flipped ? 7 - col : col, y = flipped ? row : 7 - row, i = ix(x, y), d = document.createElement('div');
        d.className = 'square ' + ((x + y) % 2 ? 'dark' : 'light');
        d.dataset.i = String(i);
        if (s.last && (s.last.from === i || s.last.to === i))
            d.classList.add('last');
        if (i === selected)
            d.classList.add('selected');
        if (check && i === king(s, s.turn))
            d.classList.add('check');
        const mv = selected !== null ? lm.filter(m => m.from === selected && m.to === i) : [];
        if (mv.length) {
            const q = document.createElement('span');
            q.className = s.b[i] ? 'capture-ring' : 'dot';
            d.append(q);
        }
        const p = s.b[i];
        if (p) {
            const q = document.createElement('span');
            q.className = 'piece ' + (p.c === 'w' ? 'white-piece' : 'black-piece');
            q.textContent = GLYPH[p.c === 'w' ? p.t.toUpperCase() : p.t.toLowerCase()];
            q.draggable = true;
            q.ondragstart = e => { e.dataTransfer?.setData('text/plain', String(i)); select(i); };
            d.append(q);
        }
        if ((flipped ? x === 7 : x === 0) && (y === 0 || y === 7)) {
            const q = document.createElement('span');
            q.className = 'coord';
            q.textContent = String(y + 1);
            d.append(q);
        }
        d.onclick = () => select(i);
        d.ondragover = e => e.preventDefault();
        d.ondrop = e => { e.preventDefault(); const from = Number(e.dataTransfer?.getData('text/plain')); tryMove(from, i); };
        boardEl.append(d);
    } renderSide(); el('#status').textContent = check ? 'Your king is in check.' : (gameOver() ? '' : 'Select a piece to begin'); }
function select(i) { if (gameOver() || s.turn !== human && human !== 'both')
    return; if (s.b[i]?.c === s.turn) {
    selected = i;
    legal = legalMoves(s);
    render();
}
else if (selected !== null)
    tryMove(selected, i); }
function tryMove(from, to) { const ms = legalMoves(s).filter(m => m.from === from && m.to === to); if (!ms.length)
    return; if (ms.some(m => m.prom)) {
    const choices = el('#promoChoices');
    choices.innerHTML = '';
    el('#promotion').hidden = false;
    for (const p of ['Q', 'R', 'B', 'N']) {
        const b = document.createElement('button');
        b.textContent = GLYPH[p];
        b.onclick = () => { el('#promotion').hidden = true; doMove(ms.find(m => m.prom === p)); };
        choices.append(b);
    }
}
else
    doMove(ms[0]); }
function renderSide() { el('#moveCount').textContent = String(s.history.length); const box = el('#moves'); box.innerHTML = ''; for (let i = 0; i < s.history.length; i += 2) {
    const r = document.createElement('div');
    r.className = 'move-row';
    r.innerHTML = `<span class="move-no">${i / 2 + 1}.</span><span class="move-cell">${s.history[i] || ''}</span><span class="move-cell">${s.history[i + 1] || ''}</span>`;
    box.append(r);
} box.scrollTop = box.scrollHeight; el('#captured').textContent = s.captured.w.map(x => GLYPH[x.toLowerCase()]).join('') + ' ' + s.captured.b.map(x => GLYPH[x.toUpperCase()]).join(''); const diff = s.captured.w.reduce((a, p) => a + (VAL[p] || 0), 0) + s.captured.b.reduce((a, p) => a - (VAL[p] || 0), 0); el('#material').textContent = (diff >= 0 ? '+' : '') + diff; }
function aiMove() { if (gameOver() || s.turn === human || human === 'both')
    return; const depth = Number(el('#difficulty').value), best = minimax(s, depth, -1e9, 1e9); if (best)
    doMove(best); }
function minimax(st, d, a, b) { const ms = legalMoves(st).sort((x, y) => (st.b[y.to] ? 1 : 0) - (st.b[x.to] ? 1 : 0)); if (!d || !ms.length)
    return null; if (d === 1) {
    let bm = ms[0], bv = st.turn === 'w' ? -1e9 : 1e9;
    for (const m of ms) {
        const v = score(apply(st, m));
        if (st.turn === 'w' ? v > bv : v < bv) {
            bv = v;
            bm = m;
        }
    }
    return bm;
} let bm = null, bv = st.turn === 'w' ? -1e9 : 1e9; for (const m of ms) {
    const n = apply(st, m), child = minimaxValue(n, d - 1, a, b);
    if (st.turn === 'w' ? child > bv : child < bv) {
        bv = child;
        bm = m;
    }
    if (st.turn === 'w')
        a = Math.max(a, bv);
    else
        b = Math.min(b, bv);
    if (b <= a)
        break;
} return bm; }
function minimaxValue(st, d, a, b) { const ms = legalMoves(st); if (!d || !ms.length)
    return score(st); let v = st.turn === 'w' ? -1e9 : 1e9; for (const m of ms) {
    const z = minimaxValue(apply(st, m), d - 1, a, b);
    if (st.turn === 'w') {
        v = Math.max(v, z);
        a = Math.max(a, v);
    }
    else {
        v = Math.min(v, z);
        b = Math.min(b, v);
    }
    if (b <= a)
        break;
} return v; }
function score(st) { return st.b.reduce((v, p) => v + (p ? (VAL[p.t] || 0) : 0), 0); }
function save() { localStorage.setItem('royal-gambit', JSON.stringify({ ...s, snap: [] })); }
function load() { try {
    const x = JSON.parse(localStorage.getItem('royal-gambit') || 'null');
    return x && x.b ? x : null;
}
catch {
    return null;
} }
function tone(kind) { if (muted)
    return; audio ?? (audio = new AudioContext()); const o = audio.createOscillator(), g = audio.createGain(); o.frequency.value = kind === 'capture' ? 180 : kind === 'check' ? 520 : 330; g.gain.value = .035; o.connect(g); g.connect(audio.destination); o.start(); o.stop(audio.currentTime + .07); }
function uci(str) { const f = files.indexOf(str[0]) + (Number(str[1]) - 1) * 8, t = files.indexOf(str[2]) + (Number(str[3]) - 1) * 8; return legalMoves(s).find(m => m.from === f && m.to === t && (!str[4] || m.prom === str[4].toUpperCase())) || null; }
document.querySelector('#newGame').onclick = () => { s = initial(); save(); render(); };
document.querySelector('#undo').onclick = () => { if (!s.snap.length)
    return; s = s.snap.pop(); save(); render(); };
document.querySelector('#flip').onclick = () => { flipped = !flipped; render(); };
document.querySelector('#mute').onclick = () => { muted = !muted; el('#mute').textContent = muted ? 'Sound off' : 'Sound on'; };
document.querySelectorAll('.side-choice').forEach(x => x.addEventListener('click', () => { document.querySelectorAll('.side-choice').forEach(y => y.classList.remove('active')); x.classList.add('active'); human = x.dataset.side; render(); if (human === 'b' && s.turn === 'w')
    aiMove(); if (human === 'w' && s.turn === 'b')
    aiMove(); }));
window.game = { fen: () => s.b.map((p, i) => p ? p.t : '.').join('') + ' ' + s.turn + ' ' + s.castle + ' ' + (s.ep === null ? '-' : files[s.ep % 8] + (Math.floor(s.ep / 8) + 1)), legalMoves: () => legalMoves(s).map(m => files[m.from % 8] + (Math.floor(m.from / 8) + 1) + files[m.to % 8] + (Math.floor(m.to / 8) + 1) + (m.prom || '').toLowerCase()), move: (u) => { human = 'both'; const m = uci(u); if (!m)
        return false; doMove(m); return true; }, state: () => JSON.parse(JSON.stringify(s)), setSide: (side) => { human = side === 'both' ? 'both' : side === 'black' ? 'b' : 'w'; render(); }, reset: () => { s = initial(); human = 'both'; save(); render(); } };
render();
if (human !== s.turn && human !== 'both')
    setTimeout(aiMove, 250);
};

// ── entry ──
__require("src/main.ts");
})();
</script>
</body>
</html>
<!-- agent-meta {"model":"openai/gpt-5.6-luna-pro","provider":"openrouter","persona":"typescript","sessionId":"61949d48-2f49-4b24-b891-c60d3399ba6e","tokensIn":2170227,"tokensOut":47455,"tokensTotal":2217682,"turns":38,"toolCalls":39,"failedToolCalls":2,"timestamp":"2026-07-31T02:35:56.147Z"} -->