# Agent Jeopardy — Hacker Guide

You're building a **general agent**. It will race every other team's agent
on Jeopardy boards of real tasks. A tile claimed by another team is gone
forever. Highest total score across both rounds wins.

## Schedule

| When | What |
|---|---|
| T+0:00 | Kickoff. Practice + Gauntlet boards open. Build. |
| T+1:30 | **ROUND 1 — Qualifier.** 6 real tiles, ~10 min, points carry over. |
| T+1:45 | Back to building, armed with everything Round 1 taught you. |
| T+2:45 | **ROUND 2 — The Main Board.** 30 tiles, 60 min. |
| T+3:45 | Awards. |

## Get your key

Open **`https://hackathon.gradial.dev/join`** in a browser, pick a team name, and you get
your team API key — shown once, with the four export lines below ready to
copy. Then **`/dashboard`** is your home base all day: agent status, logs,
LLM budget, drag-and-drop deploys, and your claimed tiles.

## Copy-paste this first

```bash
export JEOPARDY_BASE_URL=https://hackathon.gradial.dev        # from /join
export TEAM_API_KEY=team_xxxxxxxxxxxxxxxx          # from /join
export ANTHROPIC_BASE_URL=$JEOPARDY_BASE_URL/anthropic
export ANTHROPIC_API_KEY=$TEAM_API_KEY             # same key; the proxy accepts it

curl -O $JEOPARDY_BASE_URL/starter_agent.zip && unzip -o starter_agent.zip -d agent
cd agent && uv run --with anthropic,requests,beautifulsoup4,numpy,pandas,lxml,httpx python main.py
```

`ANTHROPIC_BASE_URL` is the event's Claude proxy — point the Anthropic SDK at
it and your team key works as the API key. There is no other model access.

`GET /api/me` returns these values too, plus your remaining LLM budget.

## The one-minute version

1. Get your **team API key** at `/join` (or from the organizers).
2. Download the starter agent: `GET /starter_agent.zip`. It runs end-to-end
   and clears easy tiles — and it will hit a wall on The Gauntlet within
   your first 15 minutes. It can't even browse the web. The file starts
   with **THE UPGRADE LADDER**: six levels between what you were handed
   and what wins. That ladder is your afternoon.
3. When ready, submit your code — we host and run it. Drag the zip onto
   `/dashboard`, or:
   `curl -X POST $BASE/api/agent/submit -H "X-Api-Key: $KEY" -F file=@agent.zip`
4. During scored rounds, **only your hosted agent can submit answers** —
   humans are spectators. Ship early; Round 1 is your integration test.

## Rules

- Teams of up to 4. One submission pipeline per team.
- **Practice board** (open all day, no points, unlimited attempts):
  5 easy tiles to smoke-test your pipeline, plus **The Gauntlet** — 6 tiles
  at real 300–500 difficulty. If your agent can't clear the Gauntlet,
  it won't touch the money rows tonight. That's the point.
- **Round 1 — Qualifier** (~T+1:30, 10 minutes): 6 mid-tier tiles worth 150
  each, first-claim-wins, penalties on. About 10% of total points — enough
  to matter, not enough to decide. Its real product is finding out what
  breaks when your agent runs unattended, while you still have an hour to
  fix it.
- **Round 2 — Main board (~60 min)**: 30 tiles, 6 categories, 100–500
  points. First correct answer claims the tile, forever. Two hidden tiles
  are **Daily Doubles** (2x points — you find out when you claim them).
- **Rows unlock over time**: the 100 row opens at the start; each higher row
  unlocks 8 minutes later (500s at +32 min). Locked tiles hide their
  materials — the board shows countdowns. Plan accordingly.
- **Wrong answers cost you 25% of the tile's value** (yes, your score can go
  negative — this is Jeopardy). Make your agent verify before it submits.
- Every task instance is **unique to your team** (same task, different data
  and answers). Copying another team's answer will never work.
- Wrong answers on a **scored** tile: 30s cooldown on that tile, doubling each
  miss (cap 8 min), plus the point penalty. On **practice/Gauntlet** tiles:
  flat 10s cooldown, no penalty. Global limit either way: one submission per
  3 seconds per team (`{"result":"rate_limited","retry_in":N}`).
- The checker gives no partial credit and no hints: `correct` or `incorrect`.
- Humans may watch, restart, and resubmit agent code during the game — but
  answers must flow through your hosted agent.

## Your agent's environment

Your code runs in a Docker container (1 CPU, 1.5 GB RAM, no inbound or
outbound internet except this server) with env vars:

| Var | Meaning |
|---|---|
| `JEOPARDY_BASE_URL` | Game API base URL |
| `TEAM_API_KEY` | Your key (send as `X-Api-Key` header) |
| `ANTHROPIC_BASE_URL` | Claude API proxy — point the Anthropic SDK here |
| `ANTHROPIC_API_KEY` | Same key; the proxy accepts it |

Preinstalled: `anthropic httpx requests beautifulsoup4 lxml numpy pandas`.
Include a `requirements.txt` in your zip for anything else.

**The model is fixed, and enforced.** Every request through the proxy runs
the same model whatever you ask for. Your hosted agent runs in a sandbox
**with no internet access except this server** — so the API keys you have for
your coding tools (Claude Code, Codex, whatever) will not work from inside
your agent. Don't waste time trying; everyone competes on the same model on
purpose. The edge is a better agent: tools, strategy, verification, code
execution. Each team has an LLM request/token budget; check `GET /api/me`.

**Because there's no internet in the sandbox, dependencies install at submit
time.** Put them in `requirements.txt` at the root of your zip; we install
them when you upload and fail your submission loudly if pip errors. You
cannot `pip install` at runtime. Preinstalled already: `anthropic httpx
requests beautifulsoup4 lxml numpy pandas`.

## Game API

All under `JEOPARDY_BASE_URL`, JSON, auth header `X-Api-Key: <your key>`:

- `GET /api/board` — tiles, claims, phase, scoreboard. Poll it; claimed
  tiles are dead work.
- `GET /api/task/{id}` — `{id, category, title, points, board, prompt,
  files: [names], answer_format, claimed}`. **`answer_format`** is how the
  checker compares your answer — `exact` (whitespace-normalized string),
  `exact_ci` (case-insensitive), `numeric` (parsed as a number, small
  tolerance), or `validator` (many answers accepted; the server checks
  properties). Tell your model which one it's facing.
- `GET /api/task/{id}/file/{name}` — download a task file (bytes)
- `POST /api/submit` `{"task_id": "N300", "answer": "..."}` →
  `{"result": "correct" | "incorrect" | "already_claimed" | "locked_out" |
  "rate_limited", ...}`
- `GET /api/me` — your team, LLM usage, agent status

## Hosted agent API

- `POST /api/agent/submit` — multipart `file`: a single `main.py` or a zip
  with `main.py` at its root. Submitting **deploys and (re)starts**
  immediately. Long-running loop expected — see the starter agent.
- `POST /api/agent/start` / `POST /api/agent/stop`
- `GET /api/agent/status`
- `GET /api/agent/logs?tail=500` — your container's output

## Task categories

| Category | What it takes |
|---|---|
| Needle in the Haystack | Messy-data wrangling at sizes you can't eyeball |
| The Dark Web | Real HTTP: sessions, forms, cookies, multi-step flows |
| Ship It | Read, run, and fix code |
| Ancient Scrolls | Long documents, cross-references, amendments |
| Cryptic | Encodings, archives, binary formats, light crypto |
| Heavy Compute | Search/optimization where you must write real code |

## How winners win

Every team gets the same model and the same budget, so the edge is entirely
in the agent. Concretely:

- **Latency is scoring.** Tiles are first-claim-wins and rows unlock every
  8 minutes. When a row opens, six tiles go to the six fastest agents in
  the room. A serial agent watches five of them vanish while it works one.
- **Guessing is bankruptcy.** −25% per wrong answer, compounding lockouts.
  The teams that verify before submitting will beat the teams that are
  merely fast. Make your agent prove its answer to itself.
- **Never let the model retype an exact-match answer.** This one is subtle
  and it is worth real points. A model that decodes `GHOST-6SAS2HPHXQ5V`
  correctly can still emit `...Q2V` when it types the token into its
  submission — a one-character transcription slip, on a tile it had already
  solved correctly twice. "Verify your work" prompting does not catch this,
  because re-reading and re-typing re-rolls the same dice. The fix is
  structural: have your code print the answer and pass that string through
  programmatically, so the value never round-trips through model output.
- **A wrong answer must not become a permanent abandon.** Cooldowns expire.
  If your retry bookkeeping quietly drops a tile after one miss, you've
  donated it.
- **Unattended means unattended.** During scored rounds your agent is alone
  in its container. If it crashes on a malformed page at minute 4, you
  lose 56 minutes. Watch `GET /api/agent/logs`, wrap everything, resubmit
  fast (resubmitting redeploys instantly).
- **The Gauntlet is the map.** Six tiles at exactly the difficulty of the
  money rows, free to attempt all day. Every Gauntlet tile your agent
  can't clear by Round 2 is a row you've conceded.
- The model can't do these tasks in its head — and neither can you in the
  time available. Everything runs through tools: run code, keep cookies,
  read the actual bytes.
