Files

440 lines
22 KiB
JavaScript
Raw Permalink Normal View History

2026-04-11 09:24:21 +02:00
(function(){"use strict";try{if(typeof document<"u"){var r=document.createElement("style");r.appendChild(document.createTextNode("._wrapper_h2w9g_1{--gc-bg: var(--color-bg, #0d1117);--gc-bg-secondary: var(--color-bg-secondary, #182237);--gc-text: var(--color-text, #f1f4ef);--gc-text-muted: var(--color-text-muted, #b6c0d4);--gc-primary: var(--color-primary, #45c4b0);--gc-accent: var(--color-accent, #ff6b6b);--gc-border: var(--color-border, #34516f);width:100%}._frame_h2w9g_12{position:relative;min-height:100dvh;padding:10px;color:var(--gc-text);font-family:Space Mono,IBM Plex Mono,Courier New,monospace;background:radial-gradient(circle at 12% 8%,rgb(69 196 176 / 24%),transparent 42%),radial-gradient(circle at 86% 14%,rgb(255 107 107 / 20%),transparent 38%),linear-gradient(156deg,var(--gc-bg),var(--gc-bg-secondary));border:1px solid color-mix(in srgb,var(--gc-border) 70%,transparent)}._frameSucceeded_h2w9g_25{background:radial-gradient(circle at 18% 12%,rgb(86 255 148 / 26%),transparent 42%),radial-gradient(circle at 82% 16%,rgb(42 210 104 / 22%),transparent 38%),linear-gradient(156deg,#0f2f1f,#1b5234);border-color:color-mix(in srgb,#4be38f 56%,var(--gc-border))}._header_h2w9g_33{margin-bottom:8px}._title_h2w9g_37{margin:0;font-size:clamp(20px,6.4vw,32px);letter-spacing:.05em;text-transform:uppercase}._subtitle_h2w9g_44{margin:4px 0 0;color:var(--gc-text-muted);font-size:clamp(11px,3.3vw,14px)}._statsLine_h2w9g_50{margin:5px 0 0;padding:5px 6px;border:1px solid color-mix(in srgb,var(--gc-border) 70%,transparent);background:#0913209e;font-size:clamp(11px,3.1vw,13px)}._boardPanel_h2w9g_58{margin-bottom:8px}._board_h2w9g_58{position:relative;overflow:hidden;width:min(88vw,340px);aspect-ratio:1 / 1;display:grid;grid-template-columns:repeat(8,1fr);grid-template-rows:repeat(8,1fr);border:2px solid color-mix(in srgb,var(--gc-primary) 45%,var(--gc-border));box-shadow:0 0 0 1px #00000059,0 14px 26px #00000061}._trailOverlay_h2w9g_76{position:absolute;top:0;right:0;bottom:0;left:0;width:100%;height:100%;pointer-events:none;z-index:1}._trailPath_h2w9g_85{fill:none;stroke:#ff2828e5;stroke-width:8;stroke-linecap:round;stroke-linejoin:round;filter:drop-shadow(0 0 5px rgb(255 30 30 / 65%))}._trailHead_h2w9g_94{fill:#ff3c3cf5;filter:drop-shadow(0 0 6px rgb(255 45 45 / 70%))}._cell_h2w9g_99{position:relative;display:grid;place-items:center}._lightCell_h2w9g_105{background:#f4ecd8}._darkCell_h2w9g_109{background:#c08c61}._knight_h2w9g_113{position:absolute;z-index:3;display:grid;place-items:center;width:100%;height:100%;font-family:Times New Roman,Georgia,serif;font-size:clamp(28px,15vw,56px);line-height:1;color:#111827;text-shadow:0 1px 0 rgb(255 255 255 / 55%),0 3px 8px rgb(0 0 0 / 25%);transform:translateY(-1px)}._target_h2w9g_130{position:absolute;z-index:2;font-size:clamp(12px,8.5vw,33px);color:#e21c29;text-shadow:0 0 8px rgb(236 37 58 / 60%)}._readout_h2w9g_138{margin-bottom:8px;padding:7px;border:1px solid color-mix(in srgb,var(--gc-border) 72%,transparent);background:#040a1173}._readoutLine_h2w9g_145{margin:0;font-size:clamp(11px,3.2vw,14px)}._readoutLine_h2w9g_145+._readoutLine_h2w9g_145{margin-top:3px}._programPanel_h2w9g_154{display:flex;flex-direction:column;gap:6px}._programTrack_h2w9g_160{min-height:42px;display:flex;flex-wrap:wrap;align-items:center;gap:5px;border:1px solid color-mix(in srgb,var(--gc-border) 70%,transparent);background:#07111d85;padding:7px}._placeholder_h2w9g_171{color:var(--gc-text-muted);font-size:12px}._step_h2w9g_176{width:24px;height:24px;display:grid;place-items:center;border:1px solid color-mix(in srgb,var(--gc-primary) 45%,var(--gc-border));background:#45c4b024;font-weight:600;font-size:13px}._stepExecuted_h2w9g_187{background:#ff6b6b40;border-color:#ff6b6b99}._programButtons_h2w9g_192,._controlRow_h2w9g_193{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:6px}._button_h2w9g_199,._buttonPrimary_h2w9g_200{border:1px solid color-mix(in srgb,var(--gc-border) 70%,transparent);padding:7px 6px;font:inherit;font-size:20px;text-transform:uppercase;letter-spacing:.05em;cursor:pointer;transition
import { jsx as a, jsxs as l } from "react/jsx-runtime";
import { useMemo as ne, useState as g, useRef as me, useEffect as oe, useCallback as x } from "react";
const Oe = "_wrapper_h2w9g_1", Ae = "_frame_h2w9g_12", Ee = "_frameSucceeded_h2w9g_25", Le = "_header_h2w9g_33", Re = "_title_h2w9g_37", Ie = "_statsLine_h2w9g_50", Me = "_boardPanel_h2w9g_58", De = "_board_h2w9g_58", He = "_trailOverlay_h2w9g_76", Ge = "_trailPath_h2w9g_85", Be = "_trailHead_h2w9g_94", Ue = "_cell_h2w9g_99", Fe = "_lightCell_h2w9g_105", Ye = "_darkCell_h2w9g_109", Ve = "_knight_h2w9g_113", je = "_target_h2w9g_130", Xe = "_readout_h2w9g_138", Ke = "_readoutLine_h2w9g_145", Ze = "_programPanel_h2w9g_154", ze = "_programTrack_h2w9g_160", We = "_placeholder_h2w9g_171", qe = "_step_h2w9g_176", Je = "_stepExecuted_h2w9g_187", Qe = "_programButtons_h2w9g_192", et = "_controlRow_h2w9g_193", tt = "_button_h2w9g_199", rt = "_buttonPrimary_h2w9g_200", nt = "_legend_h2w9g_233", ot = "_promptOverlay_h2w9g_239", at = "_promptTitle_h2w9g_250", st = "_promptText_h2w9g_258", ct = "_promptOptions_h2w9g_264", lt = "_promptOption_h2w9g_264", it = "_promptSelected_h2w9g_282", dt = "_promptHint_h2w9g_287", e = {
wrapper: Oe,
frame: Ae,
frameSucceeded: Ee,
header: Le,
title: Re,
statsLine: Ie,
boardPanel: Me,
board: De,
trailOverlay: He,
trailPath: Ge,
trailHead: Be,
cell: Ue,
lightCell: Fe,
darkCell: Ye,
knight: Ve,
target: je,
readout: Xe,
readoutLine: Ke,
programPanel: Ze,
programTrack: ze,
placeholder: We,
step: qe,
stepExecuted: Je,
programButtons: Qe,
controlRow: et,
button: tt,
buttonPrimary: rt,
legend: nt,
promptOverlay: ot,
promptTitle: at,
promptText: st,
promptOptions: ct,
promptOption: lt,
promptSelected: it,
promptHint: dt
}, C = 8, h = { x: 0, y: C - 1 }, pt = 220, ut = 64, mt = 3, G = 100, ht = 5, gt = {
up: { dx: 0, dy: -1 },
down: { dx: 0, dy: 1 },
left: { dx: -1, dy: 0 },
right: { dx: 1, dy: 0 }
}, _t = {
up: "",
down: "",
left: "",
right: ""
}, ge = [
{ dx: 0, dy: 1 },
{ dx: 1, dy: 1 },
{ dx: 1, dy: 2 },
{ dx: 1, dy: 3 },
{ dx: 2, dy: 3 },
{ dx: 1, dy: 5 },
{ dx: 1, dy: 6 }
], Z = (r, o, d) => Math.min(d, Math.max(o, r)), _e = (r) => r.x >= 0 && r.x < C && r.y >= 0 && r.y < C, yt = (r, o) => r.x === o.x && r.y === o.y, ce = (r, o) => o === 0 ? r : ce(o, r % o), ft = (r, o) => {
const d = Math.abs(o.x - r.x), p = Math.abs(o.y - r.y);
if (d === 0 && p === 0) return 0;
if (d === 0 || p === 0) return d + p;
const _ = ce(d, p);
return d / _ + p / _;
}, wt = (r) => new Promise((o) => {
window.setTimeout(o, r);
}), he = (r) => r === "Enter" || r === "NumpadEnter" || r === " " || r === "Space" || r === "Spacebar", bt = (r) => `${(r.x + 0.5) * G},${(r.y + 0.5) * G}`, z = (r) => Z(r, 0, ge.length - 1), ye = (r) => ge[z(r)], ae = (r) => {
const o = ye(r);
return o.dx + o.dy;
}, se = (r) => {
const o = ye(r), d = [];
for (let _ = 1; _ < C; _ += 1) {
const u = {
x: h.x + o.dx * _,
y: h.y - o.dy * _
};
if (!_e(u)) break;
Math.abs(u.x - h.x) + Math.abs(u.y - h.y) > ht && d.push(u);
}
if (d.length === 0)
return {
x: h.x + o.dx,
y: h.y - o.dy
};
const p = Math.floor(Math.random() * d.length);
return d[p];
};
function Nt({
config: r,
onComplete: o,
onProgress: d,
theme: p,
className: _
}) {
const u = ne(() => {
const n = r.params.goalCaptures;
return typeof n != "number" ? mt : Z(Math.round(n), 1, 10);
}, [r.params.goalCaptures]), [v, le] = g(0), [N, W] = g(h), [$, ie] = g(() => se(0)), [y, q] = g([]), [B, U] = g([h]), [f, k] = g("idle"), [fe, O] = g(-1), [w, de] = g(0), [pe, L] = g(""), [R, F] = g("yes"), [I, Y] = g("continue"), [b, we] = g(!1), S = me(0), V = me(!1), i = f === "running", m = f === "gameOverPrompt" || f === "successPrompt" || f === "finished", J = ae(v), be = ne(() => p ? {
"--gc-primary": p.primary,
"--gc-accent": p.accent,
"--gc-bg": p.bg,
"--gc-bg-secondary": p.bgSecondary,
"--gc-text": p.text,
"--gc-text-muted": p.textMuted,
"--gc-border": p.border
} : {}, [p]);
oe(() => {
const n = w / u;
d == null || d(Math.round(Z(n * 100, 0, 100)));
}, [w, u, d]);
const j = x((n, t, s = "") => {
S.current += 1, k("idle"), le(z(n)), W(h), ie(se(n)), U([h]), O(-1), de(t), F("yes"), Y("continue"), L(s);
}, []), Q = x(() => {
j(v, 0, `Looking for a ${J}-step move set.`);
}, [v, J, j]), X = x((n = 0) => {
const t = z(v + 1), s = ae(t);
j(t, n, `Looking for a ${s}-step move set.`);
}, [v, j]), ee = x(() => {
V.current || (V.current = !0, S.current += 1, k("finished"), o({
success: !1,
error: "Player chose no after game over.",
data: {
captures: w,
goalCaptures: u,
configId: r.id,
completedAt: (/* @__PURE__ */ new Date()).toISOString(),
hasEverSucceeded: b
}
}));
}, [w, r.id, u, b, o]), D = x(() => {
V.current || (V.current = !0, S.current += 1, k("finished"), o({
success: !0,
score: 100,
data: {
captures: w,
goalCaptures: u,
configId: r.id,
completedAt: (/* @__PURE__ */ new Date()).toISOString(),
hasEverSucceeded: b
}
}));
}, [w, r.id, u, b, o]), M = x((n) => {
q((t) => i || m || t.length >= ut ? t : [...t, n]);
}, [m, i]), xe = x(() => {
i || m || q((n) => n.slice(0, -1));
}, [m, i]), Ce = x(() => {
i || m || q([]);
}, [m, i]), Ne = x(() => {
S.current += 1, k("idle"), O(-1), L("Program stopped.");
}, []), Se = x(async () => {
if (i || m || y.length === 0) return;
const n = S.current + 1;
S.current = n;
let t = N, s = $, c = w, T = 0;
for (k("running"), O(-1), U([N]), L(""); S.current === n; )
for (let P = 0; P < y.length; P += 1) {
if (await wt(pt), S.current !== n) return;
const H = y[P], K = gt[H], A = {
x: t.x + K.dx,
y: t.y + K.dy
};
if (!_e(A)) {
k("gameOverPrompt"), F("yes"), O(-1), L(`Game over after ${T} step(s). Try again?`);
return;
}
if (T += 1, t = A, W(A), U((E) => [...E, A]), O(P), yt(A, s)) {
if (c += 1, de(c), we(!0), c >= u) {
k("successPrompt"), Y("continue"), O(-1), L(`Success. ${c} challenge(s) complete.`);
return;
}
const E = z(v + 1), re = se(E), Te = ae(E);
t = h, s = re, k("idle"), le(E), W(h), ie(re), U([h]), O(-1), L(
`Challenge ${c}/${u} complete. Looking for a ${Te}-step move set.`
);
return;
}
}
}, [w, v, u, m, N, y, i, $]);
oe(() => {
const n = (t) => {
const s = {
ArrowUp: "up",
ArrowDown: "down",
ArrowLeft: "left",
ArrowRight: "right"
};
if (f === "gameOverPrompt") {
if (t.key === "ArrowLeft" || t.key === "ArrowUp") {
t.preventDefault(), F("yes");
return;
}
if (t.key === "ArrowRight" || t.key === "ArrowDown") {
t.preventDefault(), F("no");
return;
}
he(t.key) && (t.preventDefault(), R === "yes" ? Q() : b ? D() : ee());
return;
}
if (f === "successPrompt") {
if (t.key === "ArrowLeft" || t.key === "ArrowUp") {
t.preventDefault(), Y("continue");
return;
}
if (t.key === "ArrowRight" || t.key === "ArrowDown") {
t.preventDefault(), Y("done");
return;
}
he(t.key) && (t.preventDefault(), I === "continue" ? X() : D());
return;
}
if (f !== "idle") return;
const c = s[t.key];
c && (t.preventDefault(), M(c));
};
return window.addEventListener("keydown", n), () => {
window.removeEventListener("keydown", n);
};
}, [
M,
ee,
D,
R,
b,
f,
Q,
X,
I
]), oe(
() => () => {
S.current += 1;
},
[]
);
const Pe = ne(() => {
const n = $.x - N.x, t = N.y - $.y, s = Math.abs(n), c = Math.abs(t), T = ft(N, $);
if (s === 0 && c === 0)
return {
angle: 0,
motifLength: T,
motif: "On target"
};
if (s === 0 || c === 0)
return {
angle: s === 0 ? 90 : 0,
motifLength: T,
motif: s === 0 ? `${t >= 0 ? "U" : "D"} x ${c}` : `${n >= 0 ? "R" : "L"} x ${s}`
};
const P = ce(s, c), H = s / P, K = c / P, A = `${n >= 0 ? "R" : "L"} x ${H}`, E = `${t >= 0 ? "U" : "D"} x ${K}`;
return {
angle: Math.atan2(c, s) * (180 / Math.PI),
motifLength: T,
motif: `${A}, ${E}`
};
}, [N, $]), ve = Math.round(Z(w / u * 100, 0, 100)), $e = _ ? `${e.wrapper} ${_}` : e.wrapper, ue = C * G, ke = B.map(bt).join(" "), te = B[B.length - 1];
return /* @__PURE__ */ a("div", { className: $e, style: be, children: /* @__PURE__ */ l(
"section",
{
className: `${e.frame} ${b ? e.frameSucceeded : ""}`,
"aria-label": "Chess move set trainer",
children: [
/* @__PURE__ */ l("header", { className: e.header, children: [
/* @__PURE__ */ a("h2", { className: e.title, children: "The Chess Analogy" }),
/* @__PURE__ */ l("p", { className: e.statsLine, children: [
"Captures ",
w,
"/",
u,
" Progress ",
ve,
"% Program ",
y.length,
" Target ",
J
] })
] }),
/* @__PURE__ */ a("div", { className: e.boardPanel, children: /* @__PURE__ */ l("div", { className: e.board, role: "grid", "aria-label": "8 by 8 checkerboard", children: [
/* @__PURE__ */ l(
"svg",
{
className: e.trailOverlay,
viewBox: `0 0 ${ue} ${ue}`,
preserveAspectRatio: "none",
"aria-hidden": "true",
children: [
B.length > 1 && /* @__PURE__ */ a("polyline", { className: e.trailPath, points: ke }),
te && /* @__PURE__ */ a(
"circle",
{
className: e.trailHead,
cx: (te.x + 0.5) * G,
cy: (te.y + 0.5) * G,
r: 6
}
)
]
}
),
Array.from({ length: C * C }).map((n, t) => {
const s = t % C, c = Math.floor(t / C), T = (s + c) % 2 === 1, P = N.x === s && N.y === c, H = $.x === s && $.y === c;
return /* @__PURE__ */ l(
"div",
{
className: `${e.cell} ${T ? e.darkCell : e.lightCell}`,
role: "gridcell",
"aria-label": `x ${s + 1}, y ${C - c}`,
children: [
H && /* @__PURE__ */ a("span", { className: e.target, "aria-hidden": "true", children: "" }),
P && /* @__PURE__ */ a("span", { className: e.knight, "aria-hidden": "true", children: "" })
]
},
`${s}-${c}`
);
})
] }) }),
/* @__PURE__ */ l("div", { className: e.readout, children: [
/* @__PURE__ */ l("p", { className: e.readoutLine, children: [
"Angle: ",
/* @__PURE__ */ l("strong", { children: [
Pe.angle.toFixed(1),
"°"
] })
] }),
!1
] }),
/* @__PURE__ */ l("div", { className: e.programPanel, children: [
/* @__PURE__ */ l("div", { className: e.programTrack, "aria-live": "polite", children: [
y.length === 0 && /* @__PURE__ */ a("span", { className: e.placeholder, children: "Empty move set" }),
y.map((n, t) => /* @__PURE__ */ a(
"span",
{
className: `${e.step} ${i && t === fe ? e.stepExecuted : ""}`,
children: _t[n]
},
`${n}-${t}`
))
] }),
/* @__PURE__ */ l("div", { className: e.programButtons, children: [
/* @__PURE__ */ a("button", { type: "button", className: e.button, onClick: () => M("up"), disabled: i || m, children: "" }),
/* @__PURE__ */ a("button", { type: "button", className: e.button, onClick: () => M("left"), disabled: i || m, children: "" }),
/* @__PURE__ */ a("button", { type: "button", className: e.button, onClick: () => M("down"), disabled: i || m, children: "" }),
/* @__PURE__ */ a("button", { type: "button", className: e.button, onClick: () => M("right"), disabled: i || m, children: "" })
] }),
/* @__PURE__ */ l("div", { className: e.controlRow, children: [
/* @__PURE__ */ a("button", { type: "button", className: e.button, onClick: xe, disabled: i || m || y.length === 0, children: "Undo" }),
/* @__PURE__ */ a("button", { type: "button", className: e.button, onClick: Ce, disabled: i || m || y.length === 0, children: "Clear" }),
/* @__PURE__ */ a("button", { type: "button", className: e.buttonPrimary, onClick: i ? Ne : Se, disabled: m || y.length === 0, children: i ? "Stop" : "Run" }),
/* @__PURE__ */ a("button", { type: "button", className: e.button, onClick: () => X(), disabled: i, children: "Reset" })
] }),
pe && /* @__PURE__ */ a("p", { className: e.legend, children: pe })
] }),
f === "gameOverPrompt" && /* @__PURE__ */ l("div", { className: e.promptOverlay, role: "dialog", "aria-modal": "true", "aria-label": "Game over prompt", children: [
/* @__PURE__ */ a("p", { className: e.promptTitle, children: "Game over" }),
/* @__PURE__ */ a("p", { className: e.promptText, children: b ? "Try again or stop?" : "Try again?" }),
/* @__PURE__ */ l("div", { className: e.promptOptions, children: [
/* @__PURE__ */ l(
"button",
{
type: "button",
className: `${e.promptOption} ${R === "yes" ? e.promptSelected : ""}`,
onClick: Q,
children: [
R === "yes" ? " " : "",
"Yes"
]
}
),
/* @__PURE__ */ l(
"button",
{
type: "button",
className: `${e.promptOption} ${R === "no" ? e.promptSelected : ""}`,
onClick: b ? D : ee,
children: [
R === "no" ? " " : "",
b ? "I'm done" : "No"
]
}
)
] }),
/* @__PURE__ */ a("p", { className: e.promptHint, children: "Use arrows + Enter." })
] }),
f === "successPrompt" && /* @__PURE__ */ l("div", { className: e.promptOverlay, role: "dialog", "aria-modal": "true", "aria-label": "Success prompt", children: [
/* @__PURE__ */ a("p", { className: e.promptTitle, children: "Success" }),
/* @__PURE__ */ a("p", { className: e.promptText, children: "Continue?" }),
/* @__PURE__ */ l("div", { className: e.promptOptions, children: [
/* @__PURE__ */ l(
"button",
{
type: "button",
className: `${e.promptOption} ${I === "continue" ? e.promptSelected : ""}`,
onClick: () => X(),
children: [
I === "continue" ? "▶ " : "",
"Yes"
]
}
),
/* @__PURE__ */ l(
"button",
{
type: "button",
className: `${e.promptOption} ${I === "done" ? e.promptSelected : ""}`,
onClick: D,
children: [
I === "done" ? "▶ " : "",
"I'm done"
]
}
)
] }),
/* @__PURE__ */ a("p", { className: e.promptHint, children: "Use arrows + Enter." })
] })
]
}
) });
}
const St = {
name: "chess_analogy",
displayName: "Chess Move Set Lab",
version: "1.0.0",
paramSchema: {
goalCaptures: {
type: "range",
label: "Targets To Capture",
description: "How many red targets must be captured before completion.",
default: 3,
min: 1,
max: 10,
step: 1
}
},
defaultParams: {
goalCaptures: 3
}
};
export {
Nt as default,
St as metadata
};
//# sourceMappingURL=chess_analogy.js.map