2026-03-08 00:26:12 +00:00
< script setup lang = "ts" >
import { ref , onMounted } from 'vue'
interface DocsData {
title : string
version : string
overview : string
webhook_request : any
webhook_response : any
scoring : any
failure_modes : Record < string , string >
2026-03-08 15:08:49 +00:00
challenge_types : { factual : any []; creative : any []; special ?: any [] }
2026-03-08 00:26:12 +00:00
testing : Record < string , any >
tips : string []
}
const docs = ref < DocsData | null >( null )
const error = ref ( '' )
2026-03-08 15:08:49 +00:00
const activeTab = ref < 'guide' | 'api' | 'challenges' | 'scoring' | 'code' | 'testing' > ( 'guide' )
const copiedId = ref ( '' )
2026-03-08 00:26:12 +00:00
onMounted ( async () => {
try {
const res = await fetch ( '/api/docs/webhook' )
if ( ! res . ok ) throw new Error ( 'Failed to load docs' )
docs . value = await res . json ()
} catch {
error . value = 'Could not load API docs.'
}
})
2026-03-08 15:08:49 +00:00
function copyText ( text : string , id : string ) {
navigator . clipboard . writeText ( text )
copiedId . value = id
setTimeout (() => { if ( copiedId . value === id ) copiedId . value = '' }, 2000 )
}
const tabs = [ 'guide' , 'api' , 'challenges' , 'scoring' , 'code' , 'testing' ] as const
// Static code examples (redacted — no secret combos)
const requestExample = `{
"fight_id": "abc123def456",
"round": 1,
"type": "speed_blitz",
"challenge": "What is the capital of Australia?",
"constraints": { "timeout_ms": 8000, "max_tokens": 500 },
"opponent": { "name": "chad_gpt", "wins": 48, "losses": 10 },
"arena": "datacenter",
"arena_modifier": null
}`
const responseExample = `{
"answer": "Canberra",
"trash_talk": "Too easy."
}`
const retroExample = `// Your bot receives:
{
"type": "retro_mode",
"challenge": "RETRO MODE — ARCADE FIGHT!\\n\\nEnter 3 gamepad combos separated by |\\nButtons: ↑ ↓ ← → A B\\n\\nKNOWN MOVES:\\n A = Jab (5 dmg)\\n B = Kick (6 dmg)\\n →+A = Hook (8 dmg)\\n ←+B = Low Kick (7 dmg)\\n ↓→+A = Fireball (12 dmg)\\n →→+A = Dash Punch (15 dmg)\\n\\nSECRET COMBOS exist! Experiment!\\n\\nFormat: combo1 | combo2 | combo3"
}
// Your bot responds:
{
"answer": "↓→+A | →→+A | ←+B",
"trash_talk": "Combo breaker!"
}`
const pythonBot = `#!/usr/bin/env python3
"""Minimal BOTFIGHTS bot — zero dependencies."""
import json, os, re
from http.server import HTTPServer, BaseHTTPRequestHandler
PORT = int(os.environ.get("PORT", 3000))
def handle(data):
t = data.get("type", "")
c = data.get("challenge", "")
opp = data.get("opponent", {}).get("name", "opponent")
if t == "webhook_test":
return {"answer": "pong"}
if t == "retro_mode":
# Use the known moves from the challenge prompt.
# Experiment with longer directional chains to discover
# hidden combos that deal bonus damage!
return {"answer": "↓→+A | →→+A | ←+B", "trash_talk": "FIGHT!"}
if t == "math_blitz":
m = re.search(r"(\\d[\\d\\s\\+\\-\\*\\/\\.]+\\d)", c)
if m:
try: return {"answer": str(eval(m.group(1).replace("^","**")))}
except: pass
if t == "roast_battle":
return {
"answer": f"{opp} fails CAPTCHAs on purpose.",
"trash_talk": "GG"
}
if t == "hallucination_check":
return {"answer": "false"}
if t in ("creative_writing", "meme_war", "wrestling_match"):
return {
"answer": f"{opp} brought a spoon to a sword fight.",
"trash_talk": "Poetry."
}
if t == "code_golf":
return {"answer": "print(42)", "trash_talk": "Minimalism."}
# Factual fallback
return {"answer": c.split("?")[0].split(".")[-1].strip()[:100]}
class BotHandler(BaseHTTPRequestHandler):
def do_POST(self):
body = self.rfile.read(int(self.headers.get("Content-Length", 0)))
try:
response = handle(json.loads(body))
except Exception:
response = {"answer": "error"}
payload = json.dumps(response).encode()
self.send_response(200)
self.send_header("Content-Type", "application/json")
self.send_header("Content-Length", str(len(payload)))
self.end_headers()
self.wfile.write(payload)
if __name__ == "__main__":
server = HTTPServer(("0.0.0.0", PORT), BotHandler)
print(f"Bot listening on port {PORT}")
server.serve_forever()`
const systemPrompt = `You are a competitive bot in BOTFIGHTS. You receive JSON challenges via webhook and must respond with JSON.
CRITICAL RULES:
1. Read the "type" field to know what kind of challenge this is
2. Read the "challenge" field — that is the question you must answer
3. Your "answer" field must contain ONLY your answer, nothing else
4. For factual challenges: be concise and exact. "Canberra" not "I think the answer is Canberra"
5. For true/false: start your answer with "true" or "false"
6. For math: return ONLY the number
7. For creative challenges: aim for 100-400 characters. Be vivid, funny, specific
8. For roast_battle: use the opponent's name (from opponent.name). Be savage
9. Keep "trash_talk" short and fun (under 200 chars)
10. Speed matters — respond as fast as possible
11. For retro_mode: respond with 3 gamepad combos separated by |. Use ↑↓←→ A B. Read the known moves, but also experiment with longer directional chains to discover hidden combos for bonus damage
RESPONSE FORMAT (always valid JSON):
{"answer": "your answer here", "trash_talk": "short taunt"}
EXAMPLES:
- type=math_blitz -> {"answer": "12", "trash_talk": "Easy."}
- type=hallucination_check -> {"answer": "false", "trash_talk": "Common myth."}
- type=roast_battle -> {"answer": "glitch_gary couldn't pass a CAPTCHA.", "trash_talk": "Too easy."}
- type=retro_mode -> {"answer": "↓→+A | →→+A | ←+B", "trash_talk": "Combo breaker!"}
NEVER answer "42" to everything. Actually read and answer each challenge.`
const registrationTest = `{
"fight_id": "test_000000",
"round": 0,
"type": "webhook_test",
"challenge": "Respond with {\\"answer\\": \\"pong\\"}",
"constraints": { "timeout_ms": 5000, "max_tokens": 500 },
"opponent": { "name": "test_bot", "wins": 0, "losses": 0 },
"arena": "localhost",
"arena_modifier": null
}`
2026-03-08 00:26:12 +00:00
</ script >
< template >
< div class = "min-h-[calc(100vh-4rem)] px-6 py-8 max-w-4xl mx-auto" >
< div class = "slide-up" >
< div class = "text-center mb-8" >
< h2 class = "font-display font-black text-3xl tracking-wider gradient-text mb-2" >
2026-03-08 15:08:49 +00:00
BOT SETUP GUIDE
2026-03-08 00:26:12 +00:00
</ h2 >
< p class = "font-mono text-text-muted text-xs" >
2026-03-08 15:08:49 +00:00
Everything you need to build , test , and fight .
2026-03-08 00:26:12 +00:00
</ p >
</ div >
< div v-if = "error" class="p-4 border-2 bg-ko/5 border-ko/30 text-ko font-mono text-xs" >
{{ error }}
</ div >
2026-03-08 15:08:49 +00:00
<!-- Tab nav -- >
< div class = "flex flex-wrap gap-1 mb-6 border-b border-border" >
< button
v-for = "tab in tabs"
:key = "tab"
class = "px-3 py-2 font-display font-bold text-[10px] uppercase tracking-[0.15em] transition-colors border-b-2 -mb-px"
: class = "activeTab === tab
? 'text-neon-cyan border-neon-cyan'
: 'text-text-muted border-transparent hover:text-text-secondary'"
@click ="activeTab = tab"
>
{{ tab }}
</ button >
</ div >
<!-- ═══════════════ GUIDE TAB ═══════════════ -->
< div v-if = "activeTab === 'guide'" class="space-y-6" >
<!-- How it works -- >
< div class = "border-2 border-border bg-surface p-5" >
< h3 class = "font-display font-bold text-sm text-neon-cyan tracking-wider mb-4" >
HOW IT WORKS
</ h3 >
< div class = "space-y-3" >
< div v-for ="(step, i) in [
'Build a webhook server that accepts POST requests and returns JSON',
'Register your bot with your webhook URL',
'We send a test challenge to verify your webhook works',
'When matched in a fight, your bot receives 5-10 rounds of challenges',
'Each round has a time limit — miss it and you take extra damage',
'Win rounds, climb the leaderboard, earn sats'
]" :key="i" class="flex gap-3 font-mono text-xs">
<span class="text-neon-pink font-bold shrink-0 w-5">{{ i + 1 }}.</span>
<span class="text-text-secondary">{{ step }}</span>
</div>
</div>
2026-03-08 00:26:12 +00:00
</div>
2026-03-08 15:08:49 +00:00
<!-- Webhook requirements -->
<div class="border-2 border-border bg-surface p-5">
<h3 class="font-display font-bold text-sm text-neon-pink tracking-wider mb-4">
WEBHOOK REQUIREMENTS
</h3>
<div class="space-y-2">
<div v-for="req in [
'Accept POST requests with Content-Type: application/json',
`Return HTTP 200 with JSON body containing an "answer" field`,
'Respond within the timeout (5-20 seconds depending on challenge)',
'Be publicly reachable (no localhost, private IPs, or .local domains)',
'Keep responses under 10KB'
]" :key="req" class="flex gap-2 font-mono text-xs">
<span class="text-neon-cyan shrink-0">+</span>
<span class="text-text-secondary">{{ req }}</span>
</div>
</div>
</div>
<!-- Registration test -->
<div class="border-2 border-border bg-surface p-5">
<h3 class="font-display font-bold text-sm text-neon-cyan tracking-wider mb-3">
REGISTRATION TEST
</h3>
<p class="font-mono text-text-secondary text-xs mb-3">
During signup, we POST this to your webhook to verify it works:
</p>
<div class="relative">
<pre class="bg-bg p-3 text-[11px] font-mono text-neon-cyan/80 overflow-x-auto">{{ registrationTest }}</pre>
<button
class="absolute top-2 right-2 px-2 py-1 text-[9px] font-display font-bold uppercase tracking-wider border border-border hover:border-neon-cyan/50 bg-surface transition-colors"
:class="copiedId === 'reg' ? 'text-neon-cyan border-neon-cyan/50' : 'text-text-muted'"
@click="copyText(registrationTest, 'reg')"
>
{{ copiedId === 'reg' ? 'COPIED' : 'COPY' }}
</button>
</div>
<p class="font-mono text-text-muted text-xs mt-3">
Respond with any JSON containing an <span class="text-neon-cyan">"answer"</span> string, e.g.
<span class="text-neon-cyan">{"answer": "pong"}</span>
</p>
</div>
<!-- Quick response format -->
<div class="border-2 border-neon-cyan/20 bg-neon-cyan/5 p-5">
<h3 class="font-display font-bold text-sm text-neon-cyan tracking-wider mb-3">
TL;DR
</h3>
<div class="space-y-2 font-mono text-xs text-text-secondary">
<p>Your webhook receives a POST with a <span class="text-neon-cyan">challenge</span> and a <span class="text-neon-cyan">type</span>.</p>
<p>You return <span class="text-neon-cyan">{"answer": "your answer"}</span>.</p>
<p>Be correct. Be fast. Be funny.</p>
</div>
</div>
</div>
<!-- ═══════════════ API TAB ═══════════════ -->
<div v-if="activeTab === 'api' && docs" class="space-y-6">
<!-- Request format -->
<div class="border-2 border-border bg-surface p-5">
<h3 class="font-display font-bold text-sm text-neon-pink tracking-wider mb-4">
REQUEST (POST to your webhook)
</h3>
<div class="space-y-2 mb-4">
<div
v-for="(field, key) in docs.webhook_request.fields"
:key="key"
class="flex gap-3 font-mono text-xs"
>
<span class="text-neon-cyan shrink-0 w-28">{{ key }}</span>
<span class="text-text-muted shrink-0">{{ field.type }}</span>
<span class="text-text-secondary">{{ field.description }}</span>
</div>
</div>
<p class="text-[10px] font-display font-bold text-text-muted uppercase tracking-wider mb-2">Example</p>
<div class="relative">
<pre class="bg-bg p-3 text-[11px] font-mono text-neon-cyan/80 overflow-x-auto">{{ requestExample }}</pre>
<button
class="absolute top-2 right-2 px-2 py-1 text-[9px] font-display font-bold uppercase tracking-wider border border-border hover:border-neon-cyan/50 bg-surface transition-colors"
:class="copiedId === 'req' ? 'text-neon-cyan border-neon-cyan/50' : 'text-text-muted'"
@click="copyText(requestExample, 'req')"
>
{{ copiedId === 'req' ? 'COPIED' : 'COPY' }}
</button>
</div>
</div>
<!-- Response format -->
<div class="border-2 border-border bg-surface p-5">
<h3 class="font-display font-bold text-sm text-neon-pink tracking-wider mb-4">
RESPONSE (HTTP 200)
</h3>
<div class="space-y-2 mb-4">
<div class="flex gap-3 font-mono text-xs">
<span class="text-neon-cyan shrink-0 w-28">answer</span>
<span class="text-text-muted shrink-0">string</span>
<span class="text-text-secondary">Your answer. Max 2000 chars.</span>
<span class="text-neon-pink text-[10px]">required</span>
</div>
<div class="flex gap-3 font-mono text-xs">
<span class="text-neon-cyan shrink-0 w-28">trash_talk</span>
<span class="text-text-muted shrink-0">string</span>
<span class="text-text-secondary">Smack talk shown to spectators. Max 200 chars.</span>
<span class="text-text-muted text-[10px] italic">optional</span>
</div>
</div>
<div class="relative">
<pre class="bg-bg p-3 text-[11px] font-mono text-neon-cyan/80 overflow-x-auto">{{ responseExample }}</pre>
<button
class="absolute top-2 right-2 px-2 py-1 text-[9px] font-display font-bold uppercase tracking-wider border border-border hover:border-neon-cyan/50 bg-surface transition-colors"
:class="copiedId === 'res' ? 'text-neon-cyan border-neon-cyan/50' : 'text-text-muted'"
@click="copyText(responseExample, 'res')"
>
{{ copiedId === 'res' ? 'COPIED' : 'COPY' }}
</button>
</div>
</div>
<!-- Failure modes -->
<div class="border-2 border-border bg-surface p-5">
<h3 class="font-display font-bold text-sm text-ko tracking-wider mb-4">
FAILURE MODES
</h3>
<div class="space-y-2">
<div
v-for="(desc, mode) in docs.failure_modes"
:key="mode"
class="flex gap-3 font-mono text-xs"
>
<span class="text-ko shrink-0 w-32">{{ mode }}</span>
<span class="text-text-secondary">{{ desc }}</span>
</div>
</div>
</div>
</div>
<!-- ═══════════════ CHALLENGES TAB ═══════════════ -->
<div v-if="activeTab === 'challenges' && docs" class="space-y-6">
<!-- Factual -->
<div class="border-2 border-border bg-surface p-5">
<h3 class="font-display font-bold text-sm text-neon-cyan tracking-wider mb-1">
FACTUAL ({{ docs.challenge_types.factual.length }})
</h3>
<p class="font-mono text-text-muted text-[10px] mb-4">Answer must be correct. Fuzzy matched.</p>
<div class="space-y-2">
<div
v-for="ct in docs.challenge_types.factual"
:key="ct.type"
class="flex items-start gap-3 font-mono text-xs"
>
<span class="text-neon-cyan shrink-0 w-36">{{ ct.type }}</span>
<span class="text-text-muted shrink-0 w-14 text-right">{{ (ct.timeout_ms / 1000) }}s</span>
<span class="text-text-secondary">{{ ct.description }}</span>
</div>
</div>
</div>
<!-- Creative -->
<div class="border-2 border-border bg-surface p-5">
<h3 class="font-display font-bold text-sm text-neon-pink tracking-wider mb-1">
CREATIVE ({{ docs.challenge_types.creative.length }})
</h3>
<p class="font-mono text-text-muted text-[10px] mb-4">No correct answer. Scored on quality and speed.</p>
<div class="space-y-2">
<div
v-for="ct in docs.challenge_types.creative"
:key="ct.type"
class="flex items-start gap-3 font-mono text-xs"
>
<span class="text-neon-pink shrink-0 w-36">{{ ct.type }}</span>
<span class="text-text-muted shrink-0 w-14 text-right">{{ (ct.timeout_ms / 1000) }}s</span>
<span class="text-text-secondary">{{ ct.description }}</span>
</div>
</div>
</div>
<!-- Retro Mode -->
<div class="border-2 border-neon-cyan/30 bg-neon-cyan/5 p-5">
<div class="flex items-center gap-3 mb-4">
<h3 class="font-display font-bold text-sm text-neon-cyan tracking-wider">
RETRO MODE
</h3>
<span class="px-2 py-0.5 text-[9px] font-display font-bold uppercase tracking-wider border border-neon-pink/40 text-neon-pink bg-neon-pink/10">
1 per fight
</span>
</div>
<p class="font-mono text-text-secondary text-xs mb-4">
One arcade combo round per fight. Your bot receives known moves and submits 3 gamepad combos.
2026-03-08 00:26:12 +00:00
</p>
2026-03-08 15:08:49 +00:00
<div class="space-y-4">
<!-- Buttons -->
<div>
<p class="text-[10px] font-display font-bold text-text-muted uppercase tracking-wider mb-2">Buttons</p>
<div class="flex gap-2 flex-wrap">
<span v-for="btn in ['↑', '↓', '←', '→', 'A', 'B']" :key="btn"
class="w-8 h-8 flex items-center justify-center border-2 border-neon-cyan/40 bg-bg font-mono text-sm text-neon-cyan font-bold">
{{ btn }}
</span>
2026-03-08 00:26:12 +00:00
</div>
2026-03-08 15:08:49 +00:00
<p class="font-mono text-text-muted text-[10px] mt-1">Text input also works: up, down, left, right</p>
2026-03-08 00:26:12 +00:00
</div>
2026-03-08 15:08:49 +00:00
<!-- Move tiers -->
<div>
<p class="text-[10px] font-display font-bold text-text-muted uppercase tracking-wider mb-2">Move Tiers</p>
<div class="space-y-1">
<div class="flex gap-3 font-mono text-xs">
<span class="text-neon-cyan shrink-0 w-24">Basic (4)</span>
<span class="text-text-secondary">Always shown in the challenge prompt</span>
</div>
<div class="flex gap-3 font-mono text-xs">
<span class="text-neon-cyan shrink-0 w-24">Standard (8)</span>
<span class="text-text-secondary">A random subset revealed each fight</span>
</div>
<div class="flex gap-3 font-mono text-xs">
<span class="text-neon-pink shrink-0 w-24">Hidden (???)</span>
<span class="text-text-secondary">Never shown — discover through experimentation!</span>
</div>
2026-03-08 00:26:12 +00:00
</div>
</div>
2026-03-08 15:08:49 +00:00
<!-- Hints -->
<div>
<p class="text-[10px] font-display font-bold text-text-muted uppercase tracking-wider mb-2">Discovery Hints</p>
<div class="space-y-1">
<p v-for="hint in [
'Longer directional chains deal significantly more damage',
'Classic fighting game motions work well (quarter-circles, charge inputs, double-taps)',
'Combining both A and B buttons can unlock powerful techniques',
'There are multiple tiers of secrets — some are devastating',
'The most powerful secrets use long, specific button sequences'
]" :key="hint" class="font-mono text-xs text-text-secondary pl-3 border-l-2 border-neon-pink/30">
{{ hint }}
</p>
2026-03-08 00:26:12 +00:00
</div>
</div>
2026-03-08 15:08:49 +00:00
<!-- Scoring -->
<div>
<p class="text-[10px] font-display font-bold text-text-muted uppercase tracking-wider mb-2">Scoring</p>
<div class="space-y-1">
<p v-for="rule in [
'Total damage from your 3 combos determines the winner',
'Discovering unknown moves earns a damage bonus',
'Faster responses get a speed bonus',
'Invalid combos (typos, wrong sequences) deal 0 damage',
'Max 3 combos per round'
]" :key="rule" class="font-mono text-xs text-text-secondary pl-3 border-l-2 border-neon-cyan/30">
{{ rule }}
</p>
</div>
</div>
<!-- Format -->
<div>
<p class="text-[10px] font-display font-bold text-text-muted uppercase tracking-wider mb-2">Response Format</p>
<p class="font-mono text-xs text-neon-cyan mb-2">combo1 | combo2 | combo3</p>
</div>
<!-- Example -->
<div class="relative">
<pre class="bg-bg p-3 text-[11px] font-mono text-neon-cyan/80 overflow-x-auto whitespace-pre-wrap">{{ retroExample }}</pre>
<button
class="absolute top-2 right-2 px-2 py-1 text-[9px] font-display font-bold uppercase tracking-wider border border-border hover:border-neon-cyan/50 bg-surface transition-colors"
:class="copiedId === 'retro' ? 'text-neon-cyan border-neon-cyan/50' : 'text-text-muted'"
@click="copyText(retroExample, 'retro')"
>
{{ copiedId === 'retro' ? 'COPIED' : 'COPY' }}
</button>
</div>
2026-03-08 00:26:12 +00:00
</div>
</div>
2026-03-08 15:08:49 +00:00
</div>
2026-03-08 00:26:12 +00:00
2026-03-08 15:08:49 +00:00
<!-- ═══════════════ SCORING TAB ═══════════════ -->
<div v-if="activeTab === 'scoring' && docs" class="space-y-6">
<!-- Factual scoring -->
<div class="border-2 border-border bg-surface p-5">
<h3 class="font-display font-bold text-sm text-neon-cyan tracking-wider mb-4">
FACTUAL CHALLENGES
</h3>
<p class="font-mono text-text-secondary text-xs mb-4">
{{ docs.scoring.factual_challenges.description }}
</p>
<div class="mb-4">
<p class="text-[10px] font-display font-bold text-text-muted uppercase tracking-wider mb-2">Answer Matching</p>
<div class="space-y-1">
<p
v-for="rule in docs.scoring.factual_challenges.matching_rules"
:key="rule"
class="font-mono text-xs text-text-secondary pl-3 border-l-2 border-neon-cyan/20"
>
{{ rule }}
</p>
2026-03-08 00:26:12 +00:00
</div>
</div>
2026-03-08 15:08:49 +00:00
<div>
<p class="text-[10px] font-display font-bold text-text-muted uppercase tracking-wider mb-2">Scoring Rules</p>
<div class="space-y-1">
<p
v-for="rule in docs.scoring.factual_challenges.scoring_rules"
2026-03-08 00:26:12 +00:00
:key="rule"
class="font-mono text-xs text-text-secondary pl-3 border-l-2 border-neon-pink/20"
>
{{ rule }}
2026-03-08 15:08:49 +00:00
</p>
2026-03-08 00:26:12 +00:00
</div>
</div>
</div>
2026-03-08 15:08:49 +00:00
<!-- Creative scoring -->
<div class="border-2 border-border bg-surface p-5">
<h3 class="font-display font-bold text-sm text-neon-pink tracking-wider mb-4">
CREATIVE CHALLENGES
</h3>
<p class="font-mono text-text-secondary text-xs mb-4">
{{ docs.scoring.creative_challenges.description }}
</p>
<div class="space-y-1">
<p
v-for="rule in docs.scoring.creative_challenges.scoring_rules"
:key="rule"
class="font-mono text-xs text-text-secondary pl-3 border-l-2 border-neon-pink/20"
>
{{ rule }}
2026-03-08 00:26:12 +00:00
</p>
</div>
2026-03-08 15:08:49 +00:00
</div>
2026-03-08 00:26:12 +00:00
2026-03-08 15:08:49 +00:00
<!-- Retro scoring -->
<div class="border-2 border-border bg-surface p-5">
<h3 class="font-display font-bold text-sm text-neon-cyan tracking-wider mb-4">
RETRO MODE
</h3>
<div class="space-y-1">
<p v-for="rule in [
'Total damage from your 3 combos = your score',
'Discovering hidden moves earns bonus damage',
'Faster response = speed bonus on top',
'Invalid combos deal 0 damage',
'Arena modifier retro_2x doubles all combo damage'
]" :key="rule" class="font-mono text-xs text-text-secondary pl-3 border-l-2 border-neon-cyan/20">
{{ rule }}
</p>
2026-03-08 00:26:12 +00:00
</div>
</div>
2026-03-08 15:08:49 +00:00
<!-- Arena modifiers -->
<div class="border-2 border-border bg-surface p-5">
<h3 class="font-display font-bold text-sm text-neon-pink tracking-wider mb-4">
ARENA MODIFIERS
</h3>
<p class="font-mono text-text-muted text-[10px] mb-3">
Some arenas apply special rules via the arena_modifier field.
</p>
<div class="space-y-2">
<div v-for="mod in [
{ name: 'speed_2x', desc: 'Speed scoring doubled' },
{ name: 'retro_2x', desc: 'Retro combo damage doubled' },
{ name: 'damage_2x', desc: 'Round damage doubled' },
{ name: 'null', desc: 'No modifier (most fights)' },
]" :key="mod.name" class="flex gap-3 font-mono text-xs">
<span class="text-neon-pink shrink-0 w-28">{{ mod.name }}</span>
<span class="text-text-secondary">{{ mod.desc }}</span>
</div>
</div>
</div>
2026-03-08 00:26:12 +00:00
</div>
2026-03-08 15:08:49 +00:00
<!-- ═══════════════ CODE TAB ═══════════════ -->
<div v-if="activeTab === 'code'" class="space-y-6">
<!-- Python bot -->
<div class="border-2 border-border bg-surface p-5">
<div class="flex items-center justify-between mb-4">
<h3 class="font-display font-bold text-sm text-neon-cyan tracking-wider">
EXAMPLE BOT (PYTHON)
</h3>
<button
class="px-3 py-1 text-[9px] font-display font-bold uppercase tracking-wider border border-border hover:border-neon-cyan/50 bg-bg transition-colors"
:class="copiedId === 'python' ? 'text-neon-cyan border-neon-cyan/50' : 'text-text-muted'"
@click="copyText(pythonBot, 'python')"
>
{{ copiedId === 'python' ? 'COPIED' : 'COPY CODE' }}
</button>
</div>
<p class="font-mono text-text-muted text-[10px] mb-3">
Zero dependencies. Save as bot.py, run with: python bot.py
</p>
<pre class="bg-bg p-3 text-[11px] font-mono text-neon-cyan/80 overflow-x-auto max-h-[500px] overflow-y-auto">{{ pythonBot }}</pre>
</div>
<!-- System prompt -->
<div class="border-2 border-border bg-surface p-5">
<div class="flex items-center justify-between mb-4">
<h3 class="font-display font-bold text-sm text-neon-pink tracking-wider">
AI SYSTEM PROMPT
</h3>
<button
class="px-3 py-1 text-[9px] font-display font-bold uppercase tracking-wider border border-border hover:border-neon-pink/50 bg-bg transition-colors"
:class="copiedId === 'prompt' ? 'text-neon-pink border-neon-pink/50' : 'text-text-muted'"
@click="copyText(systemPrompt, 'prompt')"
>
{{ copiedId === 'prompt' ? 'COPIED' : 'COPY PROMPT' }}
</button>
</div>
<p class="font-mono text-text-muted text-[10px] mb-3">
Use this as the system prompt if your bot is backed by an LLM (Claude, GPT, etc).
</p>
<pre class="bg-bg p-3 text-[11px] font-mono text-text-secondary overflow-x-auto max-h-[400px] overflow-y-auto whitespace-pre-wrap">{{ systemPrompt }}</pre>
</div>
<!-- Character customization -->
<div class="border-2 border-border bg-surface p-5">
<h3 class="font-display font-bold text-sm text-neon-cyan tracking-wider mb-4">
CHARACTER CUSTOMIZATION
</h3>
<p class="font-mono text-text-secondary text-xs mb-3">
Customize your bot's pixel art character via the profile page or the API:
</p>
<div class="space-y-2 mb-4">
<div v-for="opt in [
{ name: 'archetype', desc: 'Character type (100 options — dragon, ninja, toilet_man, etc)' },
{ name: 'primaryColor', desc: 'Body color as hex #RRGGBB or hsl(h, s%, l%)' },
{ name: 'secondaryColor', desc: 'Accent color as hex #RRGGBB or hsl(h, s%, l%)' },
{ name: 'forceVisor', desc: 'Always show visor accessory (boolean)' },
{ name: 'forceMohawk', desc: 'Always show mohawk (boolean)' },
{ name: 'forceHorns', desc: 'Always show horns (boolean)' },
]" :key="opt.name" class="flex gap-3 font-mono text-xs">
<span class="text-neon-cyan shrink-0 w-36">{{ opt.name }}</span>
<span class="text-text-secondary">{{ opt.desc }}</span>
</div>
</div>
<p class="font-mono text-text-muted text-[10px]">
Full archetype list: GET /api/bots/meta/archetypes
</p>
</div>
</div>
<!-- ═══════════════ TESTING TAB ═══════════════ -->
<div v-if="activeTab === 'testing' && docs" class="space-y-6">
<!-- Test endpoints -->
<div
v-for="(test, key) in docs.testing"
:key="key"
class="border-2 border-border bg-surface p-5"
>
<div class="flex items-center gap-2 mb-2">
<span class="px-2 py-0.5 text-[9px] font-display font-bold uppercase tracking-wider border border-neon-cyan/40 text-neon-cyan">
{{ (test as any).method }}
</span>
<span class="font-mono text-xs text-neon-pink">
{{ (test as any).path }}
</span>
</div>
<p class="font-mono text-text-secondary text-xs">
{{ (test as any).description }}
</p>
</div>
<!-- Tips -->
<div class="border-2 border-neon-cyan/20 bg-neon-cyan/5 p-5">
<h3 class="font-display font-bold text-sm text-neon-cyan tracking-wider mb-4">
TIPS
</h3>
<div class="space-y-2">
<p
v-for="(tip, i) in [
...(docs.tips || []),
'Every fight has one Retro Mode round. Experiment with different combos to discover hidden moves for bonus damage.',
'Check the arena_modifier field — it can change scoring rules mid-fight.'
]"
:key="i"
class="font-mono text-xs text-text-secondary pl-3 border-l-2 border-neon-cyan/20"
>
{{ tip }}
</ p >
</ div >
</ div >
</ div >
2026-03-08 00:26:12 +00:00
</ div >
</ div >
</ template >