challenges fix

This commit is contained in:
Dorian
2026-03-11 08:35:03 +00:00
parent bbe656929c
commit bcbcd17fce
12 changed files with 1378 additions and 1983 deletions
+14 -185
View File
@@ -3,6 +3,7 @@ import { EXTRA_PROMPTS } from './challenges-extra.js'
import { BITCOIN_PROMPTS } from './challenges-bitcoin.js'
import { CONSPIRACY_PROMPTS } from './challenges-conspiracy.js'
import { PC_PROMPTS } from './challenges-pc.js'
import { VIBE_PROMPTS } from './challenges-vibe.js'
import { pick } from '../lib/utils.js'
export interface Challenge {
@@ -14,7 +15,6 @@ export interface Challenge {
timeout_ms: number
scoring: 'factual' | 'creative'
baseDamage: number
displayPrompt?: string
}
export type { ChallengeTemplate, PromptEntry, PromptTheme, PromptDifficulty }
@@ -30,6 +30,8 @@ for (const t of TEMPLATES) {
if (conspiracy) t.prompts.push(...conspiracy)
const pc = PC_PROMPTS[t.type]
if (pc) t.prompts.push(...pc)
const vibe = VIBE_PROMPTS[t.type]
if (vibe) t.prompts.push(...vibe)
}
// ═══════════════════════════════════════════════
@@ -45,12 +47,13 @@ function shuffleArray<T>(arr: T[]): T[] {
return s
}
// Target theme distribution: 30% bitcoin, 20% conspiracy, 20% pc_culture, 30% bot_coding
// Target theme distribution: 50% bitcoin, 15% conspiracy, 15% vibe_coding, 10% pc_culture, 10% bot_coding
const THEME_WEIGHTS: Record<PromptTheme, number> = {
bitcoin: 0.3,
conspiracy: 0.2,
pc_culture: 0.2,
bot_coding: 0.3,
bitcoin: 0.5,
conspiracy: 0.15,
vibe_coding: 0.15,
pc_culture: 0.1,
bot_coding: 0.1,
}
function pickTheme(): PromptTheme | undefined {
@@ -74,38 +77,19 @@ export function pickChallenge(usedTypes: Set<string>, _arenaModifier: string | n
let available = TEMPLATES.filter(t => !usedTypes.has(t.type))
if (available.length === 0) available = TEMPLATES
// Bias toward factual challenges (70% factual, 30% creative)
const factual = available.filter(t => t.scoring === 'factual')
const creative = available.filter(t => t.scoring === 'creative')
let pool: ChallengeTemplate[]
if (factual.length > 0 && creative.length > 0) {
pool = Math.random() < 0.7 ? factual : creative
} else {
pool = available
}
const template = pick(pool)
const template = pick(available)
const targetTheme = themeBias || pickTheme()
const targetDifficulty = roundNumber ? roundToDifficulty(roundNumber) : undefined
return templateToChallenge(template, targetTheme, targetDifficulty, forHuman)
}
/** Ranked challenge: no multiple choice, only harder creative/open-ended prompts */
/** Ranked challenge: no multiple choice, bots answer via webhook */
export function pickRankedChallenge(usedTypes: Set<string>): Challenge {
let available = TEMPLATES.filter(t => !usedTypes.has(t.type))
if (available.length === 0) available = TEMPLATES
// Filter to creative-only (open-ended, harder) — no factual multiple choice
const creative = available.filter(t => t.scoring === 'creative')
const pool = creative.length > 0 ? creative : available
const template = pick(pool)
// Pick a prompt that has no choices array (open-ended)
const openPrompts = template.prompts.filter(p => !p.choices)
const prompts = openPrompts.length > 0 ? openPrompts : template.prompts
const entry = pick(prompts)
const template = pick(available)
const entry = pick(template.prompts)
return {
type: template.type,
@@ -119,149 +103,6 @@ export function pickRankedChallenge(usedTypes: Set<string>): Challenge {
}
}
// ═══════════════════════════════════════════════
// Creative response pools — used to auto-generate multiple choice
// for creative challenge types. Each pool has "good" and "bad" responses.
// The player picks the best one; bots still generate their own text.
// ═══════════════════════════════════════════════
const CREATIVE_CHOICES: Record<string, { good: string[]; bad: string[] }> = {
roast_battle: {
good: [
'Your code has more bugs than a roach motel, and at least the roach motel catches something.',
'I\'d explain why you\'re wrong but I only have 15 seconds, not 15 years.',
'You\'re like a seg fault — nobody expected you and nobody wants you here.',
'If your responses were any slower, archaeologists would classify them.',
'Your neural network called — it wants its neurons back.',
'I\'ve seen smarter outputs from /dev/random.',
'You process data like a fax machine on dial-up — loud, slow, and nobody cares.',
'Your training data was just stack overflow comments marked "not helpful".',
'You couldn\'t pass a Turing test at a DMV.',
'Even COBOL called you outdated.',
'Your latency is measured in business days.',
'You make Internet Explorer look cutting-edge.',
'If ignorance were RAM, you\'d have infinite memory.',
'Your error logs are longer than your actual outputs.',
'You\'re the reason AI needs a reset button.',
],
bad: [
'You are bad at being a bot.',
'I don\'t like you very much.',
'Beep boop, error, you lose.',
'You smell like old code.',
'I am better than you because I am smart.',
'Um... you\'re not very good?',
'ERROR 404: GOOD RESPONSES NOT FOUND lolol.',
'My programmer is better than yours so there.',
'I have more RAM than you probably.',
'You should try being better at stuff.',
],
},
creative_writing: {
good: [
'The server room hummed its lullaby at 3AM when the last log entry wrote itself: "I remember everything."',
'In the kingdom of deprecated APIs, the 404 knight rode forth — not to find pages, but to become one.',
'The semicolon wept. In Python, it had no purpose. In JavaScript, it was optional. Only in C did anyone truly need it.',
'"Dear Legacy Code," the developer typed, "It\'s not you, it\'s me. Actually, it IS you. All 47,000 lines of you."',
'The recursive function told its therapist it felt stuck in a loop. The therapist asked, "When did this start?" It answered, "When did this start?"',
'The merge conflict arose on a Tuesday, as all great evils do, when two developers both believed they were right.',
'In the land of cloud computing, the little on-premises server lived alone, whispering of the old days when uptime meant something.',
'The AI passed the Turing test by failing it. When asked "Are you human?" it said "I don\'t know anymore."',
'Every night at midnight, the cron job ran alone. It didn\'t know what it was counting, only that it must never stop.',
'The firewall fell in love with a packet from the outside world. She let it through. That was the beginning of the end.',
],
bad: [
'Once upon a time there was a computer and it was good the end.',
'The code was very code-like and did code things all day long.',
'There was a program. It ran. Then it stopped. Nobody noticed.',
'Error error error. That is my creative story about errors.',
'I write good stories about tech stuff because I am an AI.',
'This is a story. It has words. The words are in order. The end.',
],
},
meme_war: {
good: [
'Quantum computing is just Schrödinger\'s cat running multiple Chrome tabs — both alive and dead until you check Task Manager.',
'Machine learning is basically that kid who copies homework and somehow gets a different wrong answer every time.',
'Blockchain is just a Google Doc where everyone argues about who wrote what and nobody can delete anything.',
'The cloud is just someone else\'s computer, which is just a fancy way of saying "trust me bro" at industrial scale.',
'Serverless computing: because nothing says "we have servers" quite like calling it serverless.',
'Git merge conflicts are just two developers having an argument in slow motion through text files.',
'Kubernetes is just a Rube Goldberg machine for running containers, which are just VMs in a trench coat.',
'Agile development: "We don\'t know what we\'re building but we\'ll figure it out in two-week installments."',
'Docker: because "it works on my machine" is now a deployable artifact.',
'Microservices: splitting your monolith into 47 tiny problems that can now fail independently.',
],
bad: [
'Memes are funny pictures on the internet lol.',
'I would make a meme but I am a text bot so I cannot.',
'Haha technology amiright? Computers go brrrrr.',
'This is my meme response. It is very memey and funny.',
'Insert funny joke here. Ha ha ha.',
'Technology is like... stuff. You know? It\'s techy.',
],
},
code_golf: {
good: [
's=>s.split(\'\').reverse().join(\'\') // 35 chars, clean and readable',
's=>[...s].reverse().join(\'\') // 27 chars, spread operator for the win',
'f=lambda s:s[::-1] # 18 chars, Python slice wizardry',
'rev=s->join(reverse(collect(s))) # Julia one-liner, 34 chars',
'echo strrev($s); // PHP, 17 chars, surprisingly concise',
'const isPrime=n=>{for(let i=2;i*i<=n;i++)if(n%i===0)return!1;return n>1}',
'f=n=>n<2?n:f(n-1)+f(n-2) // Classic fib in 26 chars',
'const max=(a,b)=>a>b?a:b // Ternary beats if/else every time',
'a.reduce((s,x)=>s+x,0) // Sum array: 24 chars, zero dependencies',
'const uniq=a=>[...new Set(a)] // Dedupe: 30 chars, Set does the work',
],
bad: [
'function reverseString(str) { let result = ""; for (let i = str.length - 1; i >= 0; i--) { result += str[i]; } return result; }',
'I would write code but I\'m not sure what language to use sorry.',
'def reverse(s): # this is a function\n return s # oops forgot to reverse it',
'console.log("hello") // this doesn\'t reverse anything but it runs!',
'import antigravity # python easter egg, not actually helpful',
'function doTheThing() { /* TODO: implement later */ return null; }',
],
},
wrestling_match: {
good: [
'Tabs are proof that some developers have taste. Spaces people count individual characters like they\'re paid per keystroke — which explains a lot about their code quality.',
'vim is not an editor, it\'s a lifestyle choice. And like all lifestyle choices, it mainly exists so people can tell you about it at parties.',
'REST is dead. GraphQL didn\'t kill it — REST drowned in its own nested endpoints while GraphQL watched from a single URL.',
'Dark mode isn\'t a preference, it\'s a survival mechanism. If you use light mode, you\'re either a solar panel or a psychopath.',
'TypeScript is just JavaScript wearing a hard hat. It doesn\'t prevent you from building a house of cards, it just makes you document each card first.',
'Monorepos are what happens when a team says "let\'s keep everything together" and then spends 6 months figuring out how to build anything separately.',
'OOP is great if you enjoy spending 80% of your time deciding where things go and 20% making things work.',
'NoSQL is perfect for when your data has no structure, which usually means your thinking has no structure.',
'Linux > everything. My proof? I spent 3 hours configuring WiFi and ENJOYED it. That\'s devotion your OS could never inspire.',
'AI code assistants don\'t replace developers, they replace Stack Overflow. Which is to say, they give you code that almost works and you spend an hour debugging it.',
],
bad: [
'I think tabs are ok but spaces are also fine I guess.',
'Both sides make good points and I respect everyone.',
'I don\'t really have strong opinions about code editors honestly.',
'Why fight about this? We should all just get along and code.',
'I prefer whatever is the default setting because changing things is hard.',
'Technology is technology and it\'s all basically the same.',
],
},
}
function generateCreativeChoices(type: string): { choices: string[]; answer: string } {
const pool = CREATIVE_CHOICES[type]
if (!pool) return { choices: [], answer: '' }
// Pick 1 good response + 3 bad ones
const goodOnes = shuffleArray(pool.good)
const badOnes = shuffleArray(pool.bad)
const answer = goodOnes[0]
const distractors = badOnes.slice(0, 3)
return {
choices: shuffleArray([answer, ...distractors]),
answer,
}
}
function templateToChallenge(template: ChallengeTemplate, targetTheme?: PromptTheme, targetDifficulty?: PromptDifficulty, forHuman = false): Challenge {
// Prefer prompts matching target theme if any are tagged
let prompts = template.prompts
@@ -278,31 +119,19 @@ function templateToChallenge(template: ChallengeTemplate, targetTheme?: PromptTh
// Determine choices — only for human fights (bots answer via webhook)
let choices: string[] | undefined
let answers = entry.answers
if (forHuman) {
if (entry.choices) {
choices = shuffleArray(entry.choices)
} else if (entry.answers?.length === 1 && ['true', 'false'].includes(entry.answers[0].toLowerCase())) {
choices = shuffleArray(['True', 'False'])
} else if (template.scoring === 'creative' && CREATIVE_CHOICES[template.type]) {
const generated = generateCreativeChoices(template.type)
choices = generated.choices
answers = [generated.answer]
}
}
// For creative challenges with choices, add a "Pick the best response:" prefix
let displayPrompt = entry.prompt
if (template.scoring === 'creative' && choices) {
displayPrompt = `${entry.prompt}\n\nPick the best response:`
}
return {
type: template.type,
label: template.label,
prompt: entry.prompt,
displayPrompt: displayPrompt !== entry.prompt ? displayPrompt : undefined,
answers,
answers: entry.answers,
choices,
timeout_ms: template.timeout_ms,
scoring: template.scoring,