Files
dino-isle-online/test/matchcheck.mjs
T
deepseek 764da2ee6c Dino Isle Online: multiplayer dino survival game (The Isle-like)
- Zero-dependency Node server: HTTP static + hand-rolled RFC6455 WebSocket + 20Hz authoritative simulation
- Seeded island worldgen (tiles, forest decor, spawns)
- 4 playable species, needs (food/water/stamina), rest, growth stages Hatchling->Apex
- AI fauna: critters, fish shoals, Dryosaurus/Psittacosaurus herds; carcass feasting
- PvP with knockback, kill feed, chat, leaderboard, minimap, day/night cycle
- True top-down procedural dino renderer shared by menu cards and in-game
- Developer mode (` key): god-mode, x6 XP, instant evolve, spawn AI, Alt+Click tp
- Headless smoke tests, real-browser visual tests, pixel match checks
2026-08-23 07:00:08 +00:00

83 lines
3.4 KiB
JavaScript

// Verify menu-card dino and in-game dino renderer produce matching art.
'use strict';
import { createRequire } from 'node:module';
import { readdirSync, existsSync } from 'node:fs';
const require = createRequire('/root/kidcraft/node_modules/');
const { chromium } = require('playwright-core');
function findBrowserExe() {
const root = '/root/.cache/ms-playwright';
for (const dir of readdirSync(root)) {
if (!dir.startsWith('chromium')) continue;
for (const sub of ['chrome-headless-shell-linux64/chrome-headless-shell', 'chrome-linux/headless_shell']) {
const p = `${root}/${dir}/${sub}`;
if (existsSync(p)) return p;
}
}
}
let failures = 0;
const ok = (c, l) => { console.log(`${c ? 'PASS' : 'FAIL'} ${l}`); if (!c) failures++; };
const browser = await chromium.launch({ executablePath: findBrowserExe(), args: ['--no-sandbox'] });
const page = await browser.newPage();
await page.goto('http://127.0.0.1:8095/', { waitUntil: 'networkidle' });
await page.waitForTimeout(800);
const res = await page.evaluate(() => {
function avgColor(canvas) {
const cx = canvas.getContext('2d');
const d = cx.getImageData(0, 0, canvas.width, canvas.height).data;
let r = 0, g = 0, b = 0, n = 0;
for (let i = 0; i < d.length; i += 4) {
if (d[i + 3] < 40) continue; // skip transparent
r += d[i]; g += d[i + 1]; b += d[i + 2]; n++;
}
return n ? [r / n, g / n, b / n] : null;
}
const out = {};
// menu card canvases are the first canvas child of each .card
const cards = document.querySelectorAll('.card');
const keys = ['compy', 'raptor', 'trike', 'rex'];
cards.forEach((card, idx) => {
const cv = card.querySelector('canvas');
out[keys[idx]] = avgColor(cv);
});
// render in-game style dino for each species via shared renderer
out.game = {};
for (const k of keys) {
const cv = document.createElement('canvas');
cv.width = 180; cv.height = 88;
const c = cv.getContext('2d');
// identical backdrop as drawCardPreview
const g = c.createLinearGradient(0, 0, 0, 88);
g.addColorStop(0, 'rgba(120,190,140,0.16)');
g.addColorStop(1, 'rgba(30,60,45,0.28)');
c.fillStyle = g;
c.beginPath(); c.roundRect(0, 0, 180, 88, 8); c.fill();
c.save();
const scale = k === 'rex' ? 1.5 : k === 'compy' ? 0.95 : k === 'trike' ? 1.05 : 1.18;
c.translate(90 - 6 * scale, 88 * 0.60); c.scale(scale, scale);
paintDino(c, { x: 0, y: 4, dir: 0, r: 17, sp: k, stage: 2, phase: 1.15, moving: false, resting: false, bite: k === 'rex' ? 0.85 : 0, eat: 0, ci: 0, preview: true });
c.restore();
out.game[k] = avgColor(cv);
}
return out;
});
for (const k of ['compy', 'raptor', 'trike', 'rex']) {
const [r1, g1, b1] = res[k], [r2, g2, b2] = res.game[k];
const d = Math.hypot(r1 - r2, g1 - g2, b1 - b2);
ok(d < 12, `${k}: card vs game avg color Δ=${d.toFixed(1)} rgb(${r1.toFixed(0)},${g1.toFixed(0)},${b1.toFixed(0)}) vs (${r2.toFixed(0)},${g2.toFixed(0)},${b2.toFixed(0)})`);
}
// screenshots for human review
await page.screenshot({ path: '/tmp/shot-menu-v2.png' });
await page.fill('#nameInput', 'RexCheck');
await page.click('.card[data-key="rex"]');
await page.click('#playBtn');
await page.waitForTimeout(2200);
await page.screenshot({ path: '/tmp/shot-game-rex.png' });
await browser.close();
console.log(failures ? `\n${failures} CHECK(S) FAILED` : '\nMATCH CHECKS PASSED');
process.exit(failures ? 1 : 0);