127 lines
5.4 KiB
JavaScript
127 lines
5.4 KiB
JavaScript
/* Smoke test: create room w/ bot, 2 human clients via SSE, play until end. */
|
|
const BASE = "http://localhost:3000";
|
|
|
|
function sse(uid) {
|
|
return new Promise((resolve) => {
|
|
const { request } = require("http");
|
|
const res = request({ host: "localhost", port: 3000, path: `/api/events?uid=${uid}`, method: "GET" }, (r) => {
|
|
let buf = "";
|
|
const states = [];
|
|
const chats = [];
|
|
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);
|
|
const lines = chunk.split("\n").filter(l => l.startsWith("data: "));
|
|
for (const l of lines) {
|
|
const m = JSON.parse(l.slice(6));
|
|
if (m.type === "state") states.push(m.snap);
|
|
else if (m.type === "chat") chats.push(m);
|
|
}
|
|
}
|
|
});
|
|
resolve({ states, chats, 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));
|
|
|
|
(async () => {
|
|
// create room (host + auto bot)
|
|
const c1 = await api("create", null, "Alice", { roomName: "Testbord", addBot: true });
|
|
const uid1 = c1.uid;
|
|
// second player
|
|
const c2 = await api("join", null, "Bob", { roomId: c1.room });
|
|
const uid2 = c2.uid;
|
|
const [s1, s2] = await Promise.all([sse(uid1), sse(uid2)]);
|
|
|
|
// host starts
|
|
await api("start", uid1, "Alice");
|
|
console.log("started. waiting for bot rounds...");
|
|
|
|
const startT = Date.now();
|
|
let turns = 0;
|
|
while (Date.now() - startT < 240000) {
|
|
await sleep(1500);
|
|
const snap = s1.states[s1.states.length - 1];
|
|
if (!snap) continue;
|
|
if (snap.phase === "waiting") { console.log("GAME ENDED:", snap.log.slice(-3).map(l => l.t)); break; }
|
|
// alice acts if it's her turn/actor
|
|
const actor = snap.phase === "challenging" && snap.pending ? snap.pending.challenger : snap.turnUid;
|
|
if (actor === uid1) {
|
|
if (snap.phase === "acting") {
|
|
await api("roll", uid1, "Alice");
|
|
} else if (snap.phase === "peeking") {
|
|
await api("peek", uid1, "Alice", { revealed: true });
|
|
await sleep(200); const fresh = s1.states[s1.states.length - 1];
|
|
const myDice = fresh.myDice;
|
|
if (myDice) {
|
|
const truth = rankOf(myDice[0], myDice[1]);
|
|
const minR = fresh.lastClaim ? fresh.lastClaim.rank + 1 : 0;
|
|
let r = Math.max(minR, truth);
|
|
if (Math.random() < 0.3) r = Math.min(r + 1, 17); // occasional bluff
|
|
if (r < minR || r > 17) { await api("sendForward", uid1, "Alice"); }
|
|
else await api("declare", uid1, "Alice", { rank: r });
|
|
}
|
|
} else if (snap.phase === "challenging" && snap.pending && snap.pending.challenger === uid1) {
|
|
// bob's claim is on the table... but alice is the actor? (challenger) - decide randomly
|
|
await (Math.random() < 0.4 ? api("challenge", uid1, "Alice") : api("pass", uid1, "Alice"));
|
|
}
|
|
turns++;
|
|
}
|
|
const actor2 = snap.phase === "challenging" && snap.pending ? snap.pending.challenger : snap.turnUid;
|
|
if (actor2 === uid2) {
|
|
if (snap.phase === "acting") await api("roll", uid2, "Bob");
|
|
else if (snap.phase === "peeking") {
|
|
await api("peek", uid2, "Bob", { revealed: true });
|
|
await sleep(200); const fresh = s2.states[s2.states.length - 1];
|
|
const myDice = fresh.myDice;
|
|
if (myDice) {
|
|
const truth = rankOf(myDice[0], myDice[1]);
|
|
const minR = fresh.lastClaim ? fresh.lastClaim.rank + 1 : 0;
|
|
let r = Math.max(minR, truth);
|
|
if (Math.random() < 0.3) r = Math.min(r + 1, 17);
|
|
if (r < minR || r > 17) await api("sendForward", uid2, "Bob");
|
|
else await api("declare", uid2, "Bob", { rank: r });
|
|
}
|
|
} else if (snap.phase === "challenging" && snap.pending && snap.pending.challenger === uid2) {
|
|
await (Math.random() < 0.4 ? api("challenge", uid2, "Bob") : api("pass", uid2, "Bob"));
|
|
}
|
|
turns++;
|
|
}
|
|
// progress report
|
|
if (turns % 10 === 0) console.log(`...turns=${turns} phase=${snap.phase} scores=${JSON.stringify(snap.players.map(p => [p.name, p.score, p.eliminated]))}`);
|
|
}
|
|
|
|
// also test chat + leave
|
|
await api("chat", uid1, "Alice", { text: "hej med jer alle sammen! 🍺" });
|
|
await sleep(300);
|
|
console.log("chats received by bob:", s2.chats.length);
|
|
console.log("total turns driven:", turns);
|
|
const rooms = await (await fetch(BASE + "/api/rooms")).json();
|
|
console.log("rooms after:", JSON.stringify(rooms.rooms.map(r => ({ id: r.id, phase: r.phase, n: r.players.length }))));
|
|
process.exit(0);
|
|
})().catch(e => { console.error("SMOKE FAIL:", e); process.exit(1); });
|
|
|
|
/* local mirror of ranking for the test driver */
|
|
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;
|
|
}
|