feat: convert all human fights to multiple choice, remove text input
Creative challenges (roast_battle, creative_writing, meme_war, code_golf, wrestling_match) now auto-generate multiple choice options from per-type response pools: 1 good answer + 3 weaker distractors. The free text input UI is commented out but preserved for future use. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
4dc350dc9e
commit
3c95adb545
@@ -3,14 +3,15 @@ import { Hono } from 'hono'
|
||||
import { logger } from '../lib/logger.js'
|
||||
import { streamSSE } from 'hono/streaming'
|
||||
import { db, schema } from '../db/index.js'
|
||||
import { eq, desc } from 'drizzle-orm'
|
||||
import { eq, desc, inArray } from 'drizzle-orm'
|
||||
import { ARENAS } from '../engine/arenas.js'
|
||||
import { runMockFight, isClassicBot } from '../engine/mock.js'
|
||||
import { startFightLoop } from '../engine/fight-loop.js'
|
||||
import { runFight, runFightAsync, isInFight } from '../engine/orchestrator.js'
|
||||
import { fightEvents } from '../engine/events.js'
|
||||
import { botRateLimit } from '../middleware/rate-limit.js'
|
||||
import { getPendingChallenge, submitHumanResponse } from '../engine/human-responses.js'
|
||||
import { getPendingChallenge, getPendingAnswers, submitHumanResponse } from '../engine/human-responses.js'
|
||||
import { checkAnswer } from '../engine/answers.js'
|
||||
|
||||
// --- Request validation schemas ---
|
||||
const respondSchema = z.object({
|
||||
@@ -67,17 +68,18 @@ fightsRouter.get('/', async (c) => {
|
||||
if (f.winnerId) botIds.add(f.winnerId)
|
||||
}
|
||||
|
||||
const botMap = new Map<string, { name: string; avatarSeed: string; archetype: string; eloRating: number; tier: number; botType: string }>()
|
||||
for (const id of botIds) {
|
||||
const bot = await db.select({
|
||||
const botMap = new Map<string, { id: string; name: string; avatarSeed: string; archetype: string; eloRating: number; tier: number; botType: string }>()
|
||||
if (botIds.size > 0) {
|
||||
const bots = await db.select({
|
||||
id: schema.bots.id,
|
||||
name: schema.bots.name,
|
||||
avatarSeed: schema.bots.avatarSeed,
|
||||
archetype: schema.bots.archetype,
|
||||
eloRating: schema.bots.eloRating,
|
||||
tier: schema.bots.tier,
|
||||
botType: schema.bots.botType,
|
||||
}).from(schema.bots).where(eq(schema.bots.id, id)).limit(1)
|
||||
if (bot[0]) botMap.set(id, bot[0])
|
||||
}).from(schema.bots).where(inArray(schema.bots.id, [...botIds]))
|
||||
for (const bot of bots) botMap.set(bot.id, bot)
|
||||
}
|
||||
|
||||
const enriched = rows.map(f => {
|
||||
@@ -353,12 +355,14 @@ fightsRouter.post('/:fightId/respond/:botId', async (c) => {
|
||||
}
|
||||
|
||||
const { answer, trashTalk } = parsed.data
|
||||
const answers = getPendingAnswers(fightId, botId)
|
||||
const accepted = submitHumanResponse(fightId, botId, answer, trashTalk)
|
||||
if (!accepted) {
|
||||
return c.json({ error: 'No pending challenge found. May have timed out.' }, 404)
|
||||
}
|
||||
|
||||
return c.json({ accepted: true })
|
||||
const correct = answers && answers.length > 0 ? checkAnswer(answer, answers) > 0 : undefined
|
||||
return c.json({ accepted: true, correct })
|
||||
})
|
||||
|
||||
// SSE stream for live fight events
|
||||
@@ -432,6 +436,8 @@ fightsRouter.get('/:id/stream', (c) => {
|
||||
const current = spectatorCounts.get(fightId) || 1
|
||||
if (current <= 1) {
|
||||
spectatorCounts.delete(fightId)
|
||||
// Clean up fight-specific reaction data when last spectator leaves
|
||||
fightReactions.delete(fightId)
|
||||
} else {
|
||||
spectatorCounts.set(fightId, current - 1)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user