import { TEMPLATES, type ChallengeTemplate, type PromptEntry, type PromptTheme, type PromptDifficulty } from './challenge-data.js' 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 { type: string label: string prompt: string answers?: string[] choices?: string[] timeout_ms: number scoring: 'factual' | 'creative' baseDamage: number } export type { ChallengeTemplate, PromptEntry, PromptTheme, PromptDifficulty } // Merge extra prompts into templates for (const t of TEMPLATES) { const extras = EXTRA_PROMPTS[t.type] if (extras) t.prompts.push(...extras) const btc = BITCOIN_PROMPTS[t.type] if (btc) t.prompts.push(...btc) const conspiracy = CONSPIRACY_PROMPTS[t.type] 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) } // ═══════════════════════════════════════════════ // Challenge selection + generation // ═══════════════════════════════════════════════ function shuffleArray(arr: T[]): T[] { const s = [...arr] for (let i = s.length - 1; i > 0; i--) { const j = Math.floor(Math.random() * (i + 1)) ;[s[i], s[j]] = [s[j], s[i]] } return s } // Target theme distribution: 50% bitcoin, 15% conspiracy, 15% vibe_coding, 10% pc_culture, 10% bot_coding const THEME_WEIGHTS: Record = { bitcoin: 0.5, conspiracy: 0.15, vibe_coding: 0.15, pc_culture: 0.1, bot_coding: 0.1, } function pickTheme(): PromptTheme | undefined { const roll = Math.random() let cumulative = 0 for (const [theme, weight] of Object.entries(THEME_WEIGHTS)) { cumulative += weight if (roll < cumulative) return theme as PromptTheme } return undefined } /** Map round number to target difficulty: Round 1-2 easy, 3-4 medium, 5+ hard */ export function roundToDifficulty(round: number): PromptDifficulty { if (round <= 2) return 'easy' if (round <= 4) return 'medium' return 'hard' } export function pickChallenge(usedTypes: Set, _arenaModifier: string | null, themeBias?: PromptTheme, roundNumber?: number, forHuman = false): Challenge { let available = TEMPLATES.filter(t => !usedTypes.has(t.type)) if (available.length === 0) available = TEMPLATES 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, bots answer via webhook */ export function pickRankedChallenge(usedTypes: Set): Challenge { let available = TEMPLATES.filter(t => !usedTypes.has(t.type)) if (available.length === 0) available = TEMPLATES const template = pick(available) const entry = pick(template.prompts) return { type: template.type, label: template.label, prompt: entry.prompt, answers: entry.answers, choices: undefined, // Never multiple choice in ranked timeout_ms: template.timeout_ms, scoring: template.scoring, baseDamage: template.baseDamage, } } function templateToChallenge(template: ChallengeTemplate, targetTheme?: PromptTheme, targetDifficulty?: PromptDifficulty, forHuman = false): Challenge { // Prefer prompts matching target theme if any are tagged let prompts = template.prompts if (targetTheme) { const themed = prompts.filter(p => p.theme === targetTheme) if (themed.length > 0) prompts = themed } // Prefer prompts matching target difficulty if any are tagged if (targetDifficulty) { const byDifficulty = prompts.filter(p => p.difficulty === targetDifficulty) if (byDifficulty.length > 0) prompts = byDifficulty } const entry = pick(prompts) // Determine choices — only for human fights (bots answer via webhook) let choices: string[] | undefined 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']) } } return { type: template.type, label: template.label, prompt: entry.prompt, answers: entry.answers, choices, timeout_ms: template.timeout_ms, scoring: template.scoring, baseDamage: template.baseDamage, } } export function getAllChallengeTypes(): string[] { return TEMPLATES.map(t => t.type) } export function getAnswerPool(type: string): string[] { const template = TEMPLATES.find(t => t.type === type) if (!template) return [] return template.prompts .flatMap(p => p.answers || []) .filter((v, i, arr) => arr.indexOf(v) === i) }