81 lines
3.8 KiB
JavaScript
81 lines
3.8 KiB
JavaScript
/* 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); });
|