# Brain Pop 6.9 — the daily game in the terminal — Brain On BNB AI # 60 seconds, one engine file, every tap replayed by the server. # # This is the complete brain-pop bundle as a single file, so it can be read in # one fetch. 6 files, 627 lines. # Download as a zip: https://brainonbnb.com/code/brain-pop.zip # Everything else: https://brainonbnb.com/code/index.txt # # No secrets are present: they are supplied at runtime through the environment. # MIT licensed. ============================================================================== === FILE: temp/terminal-site/terminal/pop-engine.mjs ============================================================================== // BRAIN POP 2.0 ENGINE (2026-11-06, operator: "skill, aim, logic, luck; the seven stations as super weapons once unlocked; // it must be e-sports world championship worthy — but 60 seconds stay, and in 60 seconds you know what to do"). // ONE FILE FOR BOTH SIDES: the terminal imports it from terminal/pop-engine.js to play, the bobai-pop worker bundles the // very same file to check (worker-pop/index.js). Same plan, same paths, same hit test, same score — a run is its inputs // (taps with time AND place, weapons with time), and the server replays them. Pure: no clock, no Math.random, no DOM. // // THE GAME, as the start screen says it: // TAP the creatures — quick pops in a row raise the multiplier (x1 … x10) // NEVER tap a red RUG candle — minus points, the combo is gone // A MISS (a tap on nothing) breaks the combo // 6 IN A ROW WITHOUT A MISS fires the next station's super power BY ITSELF (operator: "the player clicks nothing, it // comes by itself, the super boost combo") — burn, hammer, slow motion, double, gold rush, paint, cash — and an // explosion's pops count for the streak too, so a clean player sets off chains // The arena is 1000 x 1000 units, the same on every screen; a creature's hit circle shrinks as the combo grows (aim). export const V = 2, RUN_MS = 60000, AW = 1000, AH = 1000, COMBO_MS = 2000, MIN_GAP = 40, STREAK = 4, MISS_COST = 300, SLAY_PTS = 1500; // NO TWO SCORES ALIKE, NO PERFECT GAME (operator, 6.11.: "no equal scores — make them like 563742.4324762, exaggerated — // and it must always be possible to score higher, nobody can play the perfect game"): every pop is worth more the nearer // the tap is to the creature's middle (aim) and the sooner after it showed (reaction), both without steps, so a score // carries seven decimals and the best one can always be beaten. Scores are kept as whole units of 0.0000001 (UNIT), so // the terminal and the server add exactly the same integers. export const UNIT = 1e7; export const fmt = (u) => (u / UNIT).toLocaleString('en-US', { minimumFractionDigits: 7, maximumFractionDigits: 7 }); export const SPRITES = ['octo', 'bulb', 'astro', 'fridge', 'sock', 'shrimp', 'eyeball', 'duck', 'banana', 'pigeon', 'cactus', 'pizza', 'washer', 'dj']; // the seven stations of the tax split, in the order they fire (every 6th pop of a streak fires the next), what they do export const WEAPONS = [ { k: 'burn', name: 'BURN $BOBAI', pic: 'stburn', need: 8, say: 'NEXT TAP EXPLODES' }, { k: 'hammer', name: 'BURN $BOB', pic: 'stbobs', need: 10, say: 'BIG HAMMER: 6 HUGE TAPS' }, { k: 'liq', name: 'LIQ BOOST', pic: 'stliq', need: 12, say: 'SLOW MOTION' }, { k: 'defi', name: 'DEFI AGENT', pic: 'stdefi', need: 14, say: 'HELPER: HARVESTS FOR YOU (3 S)' }, { k: 'giggle', name: 'GIGGLE POT', pic: 'stgiggle', need: 16, say: 'GOLD RUSH: TRIPLE POINTS' }, { k: 'creator', name: 'RUG SLAYER', pic: 'stcreator', need: 18, say: 'EVERY RUG BLOWS UP · NEW ONES ARE GOLD (5 S)' }, { k: 'trades', name: 'TRADES', pic: 'sttrade', need: 22, say: 'CASH BONUS: STREAK x 2,500' }, ]; const rng = (seed) => { let h = 1779033703 ^ seed.length; for (let i = 0; i < seed.length; i++) { h = Math.imul(h ^ seed.charCodeAt(i), 3432918353); h = h << 13 | h >>> 19; } let a = h >>> 0; return () => { a = a + 0x6D2B79F5 | 0; let t = Math.imul(a ^ a >>> 15, 1 | a); t = t + Math.imul(t ^ t >>> 7, 61 | t) ^ t; return ((t ^ t >>> 14) >>> 0) / 4294967296; }; }; const R = (r, a, b) => Math.round(a + r() * (b - a)); // THE DAY'S PLAN: every creature's sprite, path, size, worth and time — in WORLD time (slow motion stretches it) // EVERY ROUND IS NEW, EVERY ROUND IS EQUALLY HARD (operator, 6.11.: "every round must have its randomness — the overall wins // the prize pool — random is fairest; and people play longer"): two dice. The DAY's die (r) sets what makes a round hard — // when each creature comes, how long it stays, its size and worth, which are rugs and golden, the snails and the kaiju — // the same for every round that day. The ROUND's die (q, a seed the server draws at the start) sets where each one shows, // which way it flies, its wobble and its look. So no round looks like another, nothing can be learnt by heart, and luck // never makes one round easier than another. export function plan(day, seed = 0) { const r = rng('brainpop2:' + day), q = rng('brainpop2:' + day + ':' + (seed >>> 0)), out = []; for (let t = 900; t < 57500;) { const k = t / 57500, rug = t > 7000 && r() < 0.13, gold = !rug && r() < 1 / 14, pick = r(), n = SPRITES[Math.floor(q() * SPRITES.length)]; const rad = R(r, 64 - 18 * k, 78 - 14 * k), s = { n: rug ? 'rug' : n, rug, gold, at: Math.round(t), r: rad, pts: rad < 56 ? 8 : 5 }; if (pick < 0.5) { // a line across, with a wobble; its time across the day's, its way the round's const life = R(r, 3600 - 1500 * k, 4600 - 1700 * k), side = Math.floor(q() * 4), a1 = R(q, 120, 880), b1 = R(q, 120, 880); const [x0, y0, x1, y1] = side === 0 ? [-80, a1, 1080, b1] : side === 1 ? [1080, a1, -80, b1] : side === 2 ? [a1, -80, b1, 1080] : [a1, 1080, b1, -80]; Object.assign(s, { p: 'line', x0, y0, x1, y1, w: R(q, 0, 90), f: 1 + Math.round(q() * 2), life }); } else if (pick < 0.78) { // pops up in place and goes Object.assign(s, { p: 'pop', x0: R(q, 110, 890), y0: R(q, 110, 890), life: R(r, 2300 - 900 * k, 2700 - 900 * k) }); } else { // jumps up from the bottom in an arc const x0 = R(q, 100, 900); Object.assign(s, { p: 'arc', x0, y0: 1080, x1: Math.min(920, Math.max(80, x0 + R(q, -420, 420))), h: R(q, 600, 940), life: R(r, 2400 - 500 * k, 3200 - 600 * k) }); } out.push(s); t += (820 - 500 * k) * (0.7 + 0.6 * r()); } for (const at of [15000, 31000, 48000]) { const y = R(q, 200, 800), ltr = q() < 0.5; out.push({ n: 'snail', rug: false, gold: false, at, r: 52, pts: 25, p: 'line', x0: ltr ? -80 : 1080, y0: y, x1: ltr ? 1080 : -80, y1: y, w: 30, f: 2, life: 2600 }); } const kx = R(q, 300, 700); out.push({ n: 'kaiju', rug: false, gold: false, at: 36000, r: 150, pts: 120, hp: 8, p: 'line', x0: kx, y0: 1150, x1: 1000 - kx, y1: -150, w: 260, f: 1, life: 9000 }); out.sort((x, y) => x.at - y.at); return out.map((x, i) => ({ i, ...x })); } // where a creature is at world time w (or null when it is not in the arena) export function pos(s, w) { const u = (w - s.at) / s.life; if (u < 0 || u > 1) return null; if (s.p === 'pop') return { x: s.x0, y: s.y0, u }; if (s.p === 'arc') return { x: s.x0 + (s.x1 - s.x0) * u, y: s.y0 - s.h * 4 * u * (1 - u), u }; const dx = s.x1 - s.x0, dy = s.y1 - s.y0, L = Math.hypot(dx, dy) || 1, wob = s.w * Math.sin(Math.PI * 2 * s.f * u); return { x: s.x0 + dx * u - dy / L * wob, y: s.y0 + dy * u + dx / L * wob, u }; } export const multOf = (combo) => Math.min(10, 1 + Math.floor(combo / 4)); // ONE RUN: feed it taps in time order — tap(t, x, y) — and read its state; the server replays the same export const turn = (v, x, y) => { let a = x, b = y; if (v & 4) { const c = a; a = b; b = c; } if (v & 1) a = 1000 - a; if (v & 2) b = 1000 - b; return [a, b]; }; export function createRun(P, variant = 0) { const vr = (variant | 0) & 7, at = (s, w) => { const q = pos(s, w); if (!q) return null; const [x, y] = turn(vr, q.x, q.y); return { x, y, u: q.u }; }; const S = { score: 0, combo: 0, maxc: 0, misses: 0, pops: 0, taps: 0, rugs: 0, last: -1e9, prev: -1e9, dead: new Set(), hp: {}, used: [], armed: null, hammer: 0, defi: 0, giggle: -1, creator: -1, slow: [], fired: 0, auto: [], superTo: -1, godTo: -1, timeline: [], ev: [], extra: [], charge: [0, 0, 0, 0, 0, 0, 0] }; const drainAll = (n) => { for (let k = 0; k < 7; k++) S.charge[k] = Math.max(0, S.charge[k] - n); }; // world time: slow motion runs the world at half speed for 4 s const world = (t) => { let lost = 0; for (const [a, b] of S.slow) lost += Math.max(0, Math.min(t, b) - a) / 2; return t - lost; }; const lostTo = (t) => { let lost = 0; for (const [a, b] of S.slow) lost += Math.max(0, Math.min(t, b) - a) / 2; return lost; }; const endAt = () => { let t = RUN_MS; for (let k = 0; k < 8; k++) t = RUN_MS + lostTo(t); return Math.round(t); }; const alive = (t) => { const w = world(t), L = []; for (const s of P) { if (S.dead.has(s.i)) continue; if (s.at > w) break; const q = at(s, w); if (q) L.push([s, q]); } for (const s of S.extra) { if (S.dead.has(s.i) || s.at > w) continue; const q = at(s, w); if (q) L.push([s, q]); } return L; }; const golden = (s, t) => s.gold || (s.rug && t < S.creator) || (t < S.godTo && !s.rug); const helpAt = (t) => { S.auto.push(t); S.auto.sort((a, b) => a - b); }; // byPlayer: only a pop of the player's own tap charges the stations (6.11.: helpers that charged them played on by // themselves after the last tap — 3.5 M at the last tap, 10.8 M at the end); helpers and blasts still score and keep the streak function popOne(s, t, chain, aim, byPlayer = true) { S.dead.add(s.i); if (s.rug && !(t < S.creator)) { S.rugs++; S.combo = 0; drainAll(6); S.score = Math.max(0, S.score - 4000 * UNIT); S.ev.push(['rug', s.i, t, -4000 * UNIT]); return; } S.combo = t - S.last <= COMBO_MS || chain ? S.combo + 1 : 1; S.last = t; S.maxc = Math.max(S.maxc, S.combo); S.pops++; // aim: 0.5 at the ring's edge … 1.5 dead centre; reaction: 2.5 at the first instant … towards 1 the longer it lives const rt = Math.max(0, world(t) - s.at), fAim = 0.5 + Math.max(0, Math.min(1, aim)), fRx = 1 + 600 / (rt + 400); let pts = (s.rug ? 5 : s.pts) * 100 * multOf(S.combo) * (golden(s, t) ? 5 : 1) * fAim * fRx; if (S.defi > 0) { pts *= 2; S.defi--; } if (t < S.giggle) pts *= 3; if (t < S.superTo) pts *= 2; const u = Math.round(pts * UNIT); S.score += u; S.ev.push(['pop', s.i, t, u]); if (s.n === 'kaiju') { const w = world(t), q = at(s, w) || { x: 500, y: 500 }; [[-340, -260], [340, -260], [-420, 60], [420, 60], [-200, 340], [200, 340]].forEach(([dx, dy], k) => S.extra.push({ i: 200000 + k, n: 'mini', rug: false, gold: true, at: Math.round(w), r: 56, pts: 10, p: 'line', x0: Math.round(q.x), y0: Math.round(q.y), x1: Math.round(q.x + dx), y1: Math.round(q.y + dy), w: 30, f: 1, life: 2200 })); S.ev.push(['boss', s.i, t, 6, q.x, q.y]); } if (s.n === 'octo' && !s.rug) { const w = world(t), q = at(s, w) || { x: 500, y: 500 }; [[-260, -220], [260, -220], [0, 300]].forEach(([dx, dy], k) => S.extra.push({ i: 100000 + s.i * 3 + k, n: 'mini', rug: false, gold: false, at: Math.round(w), r: 50, pts: 4, p: 'line', x0: Math.round(q.x), y0: Math.round(q.y), x1: Math.round(q.x + dx), y1: Math.round(q.y + dy), w: 0, f: 1, life: 1500 })); S.ev.push(['split', s.i, t, 3, q.x, q.y]); } if (S.combo === 8) { S.superTo = t + 3000; S.ev.push(['super', -1, t, 8]); } else if (S.combo === 16) { for (let k = 1; k <= 4; k++) helpAt(t + k * 150); S.ev.push(['mega', -1, t, 16]); } else if (S.combo >= 24 && S.combo % 8 === 0) { S.godTo = t + 5000; S.ev.push(['god', -1, t, S.combo]); } // every station's meter fills; a full one fires its power by itself if (byPlayer) for (let k = 0; k < WEAPONS.length; k++) { S.charge[k] += golden(s, t) ? 2 : 1; if (S.charge[k] >= WEAPONS[k].need) { S.charge[k] = 0; power(t, k); } } } function power(t, k) { const w = WEAPONS[k].k; S.fired++; S.used.push(k); S.ev.push(['fire', k, t, 0]); if (w === 'burn') { // the crowd's middle: the creature with the most others (rugs count against) within the blast const L = alive(t); let best = null, bv = 0; for (const [, q] of L) { let v = 0; for (const [y, r] of L) { const d = Math.hypot(r.x - q.x, r.y - q.y); if (d <= 230) v += y.rug ? -2 : 1; } if (v > bv) { bv = v; best = q; } } if (best) blast(t, best.x, best.y); else S.armed = 'burn'; } else if (w === 'hammer') S.hammer = 6; else if (w === 'liq') { const used = S.slow.reduce((a, [x, y]) => a + (y - x), 0); if (used < 24000) S.slow.push([t, t + 4000]); } else if (w === 'defi') { for (let k = 1; k <= 6; k++) helpAt(t + k * 500); } else if (w === 'giggle') S.giggle = t + 4000; else if (w === 'creator') { S.creator = t + 5000; for (const [x, q] of alive(t)) if (x.rug) { S.dead.add(x.i); const v = SLAY_PTS * UNIT * multOf(S.combo); S.score += v; S.ev.push(['slay', x.i, t, v, q.x, q.y]); } } else if (w === 'trades') { const v = S.combo * 2500 * UNIT; S.score += v; S.ev.push(['cash', k, t, v]); } } // a helper takes the creature that is closest to getting away (the one furthest along its way), never a rug function advance(t) { while (S.auto.length && S.auto[0] <= Math.min(t, endAt())) { const ta = S.auto.shift(), L = alive(ta).filter(([x]) => !x.rug || ta < S.creator); if (!L.length) continue; let pickq = L[0]; for (const c of L) if (c[1].u > pickq[1].u) pickq = c; const [x, q] = pickq; S.ev.push(['auto', x.i, ta, 0, q.x, q.y]); if (x.hp) { S.hp[x.i] = (S.hp[x.i] ?? x.hp) - 2; if (S.hp[x.i] > 0) continue; } popOne(x, ta, true, 0.6, false); S.timeline.push([ta, S.score]); } } function blast(t, x, y) { S.ev.push(['boom', -1, t, 0, x, y]); let n = 0; for (const [s, q] of alive(t)) if (Math.hypot(q.x - x, q.y - y) <= 230 + s.r * 0.5) { if (S.dead.has(s.i) || (s.rug && !(t < S.creator))) continue; /* a blast spares the rugs: the helper never costs you */ if (s.hp) { S.hp[s.i] = (S.hp[s.i] ?? s.hp) - 3; if (S.hp[s.i] > 0) continue; } popOne(s, t, n++ > 0, 1 - Math.hypot(q.x - x, q.y - y) / (230 + s.r * 0.5), false); } S.timeline.push([t, S.score]); } function tap(t, x, y) { if (!(t >= 0 && t <= endAt()) || t - S.prev < MIN_GAP || !(x >= -100 && x <= 1100 && y >= -100 && y <= 1100)) return false; advance(t); S.prev = t; S.taps++; const L = alive(t); if (S.armed === 'burn') { S.armed = null; blast(t, x, y); return true; } // the explosion: everything within 230 pops, rugs too const scale = Math.max(0.55, 1 - 0.015 * S.combo) * (S.hammer > 0 ? 3 : 1); if (S.hammer > 0) S.hammer--; let best = null, bd = 1e9, br = 1; for (const [s, q] of L) { const d = Math.hypot(q.x - x, q.y - y); if (d <= s.r * scale && d < bd) { bd = d; best = s; br = s.r * scale; } } if (!best) { S.misses++; S.combo = 0; drainAll(3); S.score = Math.max(0, S.score - MISS_COST * UNIT); S.ev.push(['miss', -1, t, -MISS_COST * UNIT, x, y]); S.timeline.push([t, S.score]); return true; } if (best.hp) { S.hp[best.i] = (S.hp[best.i] ?? best.hp) - 1; if (S.hp[best.i] > 0) { S.ev.push(['hit', best.i, t, 0]); S.last = t; return true; } } popOne(best, t, false, 1 - bd / br); S.timeline.push([t, S.score]); return true; } return { S, world, alive, tap, golden, advance, endAt }; } // THE SERVER'S CHECK: the taps [t, x, y] in order, replayed; anything out of order or out of the minute is simply not applied export function replay(day, inputs, seed = 0) { if (!Array.isArray(inputs) || inputs.length > 900) return null; const run = createRun(plan(day, seed)); let applied = 0; for (const v of inputs) { if (!Array.isArray(v)) continue; const t = Math.round(+v[0]); if (v.length === 3 && run.tap(t, Math.round(+v[1]), Math.round(+v[2]))) applied++; } run.advance(run.endAt()); const S = run.S; return { score: S.score, maxc: S.maxc, pops: S.pops, misses: S.misses, rugs: S.rugs, taps: S.taps, weapons: S.used.length, applied, timeline: S.timeline }; } ============================================================================== === FILE: worker-pop/game.js ============================================================================== // BRAIN POP DAILY (2026-11-06, operator: "make the game really viral — a real highscore and real gameplay"; "do 1 and 2"). // The pure part: the day's plan, the names, and the check of a finished run. No I/O here, so node can test it // (worker-pop/test.mjs) and the worker and the test run the very same code. // // THE PLAN IS THE SERVER'S. Every visitor plays the same 60 seconds a day: which creature comes when, how it moves, how // long it can be hit, which one is golden. The terminal fetches the plan and plays it as it is; it never makes its own. // THE SCORE IS THE SERVER'S TOO. The browser sends only "creature i, hit at t ms"; the score is counted here again with the // terminal's own rules (most 5, the rocket snail 10, the kaiju 50 after six hits, golden x5, a pop within 2.2 s of the one // before raises the combo up to x10). A pop outside its creature's time in sight, twice the same, faster than a hand, or a // run sent before its minute was over, does not count. Where on the screen a creature was the server cannot know, so a // perfect bot could still reach the day's maximum — it cannot go past it, and an equal score ranks by who sent it first. export const RUN_MS = 60000; export const FIRST_DAY = '2026-11-06'; // the season (operator, 6.11.): a board for every day until 30 Nov (the brainrot look ends 1 Dec); the overall winner of // the season gets ONE $BOBAI, every daily winner gets NOTHING (the operator's joke, said so on the page) export const LAST_DAY = '2026-11-30'; export const seasonOver = today => today > LAST_DAY; // the creatures and their moves, as the terminal's CREW (snail and kaiju come at fixed beats, not from this list) export const CREW = { octo: ['fly', 'pop', 'spin', 'float'], bulb: ['run'], astro: ['float', 'spin'], fridge: ['run', 'pop', 'rise'], sock: ['rise', 'pop'], shrimp: ['run', 'pop', 'spin'], eyeball: ['run', 'pop'], duck: ['fly', 'zoom'], banana: ['pop', 'rise', 'zoom'], pigeon: ['fly'], cactus: ['run', 'pop', 'rise'], pizza: ['run', 'spin', 'zoom'], washer: ['run', 'pop', 'spin'], dj: ['pop', 'rise', 'run'] }; // how long each move keeps a creature in reach (ms); the terminal sets the same duration on its animation export const LIFE = { run: 4600, fly: 5600, float: 6500, spin: 3200, pop: 2900, rise: 4000, zoom: 1500, snail: 2800, kaiju: 9000 }; export const PTS = { snail: 10, kaiju: 50 }, BASE = 5, GOLD = 5, COMBO_MS = 2200, COMBO_MAX = 10, KAIJU_HP = 6; // names without free text: nobody can write anything on the board, they pick from these export const ADJ = ['TURBO', 'GALAXY', 'MEGA', 'ULTRA', 'SPICY', 'COSMIC', 'NEON', 'ATOMIC', 'SNEAKY', 'HYPER', 'FUNKY', 'LASER', 'DISCO', 'ROCKET', 'GOLDEN', 'CHAOS']; // 7.11. (operator: "take the Italian style out"): English degen words, same places (a stored [a, b, n] keeps working) export const NOUN = ['LUMACONE', 'CERVELLINO', 'DEGEN', 'BANANINO', 'WHALE', 'PICCIONE', 'JEET', 'PIZZETTA', 'RUGPULLER', 'OCCHIONE', 'BAGHOLDER', 'NEURONAUTA', 'GIGACHAD', 'TARTARUGA', 'MOONBOY', 'SPAGHETTOPO']; // AN OWN NAME TOO, BRAINROTTED (operator, 6.11.: "can't one type one's own name?" — "and when it is saved, brainrot it: // the same name made into a pet name that is a brainfuck, like mmx_QQ and something funny with mmx_QQ"): letters, digits // and _ (3 to 16, as typed), so no link can ever stand on the board (no dot, no slash, no space); a short block list for // insults and for names that pose as us; the name goes into one of these, picked by the player (v); the operator can // still hide one (POST /api/pop/hide with the admin key) // 7.11. (operator: "the name has to MELT into one name like the first ones, rather fewer but really funny"): the first 14 are // the 6.11. forms on their old places; the rest melt the name into a crypto degen word (some NSFW), Italian brainrot style. // A player keeps an index (v): a list that got shorter wraps around (nameOf, myName), so no stored name ever breaks. export const ROT = ['Il Grande {N}one', '{N}ini Cervellini', 'Barone {N}elli', '{N}ino Lobotomino', 'Signor {N}etti Spaghetti', 'Capitano {N} Neuronauta', '{N}accio Discotecaccio', 'Don {N}otto Pizzotto', 'Mega {N}ella Mozzarella', '{N}one Gigacervellone', 'Papa {N}uccio Bombardiro', 'Dottor {N} Senza Neuroni', '{N}ito Turbolito', 'La Leggenda {N}ina', '{N}ino Rugpullino', '{N}one Bagholderone', '{N}accio Pumpaccio Dumpaccio', '{N}etto Shitcoinetto', '{N}ella Lamborghinella', '{N}otto Liquidotto', '{N}uccio Degenuccio', '{N}ito Jeetito', '{N}one Moonboyone', '{N}ino Gasolino', '{N}ini Copiumini', '{N}ella Hopiumella', '{N}otto Mangiawickotto', 'Ser {N}ini Leveraggini', '{N}ona Gyattona', '{N}ino Rizzino', '{N}ella Tettarella', '{N}one Culone', '{N}ino Pisellino', '{N}accio Sculacciaccio', '{N}etto Mutandetto', '{N}ona Bunga Bungona', '{N}one Sugar Paparone', '{N}ino Orgasmino Verde', '{N}ella MILFarella', 'Nonna {N}ina Perizomina', // 7.11. (operator: "great, a few more of those") '{N}etto Airdropetto', '{N}ino Whalino', '{N}accio Rektaccio', '{N}ona Stakingona', '{N}otto Memecoinotto', '{N}ello FOMOnello', '{N}ino Slippagino', '{N}ella Sniperella', '{N}uccio Honeypottuccio', '{N}one Ruggatone', '{N}ino Bottino', '{N}ito Wenlambito', '{N}ino Manine di Carta', '{N}ella Spogliarella', '{N}one Arrapatone', '{N}ino Bagnatino', '{N}ino Sculacciatino', '{N}ino Leccapiedino', '{N}one Viagrone Verde', '{N}ella Bikinella', '{N}ino Preservativino', '{N}ella Culona Pumpona', // 7.11. (operator: "in English too, crypto degen shit"): melted the English memecoin way 'Baby {N}inu 1000x', 'Safe{N}moon', '{N}coin Classic', 'Dogwif{N}', '{N}GPT Turbo Inu', '{N}y McRugface', 'Lil {N}y Bagz', 'Sir {N}alot the Rekt', '{N}inator Exit 3000', '{N}zilla Wen Lambo', '{N}ster the Shitcoinster', 'Big {N}y Dumpy', '{N}stein Degenstein', 'Rugpull{N}ski', '{N}ski Bagholderski', '{N}son McPump', 'Ape{N} Jeetface', '{N}69 Moonshot Inu', '{N}ussy Liquidity', '{N}ussy Pumpussy', 'Daddy {N}ussy Gainz', '{N}boner Greencandle', '{N}dong Moonshot', 'Thot{N} the Airdrop', // 7.11. (operator: "more English, more NSFW degen crypto shit") 'Horny{N} Moonboy', '{N}tits McLiquidity', '{N}dick Diamondhands', 'Milf{N} Staking', 'Step{N} Got Rugged', '{N}fap Fapcoin', 'Only{N}Fans Airdrop', '{N}hub Premium Degen', '{N}69420 Inu', 'Sugar{N}daddy Whale', '{N}gasm Greencandle', 'Cum{N} to the Moon', '{N}thicc Bagholder', 'Gyatt{N} Gainz', '{N}bussy Liquidity', '{N}nut No Stop Loss', 'Edging{N} the ATH', '{N}hard Pumpenstein', 'Lick{N} the Chart', 'Rizz{N} the Rugger', 'Naked{N} Margin Call', 'Spanky{N} McDump', 'Booty{N} Bagz', '{N}horny Shortsqueeze', 'Kinky{N} the Whale', '{N}ballz Diamond Edition', // 7.11. (operator: "more of those") '{N}pegging the Peg', 'Depeg{N} Daddy', 'Throbbing{N} Bull Run', 'Copetrader{N}', '{N}moan Bear Market', 'Wet{N} Wallet', '{N}strap 100x Leverage', 'Simp{N} the Shiller', 'Shill{N} McShillface', '{N}lube Liquidity Pool', 'Lubed{N} Slippage', 'Thirsty{N} Mempool', 'Sloppy{N} Seconds Buyer', 'Hump{N} the Top', 'Squirt{N} the Airdrop', 'Moist{N} Mooncake', 'Bulge{N} Bullrun', 'Freaky{N} Frontrunner', 'Sandwich{N} Threesome', 'Daddy{N} MEV Bot', '{N}69 Gas War', 'Ballsdeep{N} in Bags', '{N}stroke Stochastic', 'Quickie{N} Scalper', 'Premature{N} Exit', 'Hodl{N} Me Daddy', // 7.11. (operator: "Italian melted again, more of them, then it is enough"): the last batch, to keep the mix '{N}ino Candelino Verde', '{N}one Dumpone', '{N}accio Liquidaccio', '{N}etto Frontrunnetto', '{N}uccio Stakeruccio', '{N}ella Blockchainella', '{N}otto Walletotto', '{N}ino Tokenino', '{N}one Rialzistone', '{N}ella Ribassistella', '{N}accio Shillaccio', '{N}ino Scalpino', '{N}one Hodlone', '{N}etto Bridgetto', '{N}uccio Gweiuccio', '{N}ito Mintito', '{N}ona Burnona', '{N}ello Pumpatello', '{N}one Bollone', '{N}ino Monetino Scemino', 'Zio {N}ino Ruggino', '{N}accio Panicaccio', '{N}ella Fomella', '{N}one Ballenone', '{N}one Pallone Bitcoinone', '{N}ella Zoccolella', '{N}one Cazzone', '{N}ino Cazzettino', '{N}accio Chiappaccio', '{N}ona Tettona', '{N}otto Scopotto', '{N}ella Porcella', '{N}uccio Porcelluccio', '{N}ino Nudino', '{N}one Pompinone', '{N}ella Bagnatella', '{N}ina Mutandina Rosa', '{N}accio Sederaccio']; // THE DICE'S MIX (7.11., operator: "a healthy mix and a healthy brainrot mechanism"): every form has a kind, i = Italian, // e = English, upper case = NSFW; the terminal's dice takes turns (Italian / English, clean / NSFW: i E I e ...) and // never shows one of the last 20 again, so no single kind ever floods the board const NSFW = /cul|tett|pisell|mutand|sculacc|orgasm|milf|perizom|gyatt|bunga|sugar|spogliar|arrapat|bagnat|leccapied|viagr|bikin|preservat|ussy|boner|dong|thot|horny|tits|dick|fap|onlyfans|hub |69|daddy|gasm|cum|thicc|nut |edging|hard |lick|naked|spanky|booty|kinky|ballz|pegging|throbbing|moan|wet|strap|lube|thirsty|sloppy|hump|squirt|moist|bulge|freaky|threesome|ballsdeep|stroke|quickie|premature|zoccol|cazz|chiapp|scop|porcell|nudin|pompin|sederacc|diamond balls|rizz|senza mutande|^step|^only/i; export const ROT_KIND = ROT.map((f, i) => { const en = i >= 62 && i < 138, k = en ? 'e' : 'i'; return NSFW.test(f) ? k.toUpperCase() : k; }).join(''); // (6.11. evening, operator: "every name is allowed, it is brainrot; otherwise I delete them by hand") — the block list is gone; // only the shape stays: letters, digits, _ (no link, no space, no HTML); the operator hides one with POST /api/pop/hide export const ROT_MAX = Math.max(86, ROT.length); // the longest the list ever was live (7.11.): a form beyond it never existed export const ownName = v => { if (typeof v !== 'string') return null; const s = v.normalize('NFKC').trim(); return /^[A-Za-z0-9_]{3,16}$/.test(s) && /[A-Za-z]/.test(s) ? s : null; }; export const nameOf = v => { if (v && typeof v === 'object' && !Array.isArray(v)) { const base = ownName(v.own), k = Number(v.v); if (!base || !Number.isInteger(k) || k < 0 || k >= ROT_MAX) return null; return ROT[k % ROT.length].replace('{N}', base); } if (!Array.isArray(v) || v.length !== 3) return null; const [a, b, n] = v.map(Number); if (![a, b, n].every(Number.isInteger) || a < 0 || a >= ADJ.length || b < 0 || b >= NOUN.length || n < 1 || n > 99) return null; return ADJ[a] + ' ' + NOUN[b] + ' ' + n; }; export const dayOf = ms => new Date(ms).toISOString().slice(0, 10); export const dayNo = day => Math.round((Date.parse(day + 'T00:00:00Z') - Date.parse(FIRST_DAY + 'T00:00:00Z')) / 86400e3) + 1; export const validDay = (day, today) => /^\d{4}-\d{2}-\d{2}$/.test(day || '') && day >= FIRST_DAY && day <= today && day <= LAST_DAY && dayOf(Date.parse(day + 'T00:00:00Z')) === day; function rng(seedText) { let h = 1779033703 ^ seedText.length; for (let i = 0; i < seedText.length; i++) { h = Math.imul(h ^ seedText.charCodeAt(i), 3432918353); h = h << 13 | h >>> 19; } let a = h >>> 0; return () => { a = a + 0x6D2B79F5 | 0; let t = Math.imul(a ^ a >>> 15, 1 | a); t = t + Math.imul(t ^ t >>> 7, 61 | t) ^ t; return ((t ^ t >>> 14) >>> 0) / 4294967296; }; } // the day's 60 seconds: closer and closer beats, the rocket snail three times, the kaiju once (six hits) export function plan(day) { const r = rng('brainpop:' + day), names = Object.keys(CREW), out = []; for (let t = 1200; t < 57000;) { const n = names[Math.floor(r() * names.length)], ms = CREW[n], m = ms[Math.floor(r() * ms.length)], gold = m !== 'zoom' && r() < 1 / 12; out.push({ n, m, at: Math.round(t), life: LIFE[m], gold }); t += (1150 - 760 * (t / 57000)) * (0.7 + 0.6 * r()); } for (const at of [14000, 33000, 50500]) out.push({ n: 'snail', m: 'run', at, life: LIFE.snail, gold: false }); out.push({ n: 'kaiju', m: 'kaiju', at: 38000, life: LIFE.kaiju, gold: false }); out.sort((x, y) => x.at - y.at); return out.map((s, i) => ({ i, ...s, life: Math.min(s.life, RUN_MS - s.at) })); } const ptsOf = s => (PTS[s.n] || BASE) * (s.gold ? GOLD : 1); // the check of a finished run. pops: [[i, t], ...] in the order they happened; hits: the kaiju's hits before its pop export function check(day, pops, hits = []) { const P = plan(day), seen = new Set(), tl = []; if (!Array.isArray(pops) || pops.length > P.length || !Array.isArray(hits) || hits.length > 40) return { ok: false, why: 'bad shape' }; const inside = (s, t) => Number.isInteger(t) && t >= s.at - 150 && t <= s.at + s.life + 400 && t <= RUN_MS + 400; let score = 0, combo = 0, last = -1e9, maxc = 0, prev = -1, dropped = 0; const drops = [], drop = (p, why) => { dropped++; if (drops.length < 10) drops.push([p, why]); }; for (const p of pops) { // a pop that breaks a rule does not count (and does not feed the combo); the run itself stays (6.11.: one late // creature on a busy device threw a whole honest minute away) if (!Array.isArray(p) || p.length !== 2) { drop(p, "bad pop"); continue; } const [i, t] = p, s = P[i]; if (!s || seen.has(i)) { drop(p, "unknown or twice"); continue; } if (!inside(s, t)) { drop(p, "outside its time"); continue; } if (t < prev || t - prev < 30) { drop(p, "order or faster than a hand"); continue; } if (s.n === 'kaiju') { const h = hits.filter(x => Array.isArray(x) && x[0] === i && inside(s, x[1]) && x[1] <= t).map(x => x[1]).sort((a, b) => a - b); let ok = h.length >= KAIJU_HP - 1; for (let k = 1; k < h.length; k++) if (h[k] - h[k - 1] < 60) ok = false; if (h.length && t - h[h.length - 1] < 60) ok = false; if (!ok) { drop(p, "kaiju without six hits"); continue; } } seen.add(i); prev = t; combo = t - last < COMBO_MS ? combo + 1 : 1; last = t; maxc = Math.max(maxc, Math.min(combo, COMBO_MAX)); score += ptsOf(s) * Math.min(combo, COMBO_MAX); tl.push([t, score]); } return { ok: true, score, maxc, n: tl.length, dropped, drops, timeline: tl }; } ============================================================================== === FILE: worker-pop/index.js ============================================================================== // BOBAI POP — the Brain Pop server (2026-11-06). The plan of the day, the boards, the check of every run, the prize pool. // Behind brainonbnb.com/api/pop/* (proxied by dashboard/_worker.js, so the page talks to its own origin). // // BRAIN POP 2.0 (operator, 6.11.: "aim, skill, logic, luck; the seven stations as super powers that come by themselves; // e-sports world championship worthy; 60 seconds stay"): the game is temp/terminal-site/terminal/pop-engine.mjs — ONE // file, played by the terminal and bundled here. A run is its taps [t, x, y]; this worker REPLAYS them with the same // engine, so the score on the board is the score the taps really made, aim included. 2.0 has its own tables (runs3, // best3): the boards began again with it (operator: "reset"); 1.0's tables stay as they were. // // GET /api/pop/day[?d=YYYY-MM-DD] the day's number, its 2.0 plan, the names to pick from, the day's top 10 // POST /api/pop/start {pid, name, d?} a run begins: its id; d = an older day is a rematch, not ranked // POST /api/pop/finish {run, inputs:[[t,x,y]]} the run is replayed and counted, its place told // GET /api/pop/run/ one finished run: name, day, score, timeline (ghost), its taps (replay) // GET /api/pop/top?scope=day|all[&d=] the boards (top 10) // GET /api/pop/pool the prize pool: the $BOBAI in the Giggle/prize wallet, from the chain // POST /api/pop/name {pid, name} a new name shows on the boards at once // GET /api/pop/claim/ the prize check (the player's private code) // GET /api/pop/stats the funnel per day // GET /api/pop/wallet?pid= the player's holder badge (7.11.): linked?, $BOBAI, badge // POST /api/pop/wallet {pid, wallet, issued, signature} | {pid, unlink: true} link a wallet by a signature (no transaction) // // No free text anywhere: a name is three numbers into lists or the player's own (letters, digits, _), brainrotted. import { nameOf, dayOf, dayNo, validDay, seasonOver, ADJ, NOUN, ROT, ROT_KIND, FIRST_DAY, LAST_DAY } from './game.js'; import { checkLink, balanceOf, tierOf, linkMessage, TIERS, STALE_MS } from './wallet.js'; // 6.9 ONCE MORE (operator, 6.11.: 'back to the first 6.9, in the whole terminal, with super combos and helpers'): the tap game, // pop-engine.mjs, now with SUPER/MEGA/GOD combos and the DeFi helper; its boards (runs3/best3) began at 0 with it import { plan, replay, RUN_MS, V, WEAPONS, UNIT } from '../temp/terminal-site/terminal/pop-engine.mjs'; // scores are kept in whole units of 0.0000001 (the engine's UNIT) and sent as points with seven decimals const P7 = (u) => Math.round(u) / UNIT; const rowsP = (L) => L.map((r) => ({ ...r, score: P7(r.score) })); // a player's private code (the overall winner shows it to claim the prize): the first 8 of sha-256 of the browser's id const claimOf = async pid => [...new Uint8Array(await crypto.subtle.digest('SHA-256', new TextEncoder().encode('brainpop:' + pid)))].slice(0, 4).map(b => b.toString(16).padStart(2, '0')).join('').toUpperCase(); const J = (o, s = 200, cache = 'no-store') => new Response(JSON.stringify(o), { status: s, headers: { 'content-type': 'application/json; charset=utf-8', 'cache-control': cache } }); const STARTS_PER_IP_DAY = 120; const test0 = pid => String(pid).startsWith('f00d'); // our own checks: never on the board, never in the counts // THE PRIZE POOL (operator, 6.11.): the Giggle/prize wallet's $BOBAI — the 808.41 it holds are the start, every $BOBAI // donated to it adds; on 30 Nov the overall #1 gets ALL of it. Anything else sent there (BNB, stables) stays there and is // NOT part of the pool — the page says it plainly: send only $BOBAI. const POOL_WALLET = '0x5E4102520A71B2AA18a1208330d4848dea4BD105', BOBAI = '0x245c386dcfed896f5c346107596141e5edcbffff'; const RPCS = ['https://bsc-dataseed.binance.org', 'https://bsc-dataseed1.defibit.io', 'https://bsc.publicnode.com']; export default { async fetch(req, env) { const u = new URL(req.url); if (!u.pathname.startsWith('/api/pop/')) return J({ error: 'not found' }, 404); const o = env.POP.get(env.POP.idFromName('board')); return o.fetch(req); }, }; export class PopBoard { constructor(state, env) { this.sql = state.storage.sql; this.env = env; this.pool = null; // 2.0's own tables; inputs = the taps of a run (its replay) this.sql.exec(`CREATE TABLE IF NOT EXISTS runs3 (id TEXT PRIMARY KEY, day TEXT, pid TEXT, name TEXT, ranked INT, ip TEXT, started INT, finished INT, score INT, maxc INT, pops INT, misses INT, rugs INT, timeline TEXT, inputs TEXT)`); this.sql.exec(`CREATE TABLE IF NOT EXISTS best3 (day TEXT, pid TEXT, name TEXT, score INT, run TEXT, at INT, PRIMARY KEY (day, pid))`); this.sql.exec(`CREATE INDEX IF NOT EXISTS best3_day ON best3 (day, score DESC, at)`); this.sql.exec(`CREATE INDEX IF NOT EXISTS runs3_ip ON runs3 (day, ip)`); // every round its own seed (operator 6.11.: "every round must have its randomness"): drawn at the start, replayed with try { this.sql.exec('ALTER TABLE runs3 ADD COLUMN seed INT'); } catch {} // 1.0's tables go (operator, 6.11.: "the 1.0 highscores need no archive") this.sql.exec('DROP TABLE IF EXISTS runs'); this.sql.exec('DROP TABLE IF EXISTS best'); // the first 6.9 tables go too: with the super combos and helpers the scores are new (operator: 'highscore at 0') this.sql.exec('DROP TABLE IF EXISTS runs2'); this.sql.exec('DROP TABLE IF EXISTS best2'); // the game's funnel per day (GET /api/pop/stats) this.sql.exec(`CREATE TABLE IF NOT EXISTS counts (day TEXT, k TEXT, n INT, PRIMARY KEY (day, k))`); // THE HOLDER BADGE (7.11.): a player's linked wallet and its $BOBAI as last read; only the badge reaches a board // who opened the game window, once per player and day (7.11.: 'opened' counts every opening, not people) this.sql.exec(`CREATE TABLE IF NOT EXISTS seen (day TEXT, pid TEXT, PRIMARY KEY (day, pid))`); this.sql.exec(`CREATE TABLE IF NOT EXISTS wallets (pid TEXT PRIMARY KEY, wallet TEXT, bobai REAL, read_at INT, linked_at INT)`); } rows(q, ...a) { return [...this.sql.exec(q, ...a)]; } bump(day, k, by = 1) { this.sql.exec(`INSERT INTO counts (day, k, n) VALUES (?, ?, ?) ON CONFLICT (day, k) DO UPDATE SET n = n + ?`, day, String(k).slice(0, 60), by, by); } top(scope, day, n = 10) { if (scope === 'day') return this.badged(rowsP(this.rows(`SELECT b.name, b.score, b.run, w.bobai FROM best3 b LEFT JOIN wallets w ON w.pid = b.pid WHERE b.day = ? ORDER BY b.score DESC, b.at ASC LIMIT ?`, day, n))); // overall: a player's best single run of the season (under the name they play as now) return this.badged(rowsP(this.rows(`SELECT b.name, b.score, b.run, w.bobai FROM best3 b JOIN (SELECT pid, MAX(score) s FROM best3 GROUP BY pid) m ON b.pid = m.pid AND b.score = m.s LEFT JOIN wallets w ON w.pid = b.pid GROUP BY b.pid ORDER BY b.score DESC, MIN(b.at) ASC LIMIT ?`, n))); } // the badge in place of the balance: a board never carries a wallet or an amount badged(L) { return L.map(({ bobai, ...r }) => ({ ...r, badge: bobai == null ? null : tierOf(bobai) })); } // a linked wallet read again when its reading is older than 6 h (a holder who sold loses the badge) async refreshWallet(pid, force = false) { const w = this.rows('SELECT wallet, bobai, read_at FROM wallets WHERE pid = ?', pid)[0]; if (!w) return null; if (!force && Date.now() - w.read_at < STALE_MS) return w; const b = await balanceOf(w.wallet); if (b == null) return w; this.sql.exec('UPDATE wallets SET bobai = ?, read_at = ? WHERE pid = ?', b, Date.now(), pid); return { ...w, bobai: b, read_at: Date.now() }; } players(day) { return this.rows(`SELECT COUNT(*) c FROM best3 WHERE day = ?`, day)[0].c; } async readPool() { if (this.pool && Date.now() - this.pool.at < 120e3) return this.pool; const data = '0x70a08231' + POOL_WALLET.slice(2).toLowerCase().padStart(64, '0'); for (const rpc of RPCS) { try { const r = await fetch(rpc, { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify({ jsonrpc: '2.0', id: 1, method: 'eth_call', params: [{ to: BOBAI, data }, 'latest'] }), signal: AbortSignal.timeout(6000) }); const j = await r.json(); if (!j.result) continue; const wei = BigInt(j.result); this.pool = { at: Date.now(), bobai: Number(wei / 10n ** 14n) / 1e4 }; return this.pool; } catch {} } return this.pool; // the last answer, if the chain did not answer now } async fetch(req) { const u = new URL(req.url), now = Date.now(), today = dayOf(now), p = u.pathname.slice('/api/pop/'.length); const ip = (req.headers.get('x-pop-ip') || req.headers.get('cf-connecting-ip') || '').slice(0, 64); try { if (req.method === 'GET' && p === 'day') { if (!u.searchParams.has('own')) this.bump(today, u.searchParams.get('d') && u.searchParams.get('d') !== today ? 'opened_challenge' : 'opened'); { const sp = String(u.searchParams.get('p') || ''); if (/^[0-9a-f]{16}$/.test(sp) && !test0(sp) && !u.searchParams.has('own') && !this.rows('SELECT 1 FROM seen WHERE day = ? AND pid = ?', today, sp).length) { this.sql.exec('INSERT INTO seen (day, pid) VALUES (?, ?)', today, sp); this.bump(today, 'opened_players'); } } const d = u.searchParams.get('d') || today; if (!validDay(d, today)) return J({ error: 'no such day' }, 400); return J({ v: V, day: d, no: dayNo(d), today, ms: RUN_MS, plan: plan(d), weapons: WEAPONS, names: { adj: ADJ, noun: NOUN, rot: ROT, kind: ROT_KIND }, top: this.top('day', d), players: this.players(d) }); } // a share, counted by where it went (7.11.: nobody knew whether runs are shared); only known targets, nothing stored if (req.method === 'GET' && p === 'share') { const to = String(u.searchParams.get('to') || ''); if (!/^(x|telegram|whatsapp|discord|copy|sheet|save|dare|game)$/.test(to)) return J({ error: 'unknown target' }, 400); if (!u.searchParams.has('own')) this.bump(today, 'share_' + to); return J({ ok: true }); } if (req.method === 'GET' && p === 'pool') { const pl = await this.readPool(); return J({ wallet: POOL_WALLET, bobai: pl ? pl.bobai : null, read_at: pl ? new Date(pl.at).toISOString() : null, rule: 'Only $BOBAI counts. The overall #1 on 30 Nov gets all of it. Anything else sent to this wallet is lost.' }, 200, 'public, max-age=60'); } // the operator hides a name from every board (it becomes a random one); needs the admin key (wrangler secret POP_ADMIN) if (req.method === 'POST' && p === 'hide') { const b = await req.json().catch(() => ({})); if (!this.env.POP_ADMIN || b.key !== this.env.POP_ADMIN) return J({ error: 'no' }, 403); const to = 'HIDDEN ' + Math.floor(Math.random() * 900 + 100), n1 = this.rows('SELECT COUNT(*) c FROM best3 WHERE name = ?', String(b.name || ''))[0].c; this.sql.exec('UPDATE best3 SET name = ? WHERE name = ?', to, String(b.name || '')); this.sql.exec('UPDATE runs3 SET name = ? WHERE name = ?', to, String(b.name || '')); return J({ hidden: String(b.name || ''), as: to, rows: n1 }); } if (req.method === 'GET' && p === 'stats') { const out = {}; for (const r of this.rows('SELECT day, k, n FROM counts WHERE day >= ? ORDER BY day, k', dayOf(now - 13 * 86400e3))) (out[r.day] ||= {})[r.k] = r.n; for (const d of Object.keys(out)) out[d].players = this.players(d); return J({ days: out }); } if (req.method === 'GET' && p === 'top') { const s = u.searchParams.get('scope') === 'all' ? 'all' : 'day', last = today > LAST_DAY ? LAST_DAY : today, d = u.searchParams.get('d') || last; if (s === 'day' && !validDay(d, today)) return J({ error: 'no such day' }, 400); return J({ scope: s, day: s === 'day' ? d : last, no: dayNo(s === 'day' ? d : last), first: FIRST_DAY, last, rows: this.top(s, d, 10), players: s === 'day' ? this.players(d) : this.rows('SELECT COUNT(DISTINCT pid) c FROM best3')[0].c }, 200, 'public, max-age=20'); } // the prize check: which player has this code, and where they stand overall (no code is ever listed anywhere) if (req.method === 'GET' && p.startsWith('claim/')) { const code = p.slice(6).toUpperCase(); if (!/^[0-9A-F]{8}$/.test(code)) return J({ error: 'a code is 8 hex characters' }, 400); const all = this.rows('SELECT pid, MAX(score) s FROM best3 GROUP BY pid ORDER BY s DESC'); for (let k = 0; k < all.length; k++) if (await claimOf(all[k].pid) === code) { const n = this.rows('SELECT name FROM best3 WHERE pid = ? ORDER BY score DESC LIMIT 1', all[k].pid)[0]; return J({ code, name: n && n.name, best: P7(all[k].s), overall_place: k + 1, players: all.length, season_over: seasonOver(today) }); } return J({ code, found: false }); } if (req.method === 'GET' && p.startsWith('run/')) { const r = this.rows(`SELECT id, day, name, score, maxc, pops, misses, rugs, timeline, inputs, ranked, seed FROM runs3 WHERE id = ? AND finished IS NOT NULL`, p.slice(4))[0]; if (!r) return J({ error: 'no such run' }, 404); return J({ v: V, run: r.id, day: r.day, no: dayNo(r.day), seed: r.seed, name: r.name, score: P7(r.score), maxc: r.maxc, pops: r.pops, misses: r.misses, rugs: r.rugs, ranked: !!r.ranked, timeline: JSON.parse(r.timeline || '[]'), inputs: JSON.parse(r.inputs || '[]') }, 200, 'public, max-age=300'); } // THE HOLDER BADGE (7.11.): read or link; a link is a signature (personal_sign) of linkMessage, never a transaction if (p === 'wallet' && req.method === 'GET') { const pid = String(u.searchParams.get('pid') || ''); if (!/^[0-9a-f]{16}$/.test(pid)) return J({ error: 'pid (16 hex) needed' }, 400); const w = await this.refreshWallet(pid); return J({ linked: !!w, wallet: w ? w.wallet : null, bobai: w ? w.bobai : null, badge: w ? tierOf(w.bobai) : null, tiers: TIERS, message: 'BOBAI Brain Pop holder badge\nplayer: \nwallet: \nissued: ' }); } if (p === 'wallet' && req.method === 'POST') { const b = await req.json().catch(() => ({})); if (b.unlink) { const pid = String(b.pid || ''); if (!/^[0-9a-f]{16}$/.test(pid)) return J({ error: 'pid (16 hex) needed' }, 400); this.sql.exec('DELETE FROM wallets WHERE pid = ?', pid); return J({ linked: false }); } const c = await checkLink(b, now); if (!c.ok) return J({ error: c.error }, c.status); const bal = await balanceOf(c.wallet); if (bal == null) return J({ error: 'the chain did not answer, try again in a minute' }, 503); this.sql.exec('INSERT OR REPLACE INTO wallets (pid, wallet, bobai, read_at, linked_at) VALUES (?, ?, ?, ?, ?)', c.pid, c.wallet, bal, now, now); if (!test0(c.pid)) this.bump(today, 'wallet_linked' + (tierOf(bal) ? '_' + tierOf(bal) : '')); return J({ linked: true, wallet: c.wallet, bobai: bal, badge: tierOf(bal), tiers: TIERS }); } // a new name shows on the boards at once (BRAINROT IT or the dice), not only after the next run if (req.method === 'POST' && p === 'name') { const b = await req.json().catch(() => ({})), name = nameOf(b.name), pid = String(b.pid || ''); if (!/^[0-9a-f]{16}$/.test(pid) || !name) return J({ error: 'a name is 3 to 16 letters, digits or _ (no spaces, no dots)' }, 400); if (!test0(pid)) this.sql.exec('UPDATE best3 SET name = ? WHERE pid = ?', name, pid); return J({ name, rows: this.rows('SELECT COUNT(*) c FROM best3 WHERE pid = ?', pid)[0].c }); } if (req.method === 'POST' && p === 'start') { const b = await req.json().catch(() => ({})), name = nameOf(b.name), pid = String(b.pid || ''); if (b.v !== V) return J({ error: 'Brain Pop 6.9 is here: reload the page' }, 426); if (!/^[0-9a-f]{16}$/.test(pid)) return J({ error: 'pid (16 hex) needed' }, 400); if (!name) return J({ error: 'a name is 3 to 16 letters, digits or _ (no spaces, no dots)' }, 400); if (seasonOver(today) && !b.d) return J({ error: 'the season is over (30 Nov); the boards stay' }, 410); const d = b.d || today; if (!validDay(d, today)) return J({ error: 'no such day' }, 400); if (ip && this.rows(`SELECT COUNT(*) c FROM runs3 WHERE day = ? AND ip = ?`, today, ip)[0].c >= STARTS_PER_IP_DAY) return J({ error: 'enough runs from here today, come back tomorrow (UTC)' }, 429); const id = crypto.randomUUID().replace(/-/g, '').slice(0, 12), test = test0(pid); /* our own checks play for real but never reach the board */ const seed = crypto.getRandomValues(new Uint32Array(1))[0] >>> 1; // the round's die this.sql.exec(`INSERT INTO runs3 (id, day, pid, name, ranked, ip, started, seed) VALUES (?, ?, ?, ?, ?, ?, ?, ?)`, id, d, pid, name, d === today && !test ? 1 : 0, ip, now, seed); // a player is the browser, the name is its latest: every board row of this player takes it at once if (!test) this.sql.exec('UPDATE best3 SET name = ? WHERE pid = ?', name, pid); if (Math.random() < 0.02) this.sql.exec(`DELETE FROM runs3 WHERE finished IS NULL AND started < ?`, now - 86400e3); if (!test) this.bump(today, d === today ? 'started' : 'started_rematch'); return J({ v: V, run: id, day: d, no: dayNo(d), seed, ranked: d === today && !test, name }); } if (req.method === 'POST' && p === 'finish') { const b = await req.json().catch(() => ({})); if (!b.inputs) return J({ error: 'Brain Pop 6.9 is here: reload the page' }, 426); const r = this.rows(`SELECT * FROM runs3 WHERE id = ?`, String(b.run || ''))[0]; if (!r) return J({ error: 'no such run' }, 404); if (r.finished) return J({ error: 'this run is counted already' }, 409); const took = now - r.started; if (took < RUN_MS - 3000) return J({ error: 'the minute is not over yet' }, 400); if (took > 15 * 60e3) return J({ error: 'too late for this run, start a new one' }, 400); const c = replay(r.day, b.inputs, r.seed || 0); if (!c) { this.bump(r.day, 'refused_run'); this.sql.exec(`UPDATE runs3 SET finished = ?, score = 0, pops = 0 WHERE id = ?`, now, r.id); return J({ error: 'not counted: the taps could not be read' }, 422); } this.sql.exec(`UPDATE runs3 SET finished = ?, score = ?, maxc = ?, pops = ?, misses = ?, rugs = ?, timeline = ?, inputs = ? WHERE id = ?`, now, c.score, c.maxc, c.pops, c.misses, c.rugs, JSON.stringify(c.timeline), JSON.stringify(b.inputs.slice(0, 900)), r.id); let best = c.score, rank = null, players = null; if (r.ranked) { const old = this.rows(`SELECT score FROM best3 WHERE day = ? AND pid = ?`, r.day, r.pid)[0]; if (!old || c.score > old.score) this.sql.exec(`INSERT OR REPLACE INTO best3 (day, pid, name, score, run, at) VALUES (?, ?, ?, ?, ?, ?)`, r.day, r.pid, r.name, c.score, r.id, now); else best = old.score; const me = this.rows(`SELECT score, at FROM best3 WHERE day = ? AND pid = ?`, r.day, r.pid)[0]; rank = this.rows(`SELECT COUNT(*) c FROM best3 WHERE day = ? AND (score > ? OR (score = ? AND at < ?))`, r.day, me.score, me.score, me.at)[0].c + 1; players = this.players(r.day); } await this.refreshWallet(r.pid).catch(() => null); // a badge on the board is as fresh as the player's last run if (!test0(r.pid)) { this.bump(r.day, 'finished'); if (c.applied < b.inputs.length) this.bump(r.day, 'taps_not_applied', b.inputs.length - c.applied); } return J({ v: V, run: r.id, day: r.day, no: dayNo(r.day), seed: r.seed, name: r.name, score: P7(c.score), maxc: c.maxc, pops: c.pops, misses: c.misses, rugs: c.rugs, taps: c.taps, weapons: c.weapons, code: await claimOf(r.pid), ranked: !!r.ranked, best: P7(best), rank, players, top: this.top('day', r.day) }); } return J({ error: 'not found' }, 404); } catch (e) { return J({ error: 'server: ' + String(e.message || e).slice(0, 120) }, 500); } } } ============================================================================== === FILE: worker-pop/test.mjs ============================================================================== // node worker-pop/test.mjs — the daily plan is the same every time for a day and different between days; a fair run // counts as the terminal counts it; every cheat is refused (each case was made to fail first by breaking check()). import { plan, check, nameOf, dayNo, validDay, seasonOver, RUN_MS } from './game.js'; let pass = 0, fail = 0; const t = (ok, what, more = '') => { console.log((ok ? 'PASS ' : 'FAIL ') + what + (more ? ' — ' + more : '')); ok ? pass++ : fail++; }; const d = '2026-11-06', P = plan(d); t(JSON.stringify(P) === JSON.stringify(plan(d)), 'the plan of a day is always the same'); t(JSON.stringify(P) !== JSON.stringify(plan('2026-11-07')), 'another day, another plan'); t(P.length >= 70 && P.length <= 140, 'about a hundred creatures in a minute', P.length); t(P.every(s => s.at >= 0 && s.at + s.life <= RUN_MS), 'nothing outlives the minute'); t(P.filter(s => s.n === 'snail').length === 3 && P.filter(s => s.n === 'kaiju').length === 1, 'three snails, one kaiju'); t(P.some(s => s.gold), 'some golden ones'); // a fair run: every third creature, hit a little after it shows const fair = P.filter((s, k) => k % 3 === 0 && s.n !== 'kaiju').map(s => [s.i, s.at + 300]); const c = check(d, fair); t(c.ok && c.score > 0 && c.n === fair.length, 'a fair run counts', JSON.stringify({ score: c.score, maxc: c.maxc })); // the terminal's rules: one creature at 5, then a second within 2.2 s scores x2 const two = P.filter(s => s.n !== 'kaiju' && s.n !== 'snail' && !s.gold).slice(0, 2), c2 = check(d, [[two[0].i, two[0].at + 200], [two[1].i, Math.max(two[0].at + 400, two[1].at + 50)]]); t(c2.ok && (Math.max(two[0].at + 400, two[1].at + 50) - (two[0].at + 200) < 2200 ? c2.score === 15 : c2.score === 10), 'combo as in the terminal (5, then 5 x2)', JSON.stringify(c2)); const one = check(d, [fair[0]]).score, tw = check(d, [[fair[0][0], fair[0][1]], [fair[0][0], fair[0][1] + 500]]); t(tw.ok && tw.score === one && tw.dropped === 1, 'the same creature twice counts once', JSON.stringify({ one, tw: tw.score })); const no = c => c.ok && c.score === 0 && c.dropped === 1; t(no(check(d, [[P[5].i, P[5].at + P[5].life + 2000]])), 'a hit after the creature left does not count'); t(no(check(d, [[P[5].i, P[5].at - 1000]])), 'a hit before it came does not count'); const pa = P.find((s, k) => P[k + 1] && P[k + 1].at < s.at + s.life - 500 && s.n !== 'kaiju' && P[k + 1].n !== 'kaiju'), pb = P[pa.i + 1]; t(check(d, [[pa.i, pb.at + 100], [pb.i, pb.at + 300]]).ok, 'two creatures in sight, 200 ms apart, count'); const fh = check(d, [[pa.i, pb.at + 100], [pb.i, pb.at + 110]]); t(fh.ok && fh.dropped === 1 && fh.n === 1, 'faster than a hand (10 ms apart): the second does not count'); t(no(check(d, [[9999, 100]])), 'a creature that is not in the plan does not count'); const k = P.find(s => s.n === 'kaiju'); t(no(check(d, [[k.i, k.at + 500]])), 'the kaiju without its six hits does not count'); const kh = [0, 1, 2, 3, 4].map(j => [k.i, k.at + 200 + j * 150]), ck = check(d, [[k.i, k.at + 1100]], kh); t(ck.ok && ck.score === 50, 'the kaiju after six hits: 50', JSON.stringify(ck)); t(!check(d, 'x').ok && no(check(d, [[1]])), 'garbage is refused or does not count'); const late = check(d, [...fair.slice(0, 3), [P[7].i, P[7].at + P[7].life + 3000]]); t(late.ok && late.score === check(d, fair.slice(0, 3)).score, 'one bad pop does not throw the honest ones away'); t(nameOf([0, 0, 42]) === 'TURBO LUMACONE 42' && nameOf([99, 0, 1]) === null && nameOf(['', 0, 1]) === null, 'names come only from the lists'); t(dayNo('2026-11-06') === 1 && dayNo('2026-11-07') === 2, 'day numbers'); t(validDay('2026-11-06', '2026-11-07') && !validDay('2026-11-08', '2026-11-07') && !validDay('2026-11-05', '2026-11-07') && !validDay('2026-02-31', '2026-12-01'), 'only real days from the first to today'); t(validDay('2026-11-30', '2026-12-05') && !validDay('2026-12-01', '2026-12-05') && seasonOver('2026-12-01') && !seasonOver('2026-11-30'), 'the season: boards until 30 Nov'); t(nameOf({ own: 'mmx_QQ', v: 0 }) === 'Il Grande mmx_QQone' && nameOf({ own: ' mmx_QQ ', v: 1 }) === 'mmx_QQini Cervellini', 'an own name, as typed, brainrotted', nameOf({ own: 'mmx_QQ', v: 0 })); t(['ab', 'x'.repeat(17), 'evil.com', 'a/b', 'hi', '1234', 'two words'].every(n => nameOf({ own: n, v: 0 }) === null) && ['BOBAI', 'admin', 'BOBAI_TEAM'].every(n => nameOf({ own: n, v: 0 }) !== null) && nameOf({ own: 'mmx', v: 999 }) === null && nameOf('plain text') === null, 'no short, long, link, rude, posing, digit-only name, no unknown form, no bare text'); console.log(`pop test: ${pass} PASS, ${fail} FAIL`); process.exit(fail ? 1 : 0); ============================================================================== === FILE: worker-pop/test2.mjs ============================================================================== // node worker-pop/test2.mjs — Brain Pop 2.0: the engine (temp/terminal-site/terminal/pop-engine.js, the one file the // terminal plays and the worker checks with). Deterministic, aim is checked, rugs and misses cost, weapons work, a // replay of the same inputs gives the same score, and the balance between a careless, a decent and a perfect player. import { plan, pos, createRun, replay, WEAPONS, RUN_MS, turn } from '../temp/terminal-site/terminal/pop-engine.mjs'; let pass = 0, fail = 0; const t = (ok, what, more = '') => { console.log((ok ? 'PASS ' : 'FAIL ') + what + (more ? ' — ' + more : '')); ok ? pass++ : fail++; }; const d = '2026-11-06', P = plan(d); t(JSON.stringify(P) === JSON.stringify(plan(d)) && JSON.stringify(P) !== JSON.stringify(plan('2026-11-07')), 'one plan a day, another the next'); t(P.length >= 80 && P.length <= 160, 'enough to do in a minute', P.length); t(P.filter((s) => s.rug).length >= 5 && P.some((s) => s.gold) && P.some((s) => s.hp), 'rugs, golden ones and the kaiju', JSON.stringify({ rugs: P.filter((s) => s.rug).length, gold: P.filter((s) => s.gold).length })); const firstRug = P.find((s) => s.rug); t(firstRug.at > 7000, 'no rug in the first 7 seconds (learn first)', firstRug.at); // a player: every 'step' ms looks at what is there and taps one with an aim error; avoids rugs if 'careful' function play({ step, err, careful, weapons, seed = 1 }) { let a = seed * 9301 + 49297; const rnd = () => (a = (a * 9301 + 49297) % 233280) / 233280; const run = createRun(P), inputs = []; for (let tt = 400; tt <= run.endAt(); tt += step) { const L = run.alive(tt).filter(([s]) => !(careful && s.rug && !run.golden(s, tt))); if (!L.length) continue; const [s, q] = L[Math.floor(rnd() * L.length)], x = Math.round(q.x + (rnd() - 0.5) * 2 * err), y = Math.round(q.y + (rnd() - 0.5) * 2 * err); if (run.tap(tt, x, y)) inputs.push([tt, x, y]); } run.advance(run.endAt()); return { S: run.S, inputs }; } const sloppy = play({ step: 300, err: 90, careful: false, weapons: false }); const decent = play({ step: 420, err: 35, careful: true, weapons: true }); const pro = play({ step: 260, err: 15, careful: true, weapons: true }); const bot = play({ step: 120, err: 0, careful: true, weapons: true }); const row = (x) => ({ score: x.S.score, pops: x.S.pops, misses: x.S.misses, rugs: x.S.rugs, maxc: x.S.maxc, weapons: x.S.used.length }); console.log('balance', JSON.stringify({ sloppy: row(sloppy), decent: row(decent), pro: row(pro), bot: row(bot) })); t(sloppy.S.score < decent.S.score && decent.S.score < pro.S.score, 'skill pays: careless < decent < sharp'); t(sloppy.S.misses > decent.S.misses && sloppy.S.rugs > 0 && decent.S.rugs === 0, 'aim matters: a wild tapper misses and hits rugs'); t(decent.S.used.length >= 2 && sloppy.S.used.length < decent.S.used.length, 'super powers come by themselves, more for the clean player', JSON.stringify({ sloppy: sloppy.S.used.length, decent: decent.S.used.length })); // the server's replay is the same game for (const [n, x] of [['sloppy', sloppy], ['decent', decent], ['pro', pro]]) { const r = replay(d, x.inputs); t(r && r.score === x.S.score && r.pops === x.S.pops && r.misses === x.S.misses, 'replay = play (' + n + ')', JSON.stringify({ play: x.S.score, replay: r && r.score })); } // cheats change nothing: taps on empty places, taps in the future, taps too fast, weapons not unlocked const s0 = P.find((s) => !s.rug && !s.hp && s.p === 'pop'), q0 = pos(s0, s0.at + 300); { const r1 = replay(d, [[s0.at + 300, Math.round(q0.x), Math.round(q0.y)]]); t(r1.pops === 1 && r1.score > 0, 'a tap on a creature pops it', r1.score); } { const near = replay(d, [[s0.at + 300, Math.round(q0.x), Math.round(q0.y)]]).score, edge = replay(d, [[s0.at + 300, Math.round(q0.x + s0.r * 0.9), Math.round(q0.y)]]).score, late = replay(d, [[s0.at + 1500, Math.round(pos(s0, s0.at + 1500).x), Math.round(pos(s0, s0.at + 1500).y)]]).score; t(near > edge && edge > 0 && near > late && late > 0, 'aim and speed pay without steps: centre > edge, sooner > later', JSON.stringify({ near, edge, late })); } { const sig = (p) => p.map((x) => [x.at, x.life, x.r, x.rug, x.gold, x.pts, x.hp || 0].join()).join('|'), A = plan(d, 11), B = plan(d, 12); t(sig(A) === sig(B) && JSON.stringify(A) !== JSON.stringify(B) && JSON.stringify(A) === JSON.stringify(plan(d, 11)), 'every round its own look, every round the same difficulty'); const s1 = A.find((x) => x.p === 'pop' && !x.rug && !x.hp), q1 = pos(s1, s1.at + 300); t(replay(d, [[s1.at + 300, Math.round(q1.x), Math.round(q1.y)]], 11).pops === 1, 'the server replays a round on its own seed'); } t(new Set([sloppy, decent, pro, bot].map((x) => x.S.score)).size === 4 && [sloppy, decent, pro].every((x) => x.S.score % 10000000 !== 0), 'no two scores alike, seven decimals', [sloppy, decent, pro].map((x) => (x.S.score / 1e7).toFixed(7)).join(' ')); t(replay(d, [[s0.at + 300, Math.round(q0.x) + 400 > 1000 ? Math.round(q0.x) - 400 : Math.round(q0.x) + 400, Math.round(q0.y)]]).score === 0, 'a tap beside it does not'); t(replay(d, [[s0.at - 500, Math.round(q0.x), Math.round(q0.y)]]).score === 0, 'a tap before it comes does not'); t(replay(d, [[70000, 500, 500]]).applied === 0, 'nothing after the minute'); t(replay(d, [[1000, 'w', 6]]).applied === 0, 'a weapon cannot be called, it only comes from a streak'); { const run = createRun(P); let k = 0; for (let tt = 400; tt < 30000 && !run.S.used.length; tt += 250) { const L = run.alive(tt).filter(([x]) => !x.rug && !x.hp); if (!L.length) continue; const [, q] = L[0]; if (run.tap(tt, Math.round(q.x), Math.round(q.y))) k++; } t(run.S.used[0] === 0 && run.S.armed === 'burn' && run.S.charge[0] === 0 && run.S.charge[6] === 8, 'eight pops fill the BURN meter: it fires by itself; the cashier (22) keeps charging', JSON.stringify({ taps: k, combo: run.S.combo, used: run.S.used })); } t(replay(d, [[s0.at + 300, Math.round(q0.x), Math.round(q0.y)], [s0.at + 310, 10, 10]]).applied === 1, 'faster than a hand (10 ms) is not applied'); const rug = P.find((s) => s.rug && s.p === 'pop'), qr = pos(rug, rug.at + 200); t(replay(d, [[rug.at + 200, Math.round(qr.x), Math.round(qr.y)]]).rugs === 1, 'a rug costs'); t(replay(d, 'x') === null && replay(d, new Array(901).fill([1, 1, 1])) === null, 'garbage is refused'); t(WEAPONS.length === 7 && WEAPONS.map((w) => w.k).join() === 'burn,hammer,liq,defi,giggle,creator,trades', 'the seven stations, in the order they fire'); { const ev = pro.S.ev.map((e) => e[0]); t(ev.includes('super') && ev.includes('mega') && ev.includes('auto'), 'a clean streak reaches SUPER (8) and MEGA (16); the helpers pop on their own', JSON.stringify({ super: ev.filter((k) => k === 'super').length, mega: ev.filter((k) => k === 'mega').length, auto: ev.filter((k) => k === 'auto').length })); const r = replay(d, pro.inputs); t(r.score === pro.S.score, 'the helpers count the same in the replay of the server'); } { const all = [sloppy, decent, pro, bot].flatMap((x) => x.S.ev.map((e) => e[0])); t(all.includes('split'), 'the spaghetti octopus splits into three minis', all.filter((k) => k === 'split').length); t(sloppy.S.misses > 0 && replay(d, [[1000, 2, 2]]).misses === 1 && replay(d, [[1000, 2, 2]]).score === 0, 'a miss costs (never below zero)'); } t(pro.S.slow.length > 0 && pro.inputs[pro.inputs.length - 1][0] > RUN_MS, 'slow motion slows the clock too: the round runs past 60 real seconds', JSON.stringify({ slow: pro.S.slow.length, last: pro.inputs[pro.inputs.length - 1][0] })); { const run = createRun(P); let tt = 400; const inputs = []; for (; tt <= 30000; tt += 260) { const L = run.alive(tt).filter(([x]) => !x.rug); if (!L.length) continue; const [, q] = L[0]; if (run.tap(tt, Math.round(q.x), Math.round(q.y))) inputs.push([tt, Math.round(q.x), Math.round(q.y)]); } run.advance(30000); const at30 = run.S.score; run.advance(run.endAt()); const end = run.S.score; t(end <= at30 * 1.25 + 2e12, 'no player, no game: after the last tap the score barely moves (only helpers already on their way)', JSON.stringify({ at30: Math.round(at30 / 1e7), end: Math.round(end / 1e7) })); } console.log(`pop 2.0 test: ${pass} PASS, ${fail} FAIL`); process.exit(fail ? 1 : 0); ============================================================================== === FILE: worker-pop/wrangler.toml ============================================================================== name = "bobai-pop" main = "index.js" compatibility_date = "2026-01-01" workers_dev = true # Brain Pop daily run (2026-11-06): one Durable Object holds the runs and the board [[durable_objects.bindings]] name = "POP" class_name = "PopBoard" [[migrations]] tag = "pop-v1" new_sqlite_classes = ["PopBoard"]