Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
93 lines
2.9 KiB
JavaScript
93 lines
2.9 KiB
JavaScript
const WebSocket = require('ws')
|
|
|
|
const DRAW_VEL = 2.38
|
|
const ROOMS = 10
|
|
const BASE = 'ws://127.0.0.1:3000/ws?room='
|
|
|
|
function throwFor(ws, team, broom_x, broom_y, velocity = DRAW_VEL, curl = 0, friction = 1.0) {
|
|
ws.send(JSON.stringify({ type: 'throw', team, broom_x, broom_y, velocity, curl, friction }))
|
|
}
|
|
|
|
function connect(room) {
|
|
return new Promise((resolve, reject) => {
|
|
const ws = new WebSocket(BASE + room)
|
|
const messages = []
|
|
ws.on('open', () => resolve({ ws, messages }))
|
|
ws.on('error', reject)
|
|
ws.on('message', (data) => {
|
|
try {
|
|
messages.push(JSON.parse(data.toString()))
|
|
} catch (_) {}
|
|
})
|
|
})
|
|
}
|
|
|
|
function waitFor(messages, pred, timeout = 20000) {
|
|
const start = Date.now()
|
|
return new Promise((resolve, reject) => {
|
|
const check = () => {
|
|
if (pred()) return resolve(undefined)
|
|
if (Date.now() - start > timeout) {
|
|
const errs = messages.filter(m => m.type === 'error')
|
|
return reject(new Error(
|
|
'timeout types=' + messages.map(m => m.type).join(',') +
|
|
' errs=' + JSON.stringify(errs)
|
|
))
|
|
}
|
|
setTimeout(check, 50)
|
|
}
|
|
check()
|
|
})
|
|
}
|
|
|
|
function latestState(messages) {
|
|
for (let i = messages.length - 1; i >= 0; i--) {
|
|
if (messages[i].type === 'game_state') return messages[i]
|
|
}
|
|
return null
|
|
}
|
|
|
|
function remainingSum(st) {
|
|
if (!st || !Array.isArray(st.stones_remaining)) return 16
|
|
return st.stones_remaining[0] + st.stones_remaining[1]
|
|
}
|
|
|
|
async function runRoom(i) {
|
|
const room = `LOAD${i}_${process.hrtime.bigint()}`
|
|
const p1 = await connect(room)
|
|
const p2 = await connect(room)
|
|
await waitFor(p1.messages, () => {
|
|
const st = latestState(p1.messages)
|
|
return st && st.phase === 'playing'
|
|
}, 8000)
|
|
|
|
let st = latestState(p1.messages)
|
|
const gsBefore = p1.messages.filter(m => m.type === 'game_state').length
|
|
throwFor(p1.ws, st.turn_team, 0.2, 38.5, DRAW_VEL, 0, 1.0)
|
|
await waitFor(p1.messages, () => p1.messages.some(m => m.type === 'trajectories'), 20000)
|
|
// Must wait for post-throw game_state (remaining decreased), not the pre-throw playing state.
|
|
await waitFor(p1.messages, () => {
|
|
const s = latestState(p1.messages)
|
|
return s && s.phase === 'playing' && remainingSum(s) < 16 &&
|
|
p1.messages.filter(m => m.type === 'game_state').length > gsBefore
|
|
}, 20000)
|
|
|
|
st = latestState(p1.messages)
|
|
const trajBefore = p1.messages.filter(m => m.type === 'trajectories').length
|
|
throwFor(p2.ws, st.turn_team, -0.2, 38.5, DRAW_VEL, 0, 1.0)
|
|
await waitFor(p1.messages, () => p1.messages.filter(m => m.type === 'trajectories').length > trajBefore, 20000)
|
|
|
|
p1.ws.close()
|
|
p2.ws.close()
|
|
}
|
|
|
|
;(async () => {
|
|
const start = Date.now()
|
|
await Promise.all(Array.from({ length: ROOMS }, (_, i) => runRoom(i)))
|
|
console.log(`10 rooms played start-to-throw-to-close in ${Date.now() - start}ms`)
|
|
process.exit(0)
|
|
})().catch((e) => {
|
|
console.error(e)
|
|
process.exit(1)
|
|
})
|