diff --git a/__jig_init_test.html b/__jig_init_test.html
new file mode 100644
index 0000000..7db8b7d
--- /dev/null
+++ b/__jig_init_test.html
@@ -0,0 +1,60 @@
+
+
+
+
+
+
+
+
diff --git a/src/games/jigsaw/JigsawGame.js b/src/games/jigsaw/JigsawGame.js
index 2fd4e80..7fccb81 100644
--- a/src/games/jigsaw/JigsawGame.js
+++ b/src/games/jigsaw/JigsawGame.js
@@ -77,6 +77,10 @@ export default class JigsawGame extends Phaser.Scene {
this.selectedDiff = 'easy';
this.loadArtwork();
+ // Open the menu on a random picture, not always the first one, so each
+ // visit to the puzzle picker starts on a different puzzle. (loadArtwork
+ // guarantees at least one entry, so this index is always valid.)
+ this.imageIndex = Math.floor(Math.random() * this.artwork.length);
this.buildBackground();
this.buildHUD();
this.buildMenu();
diff --git a/tools/__jig_init_driver.mjs b/tools/__jig_init_driver.mjs
new file mode 100644
index 0000000..55a5ec2
--- /dev/null
+++ b/tools/__jig_init_driver.mjs
@@ -0,0 +1,88 @@
+// Headless-chromium CDP driver: loads a page repeatedly and reads the result
+// the page publishes in document.title (RESULT: or FAIL:).
+// Usage: node tools/__jig_init_driver.mjs [loads=8]
+import { spawn } from 'node:child_process';
+import { setTimeout as sleep } from 'node:timers/promises';
+
+const url = process.argv[2];
+const loads = Math.max(1, parseInt(process.argv[3] || '8', 10));
+if (!url) { console.error('usage: driver [loads]'); process.exit(2); }
+
+const BIN = process.env.HOME + '/.cache/ms-playwright/chromium_headless_shell-1234/chrome-headless-shell-linux64/chrome-headless-shell';
+const PORT = 9333;
+const chrome = spawn(BIN, [
+ '--headless', '--no-sandbox', '--disable-gpu',
+ `--remote-debugging-port=${PORT}`,
+ 'about:blank',
+], { stdio: ['ignore', 'pipe', 'pipe'] });
+chrome.stderr.on('data', (d) => { const t = d.toString(); if (!/Fontconfig|dbus|DBus|ozone|sandbox|WebGL|GL Driver/i.test(t)) process.stderr.write(t); });
+
+async function httpJson(path) {
+ const r = await fetch(`http://127.0.0.1:${PORT}${path}`);
+ return r.json();
+}
+
+let ws, idc = 0;
+const pending = new Map();
+const send = (method, params = {}) => new Promise((resolve, reject) => {
+ const id = ++idc;
+ pending.set(id, { resolve, reject });
+ ws.send(JSON.stringify({ id, method, params }));
+});
+
+try {
+ let targets = null;
+ for (let i = 0; i < 60; i++) {
+ try { targets = await httpJson('/json/list'); break; } catch { await sleep(250); }
+ }
+ if (!targets) throw new Error('chrome devtools endpoint never came up');
+ const page = targets.find((t) => t.type === 'page');
+ if (!page) throw new Error('no page target');
+ ws = new WebSocket(page.webSocketDebuggerUrl);
+ await new Promise((res, rej) => { ws.onopen = res; ws.onerror = rej; });
+ ws.onmessage = (m) => {
+ const msg = JSON.parse(m.data);
+ if (msg.id && pending.has(msg.id)) {
+ const { resolve, reject } = pending.get(msg.id);
+ pending.delete(msg.id);
+ if (msg.error) reject(new Error(msg.error.message)); else resolve(msg.result);
+ }
+ };
+
+ await send('Runtime.enable');
+ await send('Page.enable');
+
+ const results = [];
+ for (let k = 0; k < loads; k++) {
+ await send('Page.navigate', { url });
+ const t0 = Date.now();
+ let title = '';
+ while (Date.now() - t0 < 30000) {
+ const out = await send('Runtime.evaluate', { expression: 'document.title', returnByValue: true });
+ title = out.result.value || '';
+ if (title.startsWith('RESULT:') || title.startsWith('FAIL')) break;
+ await sleep(250);
+ }
+ results.push(title);
+ }
+
+ let ok = true;
+ const idxs = [];
+ for (const t of results) {
+ if (!t.startsWith('RESULT:')) { ok = false; console.error('bad title: ' + t); continue; }
+ const r = JSON.parse(t.slice('RESULT:'.length));
+ const valid = Number.isInteger(r.idx) && r.idx >= 0 && r.idx < r.n && r.n > 1 &&
+ r.validName && r.preview && r.menuVisible;
+ if (!valid) { ok = false; console.error('bad result: ' + JSON.stringify(r)); }
+ idxs.push(r.idx);
+ }
+ const distinct = new Set(idxs).size;
+ console.log(`initial indices over ${loads} fresh page loads: ${idxs.join(', ')}`);
+ console.log(`distinct initial images: ${distinct}/${loads}`);
+ if (distinct < 2) { ok = false; console.error('expected variety in the initial image'); }
+ console.log(ok ? 'ALL PASS' : 'FAIL');
+ process.exitCode = ok ? 0 : 1;
+} finally {
+ try { ws && ws.close(); } catch {}
+ chrome.kill('SIGKILL');
+}