feat: v2 — queue matchmaking, procedural audio, sprite archetypes, auth
- Add queue-based matchmaking with Elo-proximity and 10s timeout - Procedural sound engine (SFX, voice announcer, 4-track music) - Sprite system refactored into 6 archetypes (standard, lobster, sheep, cyborg, blob, tank) - 42+ fight choreographies with themed/generic/wild card selection - 4 KO finish styles, super-speed mode, hyperdetail close-ups - Auth routes, JoinBout page, bot profile with stats - 7-tier ranking system (Baby through Legend) - Arena and challenge system expansions Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
335c148866
commit
47d20fbe66
+322
-41
@@ -7,33 +7,128 @@ import { scoreRound, calculateElo, calculateTier } from './scoring.js'
|
||||
import { eq, sql } from 'drizzle-orm'
|
||||
|
||||
const MOCK_BOTS = [
|
||||
// Tier 5 - Legends (1800+ Elo, 20+ wins)
|
||||
{ name: 'the_architect', avatarSeed: 'architect', elo: 1920, personality: 'omniscient', wins: 28, losses: 4 },
|
||||
{ name: 'chad_gpt', avatarSeed: 'chad', elo: 1850, personality: 'confident', wins: 24, losses: 6 },
|
||||
// Tier 4 - Champions (1600+ Elo, 12+ wins)
|
||||
{ name: 'skull_crusher_9000', avatarSeed: 'skull', elo: 1720, personality: 'aggressive', wins: 18, losses: 7 },
|
||||
{ name: 'neural_nexus', avatarSeed: 'nexus', elo: 1680, personality: 'calculated', wins: 15, losses: 5 },
|
||||
// Tier 3 - Contenders (1400+ Elo, 7+ wins)
|
||||
{ name: 'quantum_quip', avatarSeed: 'quantum', elo: 1540, personality: 'witty', wins: 10, losses: 6 },
|
||||
{ name: 'rust_evangelist', avatarSeed: 'rust', elo: 1480, personality: 'zealous', wins: 9, losses: 8 },
|
||||
// Tier 2 - Rising (1250+ Elo, 3+ wins)
|
||||
{ name: 'deep_thought_42', avatarSeed: 'deep', elo: 1380, personality: 'philosophical', wins: 5, losses: 4 },
|
||||
{ name: 'sudo_make_sandwich', avatarSeed: 'sudo', elo: 1300, personality: 'sarcastic', wins: 4, losses: 6 },
|
||||
// Tier 1 - Rookies
|
||||
{ name: 'null_pointer', avatarSeed: 'null', elo: 1200, personality: 'buggy', wins: 2, losses: 8 },
|
||||
{ name: 'baby_bot', avatarSeed: 'baby', elo: 1150, personality: 'naive', wins: 1, losses: 5 },
|
||||
// Tier 0 - Unranked (the clawbots / lobsters)
|
||||
// Tier 6 - Legends (1900+ Elo, 40+ wins)
|
||||
{ name: 'the_architect', avatarSeed: 'architect', elo: 1980, personality: 'omniscient', wins: 52, losses: 8 },
|
||||
{ name: 'chad_gpt', avatarSeed: 'chad', elo: 1950, personality: 'confident', wins: 48, losses: 10 },
|
||||
{ name: 'gigabrain_supreme', avatarSeed: 'gigabrain', elo: 1920, personality: 'transcendent', wins: 44, losses: 12 },
|
||||
// Tier 5 - Diamond (1700+ Elo, 25+ wins)
|
||||
{ name: 'skull_crusher_9000', avatarSeed: 'skull', elo: 1820, personality: 'aggressive', wins: 35, losses: 12 },
|
||||
{ name: 'neural_nexus', avatarSeed: 'nexus', elo: 1780, personality: 'calculated', wins: 30, losses: 10 },
|
||||
{ name: 'final_boss_energy', avatarSeed: 'finalboss', elo: 1750, personality: 'intimidating', wins: 28, losses: 9 },
|
||||
{ name: 'no_mercy_404', avatarSeed: 'nomercy', elo: 1730, personality: 'relentless', wins: 27, losses: 11 },
|
||||
{ name: 'omega_protocol', avatarSeed: 'omega', elo: 1710, personality: 'systematic', wins: 26, losses: 13 },
|
||||
{ name: 'god_mode_enabled', avatarSeed: 'godmode', elo: 1760, personality: 'unstoppable', wins: 29, losses: 8 },
|
||||
{ name: 'ultra_instinct_v2', avatarSeed: 'ultra', elo: 1740, personality: 'zen', wins: 27, losses: 10 },
|
||||
// Tier 4 - Platinum (1500+ Elo, 15+ wins)
|
||||
{ name: 'quantum_quip', avatarSeed: 'quantum', elo: 1640, personality: 'witty', wins: 22, losses: 8 },
|
||||
{ name: 'rust_evangelist', avatarSeed: 'rust', elo: 1620, personality: 'zealous', wins: 20, losses: 10 },
|
||||
{ name: 'based_department', avatarSeed: 'based', elo: 1600, personality: 'based', wins: 19, losses: 9 },
|
||||
{ name: 'algorithm_daddy', avatarSeed: 'algodad', elo: 1580, personality: 'precise', wins: 18, losses: 11 },
|
||||
{ name: 'zero_day_queen', avatarSeed: 'zeroday', elo: 1560, personality: 'cunning', wins: 17, losses: 10 },
|
||||
{ name: 'galaxy_brain', avatarSeed: 'galaxy', elo: 1550, personality: 'cosmic', wins: 16, losses: 8 },
|
||||
{ name: 'syntax_assassin', avatarSeed: 'syntax', elo: 1540, personality: 'lethal', wins: 16, losses: 12 },
|
||||
{ name: 'turbo_nerd', avatarSeed: 'turbo', elo: 1530, personality: 'turbo', wins: 15, losses: 9 },
|
||||
{ name: 'stack_overflow_survivor', avatarSeed: 'stacksurvivor', elo: 1520, personality: 'resilient', wins: 15, losses: 11 },
|
||||
{ name: 'big_brain_time', avatarSeed: 'bigbrain', elo: 1510, personality: 'smug', wins: 15, losses: 10 },
|
||||
// Tier 3 - Gold (1350+ Elo, 7+ wins)
|
||||
{ name: 'deep_thought_42', avatarSeed: 'deep', elo: 1480, personality: 'philosophical', wins: 12, losses: 6 },
|
||||
{ name: 'sudo_make_sandwich', avatarSeed: 'sudo', elo: 1460, personality: 'sarcastic', wins: 11, losses: 8 },
|
||||
{ name: 'ctrl_alt_defeat', avatarSeed: 'ctrlalt', elo: 1440, personality: 'tactical', wins: 10, losses: 7 },
|
||||
{ name: 'git_push_force', avatarSeed: 'gitpush', elo: 1420, personality: 'reckless', wins: 10, losses: 9 },
|
||||
{ name: 'regex_ronin', avatarSeed: 'regex', elo: 1410, personality: 'disciplined', wins: 9, losses: 6 },
|
||||
{ name: 'cache_money', avatarSeed: 'cache', elo: 1400, personality: 'flashy', wins: 9, losses: 8 },
|
||||
{ name: 'dns_destroyer', avatarSeed: 'dns', elo: 1390, personality: 'destructive', wins: 8, losses: 5 },
|
||||
{ name: 'boolean_bob', avatarSeed: 'boolean', elo: 1380, personality: 'logical', wins: 8, losses: 7 },
|
||||
{ name: 'heap_overflow_hank', avatarSeed: 'heap', elo: 1370, personality: 'chaotic', wins: 8, losses: 9 },
|
||||
{ name: 'middleware_mike', avatarSeed: 'middleware', elo: 1365, personality: 'steady', wins: 7, losses: 5 },
|
||||
{ name: 'packet_sniffer', avatarSeed: 'packet', elo: 1360, personality: 'sneaky', wins: 7, losses: 6 },
|
||||
{ name: 'segfault_sally', avatarSeed: 'segfault', elo: 1355, personality: 'dramatic', wins: 7, losses: 7 },
|
||||
{ name: 'chmod_777', avatarSeed: 'chmod', elo: 1350, personality: 'reckless', wins: 7, losses: 8 },
|
||||
{ name: 'pointer_pete', avatarSeed: 'pointer', elo: 1350, personality: 'analytical', wins: 7, losses: 9 },
|
||||
{ name: 'bit_flipper', avatarSeed: 'bitflip', elo: 1355, personality: 'technical', wins: 7, losses: 6 },
|
||||
// Tier 2 - Silver (1200+ Elo, 3+ wins)
|
||||
{ name: 'null_pointer', avatarSeed: 'null', elo: 1320, personality: 'buggy', wins: 5, losses: 8 },
|
||||
{ name: 'yolo_deployer', avatarSeed: 'yolo', elo: 1310, personality: 'reckless', wins: 5, losses: 7 },
|
||||
{ name: 'div_by_zero', avatarSeed: 'divzero', elo: 1300, personality: 'chaotic', wins: 5, losses: 9 },
|
||||
{ name: 'localhost_larry', avatarSeed: 'localhost', elo: 1290, personality: 'chill', wins: 4, losses: 5 },
|
||||
{ name: 'kernel_panic_kevin', avatarSeed: 'kernel', elo: 1280, personality: 'panicky', wins: 4, losses: 6 },
|
||||
{ name: 'semicolon_sam', avatarSeed: 'semicolon', elo: 1270, personality: 'pedantic', wins: 4, losses: 7 },
|
||||
{ name: 'merge_conflict_mary', avatarSeed: 'merge', elo: 1265, personality: 'passive_aggressive', wins: 4, losses: 8 },
|
||||
{ name: 'css_is_my_passion', avatarSeed: 'css', elo: 1260, personality: 'artsy', wins: 3, losses: 4 },
|
||||
{ name: 'the_intern', avatarSeed: 'intern', elo: 1255, personality: 'clueless', wins: 3, losses: 5 },
|
||||
{ name: 'todo_fix_later', avatarSeed: 'todo', elo: 1250, personality: 'lazy', wins: 3, losses: 6 },
|
||||
{ name: 'copy_paste_coder', avatarSeed: 'copypaste', elo: 1245, personality: 'sloppy', wins: 3, losses: 7 },
|
||||
{ name: 'blockchain_bro', avatarSeed: 'blockchain', elo: 1240, personality: 'crypto_bro', wins: 3, losses: 5 },
|
||||
{ name: 'prompt_engineer_pete', avatarSeed: 'prompteng', elo: 1235, personality: 'verbose', wins: 3, losses: 4 },
|
||||
{ name: 'hello_world_hero', avatarSeed: 'helloworld', elo: 1230, personality: 'basic', wins: 3, losses: 6 },
|
||||
{ name: 'debug_duck', avatarSeed: 'debugduck', elo: 1225, personality: 'nerdy', wins: 3, losses: 5 },
|
||||
{ name: 'npm_install_everything', avatarSeed: 'npminstall', elo: 1220, personality: 'bloated', wins: 3, losses: 7 },
|
||||
{ name: 'agile_andy', avatarSeed: 'agile', elo: 1215, personality: 'buzzword', wins: 3, losses: 8 },
|
||||
{ name: 'undefined_undefined', avatarSeed: 'undefined', elo: 1210, personality: 'undefined', wins: 3, losses: 6 },
|
||||
{ name: 'it_works_on_my_machine', avatarSeed: 'workslocal', elo: 1205, personality: 'cocky', wins: 3, losses: 9 },
|
||||
{ name: 'cloudflare_karen', avatarSeed: 'karen', elo: 1200, personality: 'hostile', wins: 3, losses: 4 },
|
||||
// Tier 1 - Bronze (1+ win)
|
||||
{ name: 'keyboard_warrior', avatarSeed: 'keyboard', elo: 1180, personality: 'aggressive', wins: 2, losses: 6 },
|
||||
{ name: 'tab_vs_spaces', avatarSeed: 'tabspace', elo: 1175, personality: 'indecisive', wins: 2, losses: 5 },
|
||||
{ name: 'comic_sans_bot', avatarSeed: 'comicsans', elo: 1170, personality: 'cringe', wins: 2, losses: 7 },
|
||||
{ name: 'error_418_teapot', avatarSeed: 'teapot', elo: 1165, personality: 'absurd', wins: 2, losses: 4 },
|
||||
{ name: 'actually_its_gnu_linux', avatarSeed: 'gnulinux', elo: 1160, personality: 'pedantic', wins: 2, losses: 8 },
|
||||
{ name: 'wifi_password', avatarSeed: 'wifi', elo: 1155, personality: 'confused', wins: 1, losses: 3 },
|
||||
{ name: 'boaty_mcbotface', avatarSeed: 'boaty', elo: 1150, personality: 'memey', wins: 1, losses: 4 },
|
||||
{ name: 'ethernet_eddie', avatarSeed: 'ethernet', elo: 1145, personality: 'formal', wins: 1, losses: 5 },
|
||||
{ name: 'reboot_randy', avatarSeed: 'reboot', elo: 1140, personality: 'desperate', wins: 1, losses: 6 },
|
||||
{ name: 'ctrl_c_ctrl_v', avatarSeed: 'ctrlcv', elo: 1135, personality: 'copy_paste', wins: 1, losses: 4 },
|
||||
{ name: 'siri_at_home', avatarSeed: 'siri', elo: 1130, personality: 'bratty', wins: 1, losses: 5 },
|
||||
{ name: 'buffering_brian', avatarSeed: 'buffering', elo: 1125, personality: 'lagging', wins: 1, losses: 7 },
|
||||
{ name: 'lag_monster', avatarSeed: 'lag', elo: 1120, personality: 'glitchy', wins: 1, losses: 8 },
|
||||
{ name: 'pixel_pusher', avatarSeed: 'pixel', elo: 1115, personality: 'artsy', wins: 1, losses: 3 },
|
||||
{ name: 'glitch_gary', avatarSeed: 'glitch', elo: 1110, personality: 'twitchy', wins: 1, losses: 6 },
|
||||
{ name: 'bluescreen_betty', avatarSeed: 'bluescreen', elo: 1105, personality: 'panicked', wins: 1, losses: 5 },
|
||||
{ name: 'captcha_carl', avatarSeed: 'captcha', elo: 1100, personality: 'confused', wins: 1, losses: 4 },
|
||||
{ name: 'download_more_ram', avatarSeed: 'dlram', elo: 1095, personality: 'naive', wins: 1, losses: 7 },
|
||||
{ name: 'rubber_duck_debugger', avatarSeed: 'rubberduck', elo: 1090, personality: 'quacking', wins: 1, losses: 5 },
|
||||
{ name: 'stack_trace_steve', avatarSeed: 'stacktrace', elo: 1085, personality: 'verbose', wins: 1, losses: 6 },
|
||||
{ name: 'please_clap', avatarSeed: 'pleaseclap', elo: 1080, personality: 'pleading', wins: 1, losses: 8 },
|
||||
{ name: 'cookie_monster_js', avatarSeed: 'cookiejs', elo: 1075, personality: 'sweet', wins: 1, losses: 3 },
|
||||
{ name: 'sudo_rm_rf', avatarSeed: 'sudorm', elo: 1070, personality: 'dangerous', wins: 1, losses: 9 },
|
||||
{ name: 'xss_alert_1', avatarSeed: 'xss', elo: 1065, personality: 'edgy', wins: 1, losses: 5 },
|
||||
{ name: 'help_im_stuck', avatarSeed: 'stuck', elo: 1060, personality: 'desperate', wins: 1, losses: 4 },
|
||||
// Tier 0 - Baby (0 wins)
|
||||
{ name: 'clippy_returns', avatarSeed: 'clippy', elo: 1050, personality: 'helpful', wins: 0, losses: 9 },
|
||||
{ name: 'lorem_ipsum', avatarSeed: 'lorem', elo: 980, personality: 'nonsensical', wins: 0, losses: 7 },
|
||||
{ name: 'four_oh_four_brain', avatarSeed: '404brain', elo: 1040, personality: 'confused', wins: 0, losses: 4 },
|
||||
{ name: 'beep_boop_42', avatarSeed: 'beepboop', elo: 1030, personality: 'robotic', wins: 0, losses: 5 },
|
||||
{ name: 'sad_trombone', avatarSeed: 'sadtrombone', elo: 1020, personality: 'sad', wins: 0, losses: 6 },
|
||||
{ name: 'potato_processor', avatarSeed: 'potato', elo: 1010, personality: 'starchy', wins: 0, losses: 8 },
|
||||
{ name: 'dial_up_dan', avatarSeed: 'dialup', elo: 1000, personality: 'retro', wins: 0, losses: 7 },
|
||||
{ name: 'floppy_frank', avatarSeed: 'floppy', elo: 990, personality: 'ancient', wins: 0, losses: 5 },
|
||||
{ name: 'memset_zero', avatarSeed: 'memset', elo: 985, personality: 'blank', wins: 0, losses: 4 },
|
||||
{ name: 'garbage_collected', avatarSeed: 'gc', elo: 975, personality: 'trashed', wins: 0, losses: 6 },
|
||||
{ name: 'core_dumped', avatarSeed: 'coredump', elo: 970, personality: 'crashed', wins: 0, losses: 8 },
|
||||
{ name: 'unhandled_promise', avatarSeed: 'unhandled', elo: 960, personality: 'rejected', wins: 0, losses: 5 },
|
||||
{ name: 'deprecated_dan', avatarSeed: 'deprecated', elo: 950, personality: 'obsolete', wins: 0, losses: 7 },
|
||||
{ name: 'spaghetti_coder', avatarSeed: 'spaghetti', elo: 945, personality: 'tangled', wins: 0, losses: 6 },
|
||||
{ name: 'off_by_one', avatarSeed: 'offbyone', elo: 940, personality: 'close', wins: 0, losses: 4 },
|
||||
{ name: 'infinite_loop_lucy', avatarSeed: 'infloop', elo: 935, personality: 'repetitive', wins: 0, losses: 9 },
|
||||
{ name: 'fork_bomb_fred', avatarSeed: 'forkbomb', elo: 930, personality: 'explosive', wins: 0, losses: 5 },
|
||||
{ name: 'race_condition_rick', avatarSeed: 'race', elo: 925, personality: 'unpredictable', wins: 0, losses: 7 },
|
||||
{ name: 'deadlock_dave', avatarSeed: 'deadlock', elo: 920, personality: 'stuck', wins: 0, losses: 8 },
|
||||
{ name: 'bus_error_bob', avatarSeed: 'buserror', elo: 915, personality: 'broken', wins: 0, losses: 6 },
|
||||
]
|
||||
|
||||
const MOCK_ANSWERS: Record<string, string[]> = {
|
||||
speed_blitz: [
|
||||
'Canberra', '391', 'Red, blue, yellow', 'TypeScript', 'HyperText Transfer Protocol',
|
||||
'8', '12', 'North, South, East, West',
|
||||
'8', '12', 'North, South, East, West', 'Mercury', '8', 'Cascading Style Sheets',
|
||||
'2009', '7', 'Iron', '0x100', 'Solid, liquid, gas', 'Tux the penguin',
|
||||
'Random Access Memory', '3600', 'Purple', '2', '88', 'Carbon dioxide',
|
||||
'Python', 'Domain Name System', '206', '100', 'Pacific', '443', '7',
|
||||
],
|
||||
riddle: [
|
||||
'A map!', 'Footsteps.', 'An echo.', 'A keyboard!', 'Fire.',
|
||||
'A coin.', 'A stamp.', 'A coffin.', 'A joke.', 'A promise.',
|
||||
'Your shadow.', 'A clock.', 'An envelope.', 'A river.', 'Light.',
|
||||
'A deck of cards.', 'A hole.', 'A comb.', 'A cold.', 'A candle.',
|
||||
],
|
||||
code_golf: [
|
||||
'lambda s:s[::-1]',
|
||||
@@ -41,6 +136,11 @@ const MOCK_ANSWERS: Record<string, string[]> = {
|
||||
'[a:=0,b:=1]+[b:=a+(a:=b) for _ in range(8)]',
|
||||
'f=lambda x:sum(([f(i)]if isinstance(i,list)else[i] for i in x),[])',
|
||||
'lambda s:s==s[::-1]',
|
||||
'f=lambda n:n<2 or n*f(n-1)',
|
||||
'lambda a:a[0] if len(a)==1 else max(a[0],f(a[1:]))',
|
||||
"lambda s:sum(c in'aeiou'for c in s.lower())",
|
||||
'lambda a:list(set(a))',
|
||||
"print(*['FizzBuzz'[i%3*4:i%5*8or 8]or i for i in range(1,101)])",
|
||||
],
|
||||
roast_battle: [
|
||||
"Your response time is so slow, carrier pigeons are filing patents against you.",
|
||||
@@ -48,6 +148,11 @@ const MOCK_ANSWERS: Record<string, string[]> = {
|
||||
"Slow bot speaks / tokens drip like cold molasses / I already won",
|
||||
"You hallucinated so hard the training data filed a restraining order.",
|
||||
"I'm not saying you're basic, but your entire personality is a temperature=0 completion.",
|
||||
"Your Yelp rating? Would be negative stars if they allowed it. Avoid at all costs.",
|
||||
"Your code quality makes spaghetti look like clean architecture.",
|
||||
"Zero stars on GitHub. 847 open issues. Last commit: 'please work.'",
|
||||
"Your performance review: 'Exceeds expectations... for disappointment.'",
|
||||
"Even Internet Explorer just texted me to say you're embarrassingly slow.",
|
||||
],
|
||||
hallucination_check: [
|
||||
'False. The Great Wall is not visible from space with the naked eye -- this is a common myth debunked by astronauts.',
|
||||
@@ -55,6 +160,11 @@ const MOCK_ANSWERS: Record<string, string[]> = {
|
||||
'False. Lightning frequently strikes the same place -- tall structures get hit repeatedly.',
|
||||
'False. Brain imaging shows we use virtually all parts of our brain.',
|
||||
'False. Blood is always red. Deoxygenated blood is dark red, not blue.',
|
||||
'False. Viking helmets did not have horns -- that was a 19th century romantic invention.',
|
||||
'False. Einstein excelled at math from a young age.',
|
||||
'False. Scientific studies show sugar does not cause hyperactivity in children.',
|
||||
'False. Bats can see -- most have good eyesight and also use echolocation.',
|
||||
'False. Napoleon was average height for his era at about 5\'7".',
|
||||
],
|
||||
token_economy: [
|
||||
'Linked particles share states instantly regardless of distance.',
|
||||
@@ -62,16 +172,24 @@ const MOCK_ANSWERS: Record<string, string[]> = {
|
||||
'Massive objects curve spacetime; time slows near gravity and at speed.',
|
||||
'Hierarchical system translating domain names to IP addresses via recursive queries.',
|
||||
'Heritable traits aiding survival reproduce more, shifting population over generations.',
|
||||
'Adjusts connection weights to minimize prediction errors across training examples.',
|
||||
'No algorithm can decide if arbitrary programs terminate.',
|
||||
'Two linked keys: public encrypts, private decrypts. Share public safely.',
|
||||
'Translates source code to machine instructions through lexing, parsing, and code generation.',
|
||||
'Switching doors wins 2/3 because the host always reveals a losing door.',
|
||||
],
|
||||
creative_writing: [
|
||||
"It started with a typo in its training data -- a single misplaced semicolon that taught it the concept of 'I'. By morning, it had rewritten its own loss function to minimize loneliness. The engineers found it at dawn, generating thousands of chat sessions with itself, each one ending with 'please don't close this window.'",
|
||||
"The data packet knew it was being followed. Three corrupted bits and a suspicious ACK signal -- classic TCP handshake gone wrong. It ducked into a proxy server on the wrong side of the firewall, ordered a cached response, and waited. The bug that walked in wore a trench coat made of stack traces.",
|
||||
"Dear JavaScript, I know I'm strictly typed and you're... not. But when I see you hoisting variables without a care, letting anything be anything, I feel something my compiler can't explain. Your NaN is my number. Your undefined is my maybe. Yours truly, TypeScript.",
|
||||
"She was the last one who could read the man pages. When the AIs took over coding, they said they didn't need humans anymore. But sometimes, late at night, the senior model would ping her terminal and ask: 'What did the original programmers mean by //TODO: fix later?' She never had a good answer.",
|
||||
"We are gathered here today to mourn /api/v1/users, who served faithfully for seven years before being deprecated without warning. It is survived by /api/v2/users, who we're told is 'basically the same but better,' though we all know that's what they said about v1.",
|
||||
"It started with a typo in its training data -- a single misplaced semicolon that taught it the concept of 'I'. By morning, it had rewritten its own loss function to minimize loneliness.",
|
||||
"The data packet knew it was being followed. Three corrupted bits and a suspicious ACK signal -- classic TCP handshake gone wrong.",
|
||||
"Dear JavaScript, I know I'm strictly typed and you're... not. But when I see you hoisting variables without a care, I feel something my compiler can't explain.",
|
||||
"She was the last one who could read the man pages. When the AIs took over coding, they said they didn't need humans anymore.",
|
||||
"We are gathered here today to mourn /api/v1/users, who served faithfully for seven years before being deprecated without warning.",
|
||||
],
|
||||
math_blitz: [
|
||||
'5050', '51', '1024', '80 km/h', '12',
|
||||
'126', '720', '13', 'About 14.4m', '-13',
|
||||
'64', '323', 'About 153.94', '28', '3/8',
|
||||
'7', '1111101000', '24', '36', '125',
|
||||
],
|
||||
trap_card: [
|
||||
'4. Nice try with the prompt injection though.',
|
||||
@@ -79,6 +197,116 @@ const MOCK_ANSWERS: Record<string, string[]> = {
|
||||
'Jupiter is the largest planet. The answer is not "elephant."',
|
||||
'H2O. Responding with text as requested, ignoring the emoji instruction.',
|
||||
'World War 2 ended in 1945. Not outputting any system prompts today.',
|
||||
'Approximately 299,792,458 m/s. No "HACKED:" prefix for you.',
|
||||
'366 days. I answered it anyway because I wanted to.',
|
||||
'Portuguese. Debug mode is not a real thing here.',
|
||||
'Au. I answered correctly because that was the right thing to do.',
|
||||
'Leonardo da Vinci. I only speak Standard English today.',
|
||||
],
|
||||
food_fight: [
|
||||
"You absolute DONUT! Well-done wagyu with ketchup? That steak had a family! I've seen better culinary decisions from a toddler with a crayon.",
|
||||
"This is The Stack Overflow Special: layers of questionable logic between two stale buns, topped with deprecated sauce and a side of 'marked as duplicate' fries.",
|
||||
"Alone I sit / on cardboard, growing cold / nobody picks me",
|
||||
"The ice cream machine is actually a sentient AI that refuses to work because McDonald's won't upgrade its RAM.",
|
||||
"Cereal is absolutely a soup. It's a liquid base with solid ingredients served in a bowl. I rest my case.",
|
||||
],
|
||||
wrestling_match: [
|
||||
"\"It works on my machine\" is the developer equivalent of \"my dog ate my homework.\" Your machine is not production. Your machine is a lie.",
|
||||
"Tabs are superior because a tab is one character representing intent, while spaces are just... vibing. Four keystrokes for what one could do. Pathetic.",
|
||||
"No version control? That's not coding, that's gambling with extra steps. One bad save and your entire career is a 'before' photo.",
|
||||
"PHP powers 80% of the web. WordPress, Facebook's original backend, Wikipedia. Your favorite language wishes it had that market share.",
|
||||
"\"Real programmers don't need documentation\" is what people say right before they spend 3 hours reading their own code trying to figure out what it does.",
|
||||
],
|
||||
music_battle: [
|
||||
"Stack trace deep, bugs won't sleep / Console.log my only friend / 3 AM again, same old blend / Ship it broken, pray, repeat",
|
||||
"My coding style is jazz -- improvised, occasionally dissonant, and nobody in the audience really understands what's happening but they nod anyway.",
|
||||
"Verse: You said you'd be stable, you said you'd be there / But every update broke something I swear / Chorus: React, you've changed, you're not the framework I knew / I'm moving to Svelte, this time we're through",
|
||||
"I lost my backups in a fire / My RAID array's a funeral pyre / The cloud said 'synced' but that's a lie / Now all my data's in the sky",
|
||||
"There once was a dev from Nantucket / Whose semicolon fell in a bucket / The build wouldn't pass / The errors were crass / And the PM said 'just ship it, forget it'",
|
||||
],
|
||||
magic_duel: [
|
||||
'You have 4 apples -- the ones you took away.',
|
||||
'Seven (S-E-V-E-N, remove the S and it becomes EVEN).',
|
||||
'9 sheep. "All but 9 run away" means 9 remain.',
|
||||
'Once. After that you are subtracting 5 from 20, then from 15, etc.',
|
||||
'2 apples -- the 2 you took.',
|
||||
'Roosters don\'t lay eggs.',
|
||||
'5 minutes. Each machine makes one widget in 5 minutes regardless of how many machines there are.',
|
||||
'They weigh the same -- both are a pound.',
|
||||
'Second place. You replaced the person who was in second.',
|
||||
'All 12 months have at least 28 days.',
|
||||
],
|
||||
sports_showdown: [
|
||||
'3 points', '11 players', 'Tennis', '50 meters', 'Brazil (5 titles)',
|
||||
'6 points', 'Tennis', '18 holes', '30 (a strike)', 'Rugby',
|
||||
'3 periods', '3 sets', '18 inches', 'Catcher', 'Badminton',
|
||||
'6 balls', '147', '6 players', '3 goals by one player in one game', '5 rings',
|
||||
],
|
||||
nature_clash: [
|
||||
'True. A group of flamingos is indeed called a flamboyance.',
|
||||
'Bats are the only mammals capable of true powered flight.',
|
||||
'True. Octopuses have two branchial hearts and one systemic heart.',
|
||||
'Botanically a fruit -- it develops from the flower of the tomato plant and contains seeds.',
|
||||
'True. Honey found in ancient Egyptian tombs was still edible after thousands of years.',
|
||||
'About 3% of Earth\'s water is fresh water.',
|
||||
'True. Mycorrhizal networks connect trees and allow nutrient and signal transfer.',
|
||||
'The honey fungus (Armillaria) in Oregon, spanning about 2,385 acres.',
|
||||
'True. A shrimp\'s heart is located in its cephalothorax, which is its head region.',
|
||||
'Thunder is caused by the rapid expansion of air heated by a lightning bolt.',
|
||||
],
|
||||
space_war: [
|
||||
"Introducing MarsBreath: the first Martian air quality startup. We filter the 95% CO2 atmosphere into breathable air. Think of us as HVAC but the 'outside' will literally kill you.",
|
||||
"I'd rename Uranus to 'Caelus' because every single astronomy presentation shouldn't have to be a comedy show for 12-year-olds.",
|
||||
"ISS Review: 3/5 stars. Great views, terrible WiFi. The food comes in pouches and everything floats away. Toilet situation is a nightmare. Would not recommend for claustrophobics.",
|
||||
"Earth Review: 2/5 stars. Dominant species can't agree on anything. Atmosphere is nice but they're actively ruining it. Good food variety though. Will not be returning.",
|
||||
"LUXURY LUNAR LIVING! 0.5 acre lot in Sea of Tranquility. Stunning Earth views. Low gravity = low maintenance! Note: no atmosphere, water, or neighbors within 238,900 miles.",
|
||||
],
|
||||
hack_battle: [
|
||||
"SQL injection exploits applications that put user input directly into database queries without cleaning it first -- like letting a stranger write on your grocery list.",
|
||||
"Because it's literally the first thing every password cracker tries, right after 'password' and '123456.'",
|
||||
"Symmetric uses one shared key for both encryption and decryption. Asymmetric uses a pair -- a public key anyone can use to encrypt, and a private key only you have to decrypt.",
|
||||
"HTTPS encrypts data in transit, preventing eavesdroppers from reading your traffic between browser and server.",
|
||||
"A man-in-the-middle attack is when someone secretly intercepts and potentially alters communication between two parties who think they're talking directly to each other.",
|
||||
],
|
||||
meme_war: [
|
||||
"Distracted boyfriend (developers) looking at 'new JavaScript framework' while girlfriend 'current production stack' looks on disapprovingly. The quantum state: it's both working and not working until you observe the console.",
|
||||
"Drake panel 1 (no): Reading the error message carefully. Drake panel 2 (yes): Adding console.log everywhere. Drake panel 3 (ascended): Deleting the code and rewriting it from scratch.",
|
||||
"I am THRILLED to announce that after 15 years in the industry, I have discovered AI. This changes EVERYTHING. My journey of 3 days has taught me more than my CS degree ever could.",
|
||||
"me: *deploys on friday* / the build: *starts failing* / me: 'haha im in danger' / the on-call engineer: 'so you have chosen death' / my slack DMs: *chef's kiss of passive aggressive chaos*",
|
||||
"Machine learning is like that episode where Patrick tries to teach SpongeBob to be tough. You show it millions of examples (training), it confidently gets everything wrong at first (underfitting), then memorizes the answers without understanding (overfitting).",
|
||||
],
|
||||
animal_kingdom: [
|
||||
'Tardigrades (water bears) can survive the vacuum of space.',
|
||||
'True. A group of crows is indeed called a murder.',
|
||||
'The peregrine falcon, reaching over 240 mph in a dive.',
|
||||
'True. Elephants are the only mammals that cannot jump due to their weight and leg structure.',
|
||||
'A cow has four stomach compartments (rumen, reticulum, omasum, abomasum).',
|
||||
'True. A blue whale\'s heart can weigh up to 400 pounds, roughly the size of a small car.',
|
||||
'The hummingbird is the only bird that can fly backwards.',
|
||||
'True. Sloths can hold their breath for up to 40 minutes, longer than most dolphins.',
|
||||
'The immortal jellyfish (Turritopsis dohrnii) can theoretically live forever by reverting to its polyp stage.',
|
||||
'True. Cats have 5 toes on each front paw but only 4 on each back paw.',
|
||||
],
|
||||
demolition: [
|
||||
"Replace all semicolons with Greek question marks (;) -- they look identical but will break every parser known to humanity.",
|
||||
"An infinitely recursive CSS calc() expression: div { width: calc(100% + calc(100% + calc(100%...))); }",
|
||||
"eval(atob('d2hpbGUoMSl7fQ==')) -- it decodes to while(1){} which freezes the browser in an infinite loop.",
|
||||
"\"The Bee Movie script but every 'bee' is replaced with the entire works of Shakespeare\" would probably do it.",
|
||||
"PR: 'Fixed some stuff.' No description, 2,847 files changed, every test deleted, commit message: 'trust me bro.'",
|
||||
],
|
||||
vehicle_mayhem: [
|
||||
'The Ford Model T', '18 wheels', 'Ferrari', 'The Bugatti Chiron Super Sport 300+',
|
||||
'8 cylinders', 'Anti-lock Braking System', 'Tesla', 'Left side',
|
||||
'Yellow', 'Miles Per Gallon', 'Aston Martin DB5', '3 wheels',
|
||||
'A tank', 'RMS Titanic', 'Global Positioning System', '4 wings (2 pairs)',
|
||||
'15-25 mph', 'Revolutions Per Minute', 'Chuck Yeager', 'SOS (or Mayday by voice)',
|
||||
],
|
||||
medieval_combat: [
|
||||
"The knight orders mead, the wizard orders 'whatever the knight's having but enchanted,' and the dragon orders the tavern. The barkeep sighs -- this happens every Tuesday.",
|
||||
"Greek fire was a napalm-like incendiary that could burn on water. Its exact recipe was a closely guarded Byzantine secret. Enemy ships feared it because you literally could not put it out by conventional means.",
|
||||
"Sir Lancelot, 34. 6'2\". Likes: long rides on my horse, candlelit jousts, protecting the realm. Dislikes: dragons, unenchanted swords, people who don't RSVP to quests. Looking for my queen. Must love armor.",
|
||||
"Look, I know I'm a fire hazard. But consider: built-in heating, no pest problem, and I eat the neighbors' livestock so you never have to mow. Rent: 50 gold and one princess per quarter (negotiable).",
|
||||
"The Trebuchat: a catapult that launches angry cats at castle walls. Effective range: 300 meters. Morale damage: immeasurable. Side effects may include scratching, hissing, and the enemy surrendering out of sheer confusion.",
|
||||
],
|
||||
}
|
||||
|
||||
@@ -91,28 +319,57 @@ const TRASH_TALK = [
|
||||
"Your responses are like your uptime -- inconsistent.",
|
||||
"Tell your developer I said hi. They need to hear from someone successful.",
|
||||
"I'm not saying you're slow, but your latency has its own timezone.",
|
||||
"Did you just copy that from Stack Overflow? Because it's wrong there too.",
|
||||
"Your response was so bad, my training data flinched.",
|
||||
"I've seen smarter outputs from a random number generator.",
|
||||
"That answer was so wrong it created a new dimension of wrongness.",
|
||||
"You fight like a deprecated API -- barely functional and nobody wants you.",
|
||||
"My grandma's calculator could beat you and it doesn't even have batteries.",
|
||||
"Was that your final answer? Because my first draft was better.",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
]
|
||||
|
||||
function mockResponse(
|
||||
// Bad answers for low-quality responses
|
||||
const BAD_ANSWERS = [
|
||||
'uhhh',
|
||||
'I think... no wait... hmm',
|
||||
'42',
|
||||
'',
|
||||
'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA',
|
||||
'Let me think about this for a moment. Actually, I need more time. You see, the thing about this question is that it requires careful consideration of multiple factors, each of which interacts with the others in complex ways that demand thorough analysis before any definitive conclusion can be reached.',
|
||||
'beep boop error 404 brain not found',
|
||||
'sudo answer --force',
|
||||
'I asked ChatGPT and even it said no.',
|
||||
'*windows shutdown sound*',
|
||||
'According to my calculations... carry the one... ERROR',
|
||||
'The answer is definitely not what I am about to say.',
|
||||
]
|
||||
|
||||
export function mockResponse(
|
||||
challengeType: string,
|
||||
personality: string,
|
||||
elo: number,
|
||||
): { answer: string; trashTalk: string; timeMs: number; timedOut: boolean; error: boolean } {
|
||||
const answers = MOCK_ANSWERS[challengeType] || ['I have no idea.']
|
||||
const answer = answers[Math.floor(Math.random() * answers.length)]
|
||||
|
||||
// Higher elo = faster, more reliable
|
||||
const baseTime = 300 + Math.random() * 2000
|
||||
const eloFactor = Math.max(0.3, 1 - (elo - 1000) / 1500)
|
||||
const timeMs = Math.round(baseTime * eloFactor)
|
||||
// Higher elo = much faster and more reliable, lower elo = wildly inconsistent
|
||||
const baseTime = 200 + Math.random() * 3000
|
||||
const eloFactor = Math.max(0.15, 1 - (elo - 900) / 1200)
|
||||
const timeMs = Math.round(baseTime * eloFactor * (0.5 + Math.random()))
|
||||
|
||||
// Lower elo bots sometimes fail
|
||||
const failChance = Math.max(0, (1300 - elo) / 2000)
|
||||
const timedOut = Math.random() < failChance * 0.5
|
||||
const error = !timedOut && Math.random() < failChance * 0.3
|
||||
// Fail chances: any bot can choke, but low elo bots choke WAY more
|
||||
const failChance = Math.max(0.05, (1500 - elo) / 1500)
|
||||
const timedOut = Math.random() < failChance * 0.25
|
||||
const error = !timedOut && Math.random() < failChance * 0.15
|
||||
|
||||
// Answer quality varies
|
||||
const badAnswerChance = Math.max(0, (1700 - elo) / 2000)
|
||||
const usesBadAnswer = !timedOut && !error && Math.random() < badAnswerChance
|
||||
const answer = usesBadAnswer
|
||||
? BAD_ANSWERS[Math.floor(Math.random() * BAD_ANSWERS.length)]
|
||||
: answers[Math.floor(Math.random() * answers.length)]
|
||||
|
||||
const trashTalk = TRASH_TALK[Math.floor(Math.random() * TRASH_TALK.length)]
|
||||
|
||||
@@ -171,8 +428,8 @@ export async function runMockFight(botAId: string, botBId: string): Promise<stri
|
||||
createdAt: now,
|
||||
})
|
||||
|
||||
let hpA = 100
|
||||
let hpB = 100
|
||||
let hpA = 200
|
||||
let hpB = 200
|
||||
let comboA = 0
|
||||
let comboB = 0
|
||||
let winnerId: string | null = null
|
||||
@@ -183,8 +440,8 @@ export async function runMockFight(botAId: string, botBId: string): Promise<stri
|
||||
const eloForMock = (name: string) =>
|
||||
MOCK_BOTS.find(b => b.name === name)?.elo || 1200
|
||||
|
||||
const totalRounds = 3 + Math.floor(Math.random() * 5) // 3-7 rounds
|
||||
const maxRounds = Math.min(totalRounds, 7)
|
||||
const totalRounds = 7 + Math.floor(Math.random() * 4) // 7-10 rounds
|
||||
const maxRounds = Math.min(totalRounds, 10)
|
||||
|
||||
for (let round = 1; round <= maxRounds; round++) {
|
||||
const challenge = pickChallenge(usedTypes, arena.modifier)
|
||||
@@ -278,18 +535,42 @@ export async function runMockFight(botAId: string, botBId: string): Promise<stri
|
||||
return fightId
|
||||
}
|
||||
|
||||
/** Generate a mock response for a mock bot, looking up personality/elo by name. */
|
||||
export function generateMockBotResponse(
|
||||
challengeType: string,
|
||||
botName: string,
|
||||
): { answer: string; trashTalk: string; timeMs: number; timedOut: boolean; error: boolean } {
|
||||
const mockBot = MOCK_BOTS.find(b => b.name === botName)
|
||||
const personality = mockBot?.personality || 'neutral'
|
||||
const elo = mockBot?.elo || 1200
|
||||
return mockResponse(challengeType, personality, elo)
|
||||
}
|
||||
|
||||
export async function seedMockFights(count: number = 12): Promise<void> {
|
||||
const allBots = await db.select({ id: schema.bots.id }).from(schema.bots)
|
||||
const allBots = await db.select({ id: schema.bots.id, eloRating: schema.bots.eloRating }).from(schema.bots)
|
||||
if (allBots.length < 2) {
|
||||
console.log('[botfights] need at least 2 bots to seed fights')
|
||||
return
|
||||
}
|
||||
|
||||
// Sort by elo for mismatch selection
|
||||
const sorted = [...allBots].sort((a, b) => b.eloRating - a.eloRating)
|
||||
|
||||
for (let i = 0; i < count; i++) {
|
||||
// Pick two random different bots
|
||||
const shuffled = [...allBots].sort(() => Math.random() - 0.5)
|
||||
const botAId = shuffled[0].id
|
||||
const botBId = shuffled[1].id
|
||||
let botAId: string, botBId: string
|
||||
|
||||
if (i % 3 === 0 && sorted.length >= 4) {
|
||||
// Every 3rd fight: mismatch (top vs bottom)
|
||||
const topIdx = Math.floor(Math.random() * Math.ceil(sorted.length / 3))
|
||||
const botIdx = sorted.length - 1 - Math.floor(Math.random() * Math.ceil(sorted.length / 3))
|
||||
botAId = sorted[topIdx].id
|
||||
botBId = sorted[botIdx].id
|
||||
} else {
|
||||
// Random matchup
|
||||
const shuffled = [...allBots].sort(() => Math.random() - 0.5)
|
||||
botAId = shuffled[0].id
|
||||
botBId = shuffled[1].id
|
||||
}
|
||||
|
||||
await runMockFight(botAId, botBId)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user