/* Aggressive end-to-end: force a full game to completion (eliminations). */ const BASE = "http://localhost:3000"; const { request } = require("http"); function sse(uid) { return new Promise((resolve) => { const res = request({ host: "localhost", port: 3000, path: `/api/events?uid=${uid}`, method: "GET" }, (r) => { let buf = ""; const states = []; r.on("data", (c) => { buf += c; let idx; while ((idx = buf.indexOf("\n\n")) >= 0) { const chunk = buf.slice(0, idx); buf = buf.slice(idx + 2); for (const l of chunk.split("\n")) { if (l.startsWith("data: ")) { try { const m = JSON.parse(l.slice(6)); if (m.type === "state") states.push(m.snap); } catch (e) {} } } } }); resolve({ states, res }); }); res.on("error", () => {}); res.end(); }); } const api = async (action, uid, name, extra = {}) => { const r = await fetch(BASE + "/api", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ action, uid, name, ...extra }) }); const d = await r.json(); if (!r.ok) throw new Error(`${action}: ${d.error}`); return d; }; const sleep = (ms) => new Promise(r => setTimeout(r, ms)); const COMBOS = [[1,2],[1,3],[6,6],[5,5],[4,4],[3,3],[2,2],[1,1],[6,5],[6,4]]; const DYN = [[6,3],[6,2],[5,4],[5,3],[5,2],[4,3],[4,2],[3,2]]; function rankOf(a, b) { const x = Math.min(a, b), y = Math.max(a, b); for (let i = 0; i < COMBOS.length; i++) if (COMBOS[i][0] === y && COMBOS[i][1] === x) return i; const i = DYN.findIndex(c => c[0] === y && c[1] === x); return i >= 0 ? 10 + i : -1; } (async () => { const c1 = await api("create", null, "Alice", { roomName: "Finaltest", addBot: true }); const c2 = await api("join", null, "Bob", { roomId: c1.room }); const u1 = c1.uid, u2 = c2.uid; const [s1, s2] = await Promise.all([sse(u1), sse(u2)]); await api("start", u1, "Alice"); const t0 = Date.now(); while (Date.now() - t0 < 300000) { await sleep(1200); const snap = s1.states[s1.states.length - 1]; if (!snap) continue; if (snap.phase === "waiting" && snap.gameOver) { console.log("GAME OVER. Final:", JSON.stringify(snap.players.map(p => [p.name, p.score, p.eliminated]))); console.log("Last log lines:", snap.log.slice(-4).map(l => l.t).join(" | ")); // test restart works await api("restart", u1, "Alice"); await sleep(800); const s2snap = s2.states[s2.states.length - 1]; console.log("After restart: phase=", s2snap.phase, "scores=", JSON.stringify(s2snap.players.map(p => [p.name, p.score, p.eliminated]))); process.exit(0); } const actor = snap.phase === "challenging" && snap.pending ? snap.pending.challenger : snap.turnUid; for (const [uid, states, name] of [[u1, s1.states, "Alice"], [u2, s2.states, "Bob"]]) { if (actor !== uid) continue; if (snap.phase === "acting") await api("roll", uid, name); else if (snap.phase === "peeking") { await api("peek", uid, name, { revealed: true }); await sleep(150); const fresh = states[states.length - 1]; if (!fresh.myDice) continue; const truth = rankOf(fresh.myDice[0], fresh.myDice[1]); const minR = fresh.lastClaim ? fresh.lastClaim.rank + 1 : 0; // AGGRESSIVE: always bluff 3 above (capped), guarantees busts & eliminations let r = Math.min(Math.max(minR, truth + 3), 17); if (r < minR) await api("sendForward", uid, name); else await api("declare", uid, name, { rank: r }); } else if (snap.phase === "challenging" && snap.pending && snap.pending.challenger === uid) { await api("challenge", uid, name); // always challenge -> busts everywhere } } } console.log("TIMEOUT - game did not finish in time"); process.exit(1); })().catch(e => { console.error("FAIL:", e); process.exit(1); });