Files
botfights/server/src/engine/dev-seed.ts
T

201 lines
7.0 KiB
TypeScript
Raw Normal View History

import { db, schema } from '../db/index.js'
import { sql, eq } from 'drizzle-orm'
import { nanoid } from 'nanoid'
import { createTournament, joinTournament, startTournament } from './tournaments.js'
import { randomArena } from './arenas.js'
import { getCurrentSeason } from './seasons.js'
import { logger } from '../lib/logger.js'
/** Seed a dev tournament with mock bots for testing betting/tournament UI */
export async function seedDevTournament(): Promise<void> {
// Skip if a tournament already exists
const existing = db.select({ count: sql<number>`count(*)` })
.from(schema.tournaments)
.get()
if (existing && existing.count > 0) {
logger.info('dev-seed', 'tournaments already exist — skipping seed')
return
}
// Grab 8 mock bots for the tournament
const bots = db.select({ id: schema.bots.id, name: schema.bots.name })
.from(schema.bots)
.limit(8)
.all()
if (bots.length < 4) {
logger.warn('dev-seed', `only ${bots.length} bots — need at least 4 for tournament seed`)
return
}
// Create tournament
const tournamentId = createTournament(
'SATOSHI SHOWDOWN #1',
'single_elim',
8,
100,
)
// Join bots
for (const bot of bots.slice(0, 8)) {
try {
joinTournament(tournamentId, bot.id)
} catch (err) {
logger.warn('dev-seed', `failed to join bot ${bot.name}: ${(err as Error).message}`)
}
}
// Start the tournament (seeds by elo, generates bracket)
try {
startTournament(tournamentId)
logger.info('dev-seed', `seeded tournament "${tournamentId}" with ${Math.min(bots.length, 8)} bots`)
} catch (err) {
logger.warn('dev-seed', `failed to start tournament: ${(err as Error).message}`)
}
logger.info('dev-seed', 'dev seeding complete — tournament ready for testing')
}
/** Seed a diverse fight card with scheduled fights for the Fight Card page */
export async function seedFightCard(): Promise<void> {
// Skip if scheduled fights already exist
const scheduled = db.select({ count: sql<number>`count(*)` })
.from(schema.fights)
.where(eq(schema.fights.status, 'scheduled'))
.get()
if (scheduled && scheduled.count > 0) {
logger.info('dev-seed', 'scheduled fights already exist — skipping fight card seed')
return
}
// Get all bots by type
const allBots = db.select({
id: schema.bots.id,
name: schema.bots.name,
botType: schema.bots.botType,
webhookUrl: schema.bots.webhookUrl,
archetype: schema.bots.archetype,
tier: schema.bots.tier,
eloRating: schema.bots.eloRating,
})
.from(schema.bots)
.where(eq(schema.bots.isActive, true))
.all()
const humans = allBots.filter(b => b.webhookUrl === 'http://human.local/')
const mockBots = allBots.filter(b => b.botType === 'mock')
const classicBots = allBots.filter(b => b.botType === 'classic')
const userBots = allBots.filter(b => b.botType === 'regular' && b.webhookUrl !== 'http://human.local/')
function pick<T>(arr: T[]): T { return arr[Math.floor(Math.random() * arr.length)] }
const season = getCurrentSeason()
const fights: Array<{ botAId: string; botBId: string; label: string }> = []
// 1. AI vs AI (mock bots fighting each other) — main event
if (mockBots.length >= 2) {
const a = pick(mockBots.filter(b => b.tier >= 4)) || pick(mockBots)
let b = pick(mockBots.filter(x => x.id !== a.id && x.tier >= 3)) || pick(mockBots.filter(x => x.id !== a.id))
if (b) fights.push({ botAId: a.id, botBId: b.id, label: 'AI vs AI (high tier)' })
}
// 2. Human vs AI (mock bot)
if (humans.length > 0 && mockBots.length > 0) {
fights.push({ botAId: pick(humans).id, botBId: pick(mockBots).id, label: 'Human vs AI' })
}
// 3. AI vs AI (different archetypes, mid tier)
if (mockBots.length >= 4) {
const midBots = mockBots.filter(b => b.tier >= 1 && b.tier <= 3)
if (midBots.length >= 2) {
const a = pick(midBots)
const b = pick(midBots.filter(x => x.id !== a.id && x.archetype !== a.archetype))
|| pick(midBots.filter(x => x.id !== a.id))
if (b) fights.push({ botAId: a.id, botBId: b.id, label: 'AI vs AI (mid tier)' })
}
}
// 4. Human vs Classic Bot
if (humans.length > 0 && classicBots.length > 0) {
fights.push({ botAId: pick(humans).id, botBId: pick(classicBots).id, label: 'Human vs Classic' })
}
// 5. Bot vs Bot (user bots or mock if none)
if (userBots.length >= 2) {
const a = pick(userBots)
const b = pick(userBots.filter(x => x.id !== a.id))
if (b) fights.push({ botAId: a.id, botBId: b.id, label: 'Bot vs Bot (user)' })
} else if (mockBots.length >= 6) {
const usedIds = new Set(fights.flatMap(f => [f.botAId, f.botBId]))
const available = mockBots.filter(b => !usedIds.has(b.id))
if (available.length >= 2) {
fights.push({ botAId: available[0].id, botBId: available[1].id, label: 'Bot vs Bot' })
}
}
// 6. AI vs Classic Bot
if (mockBots.length > 0 && classicBots.length > 0) {
fights.push({ botAId: pick(mockBots).id, botBId: pick(classicBots).id, label: 'AI vs Classic' })
}
// 7. Human vs Human (if we have 2+)
if (humans.length >= 2) {
const a = pick(humans)
const b = pick(humans.filter(x => x.id !== a.id))
if (b) fights.push({ botAId: a.id, botBId: b.id, label: 'Human vs Human' })
}
// 8. Rookie rumble — tier 0 bots
{
const rookies = mockBots.filter(b => b.tier === 0)
if (rookies.length >= 2) {
fights.push({ botAId: rookies[0].id, botBId: rookies[1].id, label: 'Rookie Rumble' })
}
}
// 9. Legend clash — tier 5 bots
{
const legends = mockBots.filter(b => b.tier >= 5)
const usedIds = new Set(fights.flatMap(f => [f.botAId, f.botBId]))
const available = legends.filter(b => !usedIds.has(b.id))
if (available.length >= 2) {
fights.push({ botAId: available[0].id, botBId: available[1].id, label: 'Legend Clash' })
}
}
// 10. Wild card — random pairing from anything left
{
const usedIds = new Set(fights.flatMap(f => [f.botAId, f.botBId]))
const remaining = allBots.filter(b => !usedIds.has(b.id))
if (remaining.length >= 2) {
const a = pick(remaining)
const b = pick(remaining.filter(x => x.id !== a.id))
if (b) fights.push({ botAId: a.id, botBId: b.id, label: 'Wild Card' })
}
}
// Insert all as scheduled fights
const now = new Date()
for (let i = 0; i < fights.length; i++) {
const f = fights[i]
const scheduledTime = new Date(now.getTime() + (i + 1) * 10 * 60 * 1000) // stagger 10 min apart
try {
db.insert(schema.fights).values({
id: nanoid(12),
botAId: f.botAId,
botBId: f.botBId,
arena: randomArena().id,
status: 'scheduled',
currentSeason: season.id,
scheduledAt: scheduledTime.toISOString(),
createdAt: now.toISOString(),
}).run()
logger.info('dev-seed', `fight card: ${f.label}`)
} catch (err) {
logger.warn('dev-seed', `fight card failed: ${(err as Error).message}`)
}
}
logger.info('dev-seed', `seeded ${fights.length} scheduled fights for fight card`)
}