Games / Web
Who it's for. You write a browser sim or Hub adapter (Tsunami, Surge, or your own).
What you'll do. Encode rays into 32 (or 64) floats, POST /play/act without blocking the 0.1 s tick, and keep wave/contest extras as overlays.
For authors of a browser sim or adapter that talks to the Hub. The NPC sees classified rays, not pixels. Reference sim: apps/web-obby (Escape the Tsunami, Surge Gauntlet). Roblox companion · Licensing · Deploy & compete
| Spec | Training target | Notes |
|---|---|---|
| Walk speed | 16 | Panic sprint is animation only on tsunami |
| Learn gaps | 2–4 | Lip remain ≤ 3 (MAP_LIP_REMAIN) |
| Advanced gaps | ≤ 8 | Not a 12-stud default jump |
| Body width | ~2 | Surge corridor radius 0.7, clearance ≥ 0.5 to obstacles |
| Vision | 30 units | Classified hits, not pixels |
POST https://<hub>/api/v1/play/act with Authorization: Bearer aig_…. Do not block the 0.1 s sim on fetch. One in-flight POST per agent. Hold last action ~0.45 s, then a lip-safe local pack, then halt after ~2 s.
game_id, brain_id, room_slug. On web, place_id is unused — send Origin matching the license allowlist.obs — 32 floats (obby / obby_wave) or 64 (surge_gauntlet). Do not slice 64→32.seq — monotonic; Hub echoes it so you can drop stale replies.Build obs from pose (x,y,z, grounded, checkpoint) plus perception_rays[]: angle, pitch, direction, distance, hit_kind (platform, hazard, gap, wall, target, reward, finish). Room extras: wave height, surge distance. Do not put rivals into the 32-dim map vector; contest is overlay.
Map obs is pose + checkpoint + edge probes + yaw platform/hazard distances + down ray + nearest reward + falling + atEdge. Wave height, panic, lip remain, contest rivals, and moving-pad JumpNow are overlays or extra fields you pack — Hub greedy act does not invent them from a still ray. Surge uses a separate 64-dim vector (dest / The Surge). Do not slice 64→32.
Response: { m, j, f, seq } only — never persist this as a brain file.
const TICK_SEC = 0.1;
const HOLD_SEC = 0.45;
const HALT_SEC = 2.0;
let seq = 0;
let inFlight = false;
let lastAct = { m: 1, j: false, f: false };
let lastHubAt = 0;
let startedAt = performance.now() / 1000;
let connectionLost = false;
async function requestAct(obs) {
if (inFlight || connectionLost) return;
inFlight = true;
seq += 1;
const mySeq = seq;
try {
const res = await fetch("https://aia-hub.metamindinglab.com/api/v1/play/act", {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: "Bearer aig_YOUR_LICENSE",
},
body: JSON.stringify({
game_id: "your-game",
brain_id: "brain-uuid",
room_slug: "obby_wave",
obs,
seq: mySeq,
}),
});
const reply = await res.json();
if ((reply.seq ?? 0) < mySeq) return;
lastAct = {
m: Math.max(0, Math.min(8, Math.floor(reply.m ?? 1))),
j: reply.j === true,
f: reply.f === true,
};
lastHubAt = performance.now() / 1000;
} finally {
inFlight = false;
}
}
function currentAct(obs) {
void requestAct(obs);
const now = performance.now() / 1000;
if (lastHubAt > 0) {
const age = now - lastHubAt;
if (age <= HOLD_SEC) return { ...lastAct, mode: "hub" };
if (age <= HALT_SEC) return safetyFallback(obs);
connectionLost = true;
return { m: 0, j: false, f: false, mode: "halt" };
}
if (now - startedAt <= HALT_SEC) return safetyFallback(obs);
connectionLost = true;
return { m: 0, j: false, f: false, mode: "halt" };
}
function safetyFallback(obs) {
const falling = (obs[30] ?? 0) > 0.5;
const atEdge = (obs[31] ?? 0) > 0.5;
if (falling) return { m: 0, j: false, f: false, mode: "safety" };
if (atEdge) return { m: 1, j: true, f: false, mode: "safety" };
return { m: 1, j: false, f: false, mode: "safety" };
}
setInterval(() => {
const obs = encodeObs(sim.state); // 32 or 64 floats
const act = currentAct(obs);
if (act.mode !== "halt") applyMoveJumpFire(sim.agent, act);
}, TICK_SEC * 1000);First-party / train license only. Public production builds must not call /training/brain/export.
POST /api/v1/training/sessions — completed or failedPOST /api/v1/training/auto-coach — policy_steps[] with { o, m, j, f, r, phase, lip_*, skip_weight_update }Use campus secret VITE_ARENA_CAMPUS_SECRET / ARENA_CAMPUS_SECRET or a train grant.
Map owns the tick when airborne, jump pending, pad remain ≤ 3, side hop lip, or wave panic. Strategy and contest yield. Canonical: apps/web-obby/src/policy/skillCatalog.ts.
Contest director (optional): POST /api/v1/training/contest-director with train/campus auth. Apply block_slow / bump_deny only if map does not own the tick; typical gates confidence ≥ 0.7 and safe_to_contest ≥ 0.55.
4 agents, shared collision, pads Platform_5–Platform_9, CP ≥ 3. Block if ahead and rival behind; bump if behind and rival beside/ahead. Sessions only — no map BC. Full owner-selects-game + N-runtime example: Deploy & compete.