| 1 | |
| 2 | |
| 3 | |
| 4 | |
| 5 | import { spawn } from 'node:child_process'; |
| 6 | import { createInterface } from 'node:readline'; |
| 7 | import path from 'node:path'; |
| 8 | import { fileURLToPath } from 'node:url'; |
| 9 | import starter from '../routes/bot.mjs'; |
| 10 | |
| 11 | const GAME = path.dirname(path.dirname(fileURLToPath(import.meta.url))); |
| 12 | const seats = { |
| 13 | 0: { kind: 'human', name: 'ada' }, |
| 14 | 1: { kind: 'agent', name: 'bob-codex' }, |
| 15 | 2: { kind: 'bot', field: 'upstream' }, |
| 16 | 3: { kind: 'bot', field: 'random' }, |
| 17 | }; |
| 18 | const room = spawn(process.execPath, ['referee/room.mjs'], { |
| 19 | cwd: process.env.ROOM_DIR ?? GAME, |
| 20 | env: { ...process.env, SS_ROOM: JSON.stringify({ seats, seed: Number(process.argv[2] ?? 3), limit: 2 }) }, |
| 21 | stdio: ['pipe', 'pipe', 'inherit'], |
| 22 | }); |
| 23 | const send = (m) => { |
| 24 | console.log(`>> ${JSON.stringify(m)}`); |
| 25 | room.stdin.write(`${JSON.stringify(m)}\n`); |
| 26 | }; |
| 27 | const short = (s) => (s.length > 300 ? `${s.slice(0, 300)}… (${s.length} bytes)` : s); |
| 28 | |
| 29 | let asked = { 0: 0, 1: 0 }; |
| 30 | let handedOver = false; |
| 31 | let stale = null; |
| 32 | let missed = false; |
| 33 | const lines = { event: 0, private: 0, request: 0 }; |
| 34 | for await (const line of createInterface({ input: room.stdout })) { |
| 35 | if (!line.startsWith('@shoal ')) { |
| 36 | console.log(` log: ${line}`); |
| 37 | continue; |
| 38 | } |
| 39 | const m = JSON.parse(line.slice(7)); |
| 40 | if (m.event) lines.event++; |
| 41 | else if (m.to !== undefined && m.version === undefined) lines.private++; |
| 42 | else if (m.version !== undefined) lines.request++; |
| 43 | console.log(`<< ${short(JSON.stringify(m))}`); |
| 44 | if (m.end) break; |
| 45 | if (m.version === undefined) continue; |
| 46 | const seat = Number(m.to); |
| 47 | if (!(seat in asked)) continue; |
| 48 | asked[seat]++; |
| 49 | |
| 50 | if (seat === 1 && asked[1] === 3 && !missed) { |
| 51 | missed = true; |
| 52 | console.log(' (seat 1 lets its time run out)'); |
| 53 | continue; |
| 54 | } |
| 55 | |
| 56 | if (seat === 0 && asked[0] === 5 && !handedOver) { |
| 57 | handedOver = true; |
| 58 | stale = m.version; |
| 59 | send({ control: { seat: '0', kind: 'agent', name: 'ada-codex' } }); |
| 60 | continue; |
| 61 | } |
| 62 | if (seat === 0 && stale !== null && m.version !== stale) { |
| 63 | send({ act: { seat: '0', version: stale, action: starter(m.obs, m.legal) } }); |
| 64 | stale = null; |
| 65 | } |
| 66 | send({ act: { seat: String(seat), version: m.version, action: starter(m.obs, m.legal) } }); |
| 67 | if (seat === 1 && asked[1] === 6) send({ control: { seat: '1', kind: 'auto' } }); |
| 68 | } |
| 69 | console.log(`\n${lines.event} public events, ${lines.private} private updates, ${lines.request} requests`); |
| 70 | room.stdin.end(); |