Add jigsaw test harness and local phaser.esm.js bundle

- __jig_test.html boots a Phaser game that loads JigsawGame with stubbed artwork/music caches and exposes window.__* helpers for driving piece grab/drop, scene inspection, and coordinate conversion.
- phaser.esm.js is a vendored webpack build of Phaser used as the "phaser" import map target so the harness can run without a package manager.
This commit is contained in:
Brian Fertig 2026-08-30 21:03:23 -06:00
parent 8dee798ae2
commit 19dff633ce
2 changed files with 248365 additions and 0 deletions

62
__jig_test.html Normal file
View File

@ -0,0 +1,62 @@
<!doctype html>
<html><head><meta charset="utf-8">
<script type="importmap">{"imports":{"phaser":"/phaser.esm.js"}}</script>
<style>html,body{margin:0;background:#111}canvas{display:block}</style>
</head><body>
<div id="log"></div>
<script type="module">
import * as Phaser from 'phaser';
import JigsawGame from './src/games/jigsaw/JigsawGame.js';
const log = (m) => { const el = document.getElementById('log'); el.textContent += m + '\n'; console.log('[harness]', m); };
const config = {
type: Phaser.WEBGL,
width: 1920, height: 1080,
parent: document.body,
backgroundColor: '#000',
scene: [ { key:'boot', create(){
// Provide the artwork cache so loadArtwork() has a real list.
this.cache.json.add('shift-artwork', { artwork: [ { name:'Alien World', path:'assets/images/shift/alien-world.png' } ] });
this.cache.json.add('music', { tracks: [] });
this.scene.start('jigsaw-game');
} }, JigsawGame ],
};
const game = new Phaser.Game(config);
window.__game = game;
// Helpers exposed for the driver.
window.__log = log;
window.__scene = () => game.scene.getScene('jigsaw-game');
window.__worldToScreen = (wx, wy) => {
const cam = window.__scene().cameras.main;
return { x: (wx - cam.scrollX) * cam.zoom + cam.x, y: (wy - cam.scrollY) * cam.zoom + cam.y };
};
window.__pieceAt = (i) => { const s = window.__scene(); const p = s.pieces[i]; return { i, x:p.img.x, y:p.img.y, placed:p.placed, groupLeader: p.group===p, depth:p.img.depth, hasInput: !!(p.img.input&&p.img.input.enabled) }; };
window.__pieces = () => window.__scene().pieces.map((p,i)=>({i, x:Math.round(p.img.x), y:Math.round(p.img.y), placed:p.placed, depth:p.img.depth}));
window.__startPuzzle = () => { const s = window.__scene(); s.selectedDiff='easy'; s.startPuzzle(); };
window.__grabAndDrop = async (pieceIdx, toWorld, steps=12, holdMs=40) => {
const s = window.__scene();
const p = s.pieces[pieceIdx];
const from = { x: p.img.x, y: p.img.y };
// mousedown at from
const f2s = window.__worldToScreen(from.x, from.y);
await __mouseDown(f2s.x, f2s.y);
await new Promise(r=>setTimeout(r,holdMs));
for (let k=1;k<=steps;k++){
const x = from.x + (toWorld.x-from.x)*k/steps, y = from.y + (toWorld.y-from.y)*k/steps;
const c = window.__worldToScreen(x,y);
await __mouseMove(c.x,c.y);
await new Promise(r=>setTimeout(r,8));
}
await __mouseUp();
return { from, to:{x:p.img.x,y:p.img.y}, moved: Math.hypot(p.img.x-from.x,p.img.y-from.y) };
};
// Low-level mouse dispatch in canvas (client) coordinates.
window.__mouseDown = (cx,cy) => { const el=game.canvas; el.dispatchEvent(new MouseEvent('mousedown',{clientX:cx,clientY:cy,bubbles:true,button:0})); };
window.__mouseMove = (cx,cy) => { const el=game.canvas; el.dispatchEvent(new MouseEvent('mousemove',{clientX:cx,clientY:cy,bubbles:true})); };
window.__mouseUp = (cx=0,cy=0) => { const el=game.canvas; el.dispatchEvent(new MouseEvent('mouseup',{clientX:cx,clientY:cy,bubbles:true,button:0})); };
log('harness ready');
</script>
</body></html>

248303
phaser.esm.js Normal file

File diff suppressed because it is too large Load Diff