Underground 2 Revived
Play Need for Speed: Underground 2 (2004) in Chrome and Firefox with WebGPU graphics, Web Audio, up to 120 fps widescreen, controller support, free roam and Online races with friends. Fan project, not
翻譯準備中
個人 GitHub 帳號下公開儲存庫的擁有者可以登入並驗證所有權,自行管理介紹,無需管理員核准。
登入並產生驗證檔案,在 30 分鐘內將其提交至此儲存庫的預設分支。其他分支或分叉儲存庫中的檔案無效。然後在下方檢查檔案,即可無需管理員核准直接管理專案。請保留檔案以維持管理權限。
如需登入,將前往登入頁面。登入後請回到此處,再次點擊所選的驗證按鈕。
在專案資料夾中執行 /nomorevibe verify,即可驗證所有權並自行管理介紹。
一分鐘完成驗證Discord chatbot impl for MTG judge calls with RAG
翻譯準備中
取自儲存庫 README
正在載入完整 README。 以下為已儲存的摘錄。
A Discord bot and anonymous web page that answers Magic: The Gathering rules questions
like a judge. Ask in plain language. Nicknames such as "bob", "goyf" and "snappy" work.
The reply is a concise ruling in which every claim is backed by a validated, clickable
citation: a Comprehensive Rules section, an official Scryfall ruling, the card's
current Oracle text, or a prior rated call. On Discord, buttons rate an answer 1–3
(incorrect / partially correct / correct), and ratings feed back into how future answers
are grounded.
The same pipeline behind the web page:檢視完整 README ↗A Discord bot and web app that answers Magic: The Gathering rules questions like a judge. Ask in plain language. Nicknames such as "bob", "goyf" and "snappy" work. Every claim in the answer is backed by a checked, clickable citation: a Comprehensive Rules section, an official Scryfall ruling, the card's current Oracle text, or a past answer. On Discord, members rate answers as correct, partially correct or incorrect, and ratings decide which past answers new ones draw on as examples. A rating that finds fault says why, and the reason goes to the operator's feedback queue to be fixed.
The same answer in the web app:
More answers, copied verbatim from a published evaluation run, are on the Sample answers page.
[[Full Card Name]] in brackets matches that
exact name (or a listed nickname) only. Answers show which cards they used. The bot
never guesses: an ambiguous name ("Tibalt") gets a "Did you mean…?" prompt.Each community runs its own judgebot: one compose file, a model API key, and a Discord application you create in a few minutes.
| Cost per answer | $0.07–0.15 on the default model (Claude Opus 5.5). A judge.toml can switch to a cheaper or a local model. |
| Cost per month | About $20–45 for ten questions a day. Nothing else costs money. JUDGE_MAX_USD is a hard cap, and JUDGE_BUDGET_PERIOD=month makes it a monthly budget. |
| Accounts | A model provider (Anthropic by default). Optional: Voyage AI for semantic search (a few cents, once), and Discord for the bot. |
| Host | Any machine that runs Docker and stays on, with about 200 MB of RAM to spare. Images are published for amd64 and arm64, so nothing is compiled. |
| Setup | Six commands. The first data load downloads about 110 MB from Scryfall and takes about a minute on a fast connection. |
You don't need Discord to start: the web app and the command line work without a bot token. The documentation site, https://mtg-judgebot.rpeters.dev/, has the long form of everything below and explains how the judge works.
You need Docker with the compose plugin, and a model API key.
git clone https://github.com/sloshy/mtg-judgebot && cd mtg-judgebot
docker compose pull # the published image, amd64 and arm64
scripts/config.sh # the config editor: open the URL it prints, set ANTHROPIC_API_KEY,
# JUDGE_OPERATOR_EMAIL (a support address the app shows) and
# JUDGE_ROLES to '--api --web --jobs' (no Discord yet), save, Ctrl-C.
# VOYAGE_API_KEY turns on semantic search
scripts/deploy.sh --db # starts Postgres with pgvector, on localhost:5432
docker compose run --rm refresh init # the first data load: schema, cards, rules, nicknames, notes and
# embeddings if keyed. Safe to rerun. judgebot refreshes the data
# daily after that (JUDGE_REFRESH_HOURS)
scripts/deploy.sh # judgebot: the web app and the daily refresh
Open http://localhost:8787 and ask a question. Each address can ask 4 questions per 5
minutes by default (API_RATE_LIMIT). If port 5432 is taken, set DB_PORT and change
DATABASE_URL to match.
The config editor checks every change the same way the bot does at startup, and tells you
what would fail and why. To edit by hand instead, cp .env.example .env and fill it in.
Every variable is documented in the file.
Every model call, embeddings included, counts against a hard cap, JUDGE_MAX_USD
(default $5):
JUDGE_BUDGET_PERIOD=day or month makes it a daily or monthly budget that survives
restarts.JUDGE_ALERT_WEBHOOK is notified. JUDGE_SPEND_WARN_AT=50,80 warns
there at those percentages of the cap first.docker compose pull fetches the image CI builds from upstream main. To run your own
checkout instead, skip it and run scripts/deploy.sh --build (takes minutes and about
4 GB of RAM). CONTRIBUTING.md covers working from source with Rust,
where docker compose run --rm refresh <command> becomes
cargo run --release -p judgebot -- ingest <command>.
Every judgebot is its own Discord application, owned by whoever runs it. Create one in the Discord Developer Portal (Discord's Building your first Discord Bot walks through the portal). Then:
.env as DISCORD_TOKEN. Leave every
Privileged Gateway Intent off: the bot only receives its own slash commands and
button clicks, never messages. Put your own Discord username in .env as
JUDGE_OPERATOR_DISCORD. The bot won't start without it, and /help and /license
show it so people know who runs the bot.bot and applications.commands
and leave the permissions empty. Open the generated URL to add the bot to your server
(you need Manage Server there)..env as GUILD_ID
(User Settings → Advanced → Developer Mode, then right-click the server → Copy
Server ID). The commands then work only in that server. Without it they register
everywhere, which can take up to an hour, and work in every server the bot joins and
in DMs (all but /judge).JUDGE_ROLES (unset, it runs the bot, the web app and the refresh) and run
scripts/deploy.sh. The log line registered /judge, /judge-private, /card, /rule, /help, /license and /forget means it worked. Try /help in your server.Members with the role named by JUDGE_ROLE (default Judge) rate as judges, and their
rating overrides everyone else's. Run docker compose run --rm refresh emoji once to
upload the mana symbols, so answers show pictures instead of {W}.
scripts/deploy.sh is how every change goes live: a new release, an edited .env or
judge.toml, a git pull. It starts the new container beside the running one and
switches once it is healthy, so nobody sees downtime. A change to the database's own
service waits for scripts/deploy.sh --db, which takes it offline for a few seconds:
the script refuses until you run it.
Run your own judgebot has the
long form.
With only .env, the judge uses Anthropic's API with claude-opus-5-5 for both
steps, and Voyage voyage-3.5 for semantic search if VOYAGE_API_KEY is set. The
results below were measured on that setup.
A judge.toml can pick a different model for each step: Anthropic directly, through a
proxy, on Claude Platform on AWS, Bedrock or Vertex, or any OpenAI-compatible server
(OpenAI, LiteLLM, OpenRouter, vLLM, a local Ollama priced free). Embeddings can come from Voyage or an
OpenAI-compatible server. judge.example.toml documents every option, and
Model choice is the guide. Under
Docker, also set JUDGE_CONFIG=./judge.toml in .env.
The config editor (scripts/config.sh) edits both files from a local page. It never
shows a saved secret, only lets you replace it.
The config editor has the
details.
The web app at http://localhost:8787 uses the same pipeline, citations and "did you
mean…?" prompts as the bot. Its ratings are anonymous and never shape later answers. Its /feed
lists answered questions from every interface, except those asked private, and its
/dashboard counts them by day, interface, outcome, rating, topic and card. What a
judgebot runs is set by roles: --discord, --api (the JSON API), --web (the web
app), --mcp (for AI agents), --admin (the operator's pages, on a port of their own)
and --jobs (the data refresh). JUDGE_ROLES in .env lists them, and unset it is
--discord --api --web --jobs.
The web app and
The HTTP API have the details.
The intended setup is a machine you own running the compose stack, with no cloud bill.
Out of the box it answers on 127.0.0.1:8787 only, which is enough for a Discord bot
over its gateway connection. To publish the web app, set COMPOSE_PROFILES=public and
JUDGE_DOMAIN in .env: the bundled Caddy then serves https://<JUDGE_DOMAIN> on ports
80 and 443 with a Let's Encrypt certificate. A reverse proxy of your own, or a Cloudflare
Tunnel
with no open ports, works instead. docs/DEPLOYMENT.md is the runbook. It covers the
three routes, protecting the model bill, and weekly backups to S3-compatible storage
(Cloudflare R2, AWS S3, Backblaze B2, MinIO).
eval/gold.yaml holds 24 questions chosen to be hard (layers, multi-faced cards, errata traps,
Commander, out-of-scope questions, unreleased cards, and one ruling corrected after a wrong
answer was reported) with the rules a correct answer cites and a reference answer. All but
the corrected one and the two about unreleased cards were checked adversarially.
cargo run -p judge-eval -- recall # are the expected rules in the material? (free)
cargo run -p judge-eval -- answer --label x --limit 22 --max-usd 6 # full live run (~$1.70)
cargo run -p judge-eval -- grade eval/runs/x.json # a model grades each answer (~$0.40)
cargo run -p judge-eval -- rescore eval/runs/x.json # re-score a stored run (free)
cargo run -p judge-eval -- show eval/runs/x.json # bot vs. reference, side by side
cargo run -p judge-eval -- sweep eval/sweeps/efforts.toml # models × efforts per stage, averaged
Each question can list alternate rule ids that state the same fact, so the score tracks
whether the answer is right, not whether it picked one author's preferred citation.
grade asks a model whether each ruling follows from its quotes and agrees with the
reference, which is how the Results table below was graded (docs/DECISIONS.md D28). It
is a score, not a pass/fail gate. sweep runs a plan of models and efforts, each several
times, graded by one model, and averages them; eval/sweeps/ holds the plans behind the
table.
The default and four changes from it, each run twice over the 22 questions (19 in scope,
3 out of scope) on 2026-10-22, on CR 2026-09-25 with Voyage voyage-3.5 embeddings. Every
figure is pooled over both runs. Each configuration's judge.toml and both run files are in
eval/published/ (v2-*), with every question, answer, citation, time and cost.
judge-eval show eval/published/v2-default-r1.json prints each answer beside its
reference. The two cheaper models keep the default's extraction and only answer.
Default: claude-opus-5-5 at medium effort, both stages | Extraction at low | Synthesis at xhigh | Synthesis on claude-sonnet-5-5 at low | Synthesis on claude-haiku-5-5 at high | |
|---|---|---|---|---|---|
| Out-of-scope questions declined (of 6) | 6 | 6 | 6 | 6 | 6 |
| In-scope questions answered (of 38) | 38 | 37 | 38 | 38 | 38 |
| …agreeing with the reference ruling | 38 | 37 | 38 | 36 | 35 |
| …partly (right on the main point, a sub-question missed) | 0 | 0 | 0 | 2 | 3 |
| …contradicting the reference | 0 | 0 | 0 | 0 | 0 |
| Asked "did you mean?" instead | 0 | 1 | 0 | 0 | 0 |
| Not answered | 0 | 0 | 0 | 0 | 0 |
| …following from what they cite alone | 21 | 21 | 24 | 12 | 20 |
| Wrong side remarks | 1 | 2 | 0 | 5 | 3 |
| Decisive rule ids cited | 64 of 74 (86%) | 61 of 74 (82%) | 67 of 74 (91%) | 57 of 74 (77%) | 59 of 74 (80%) |
| Supporting rule ids also cited | 34 of 66 | 26 of 66 | 34 of 66 | 32 of 66 | 24 of 66 |
| Cost per in-scope question (median) | $0.087 | $0.086 | $0.099 | $0.046 | $0.009 |
| Time per in-scope question (median / longest) | 13 s / 24 s | 12 s / 29 s | 18 s / 43 s | 9 s / 19 s | 17 s / 39 s |
| Whole run (of 22 questions) | $1.67 | $1.63 | $1.93 | $0.93 | $0.21 |
How to read the table:
judge-eval grade),
reading each answer against the reference under a strict rubric. The references were
written and checked by models, then audited against Oracle text, rulings and the rules,
which found three to correct. No human judge has reviewed either side, so read this as
"no contradiction found", not as measured accuracy. It grades the ruling, not every side
remark. The default made one wrong remark (that you choose the order of Bruna and Gisela
in exile). Sonnet's partial answers misdescribed Opalescence under Humility and missed
the Lion's Eye Diamond line of holding priority. Haiku's never worked through the
trample-and-deathtouch case the Questing Beast question describes, and once wouldn't
name "Tim" (Prodigal Sorcerer). Both said once that Blood Moon leaves Urborg with no
abilities, when it gives it "{T}: Add {R}".Opus 5.5 at medium effort on both stages is the default: every in-scope answer agreed
with the reference, for $0.087 a question. The effort sweep behind it ran each stage alone
at every effort from low to xhigh on all three models, two runs each
(eval/published/v2-efforts-summary.md, eval/sweeps/efforts.toml). Opus agreed with the
reference at every synthesis effort; medium is where its decisive-rule recall levels off,
for 7% over low. At extraction, low and medium found the same cards except where low wrote
part of a name alone, so low is a few percent cheaper with an occasional "did you mean?".
Moving one stage from the default, as the table shows:
The documentation site's Model choice page has each of these as a judge.toml and covers
other ways to save money. The 2026-09-29 and 2026-10-07 runs of the earlier configurations
stay in eval/published/ as v1-*.
Rust was chosen so the compiler enforces correctness. docs/DECISIONS.md records that
choice and the other main design decisions, with the alternatives rejected. In practice:
Verdict<Unvalidated> → validate() → Verdict<Validated>, so an unchecked answer
cannot be stored or shown.docs/ARCHITECTURE.md is the full design reference.
crates/
core domain types, ports, judge() pipeline, citation validation — no I/O
llm provider-neutral chat types, Backend + sealed ChatModel port, spend cap, retry loop, Synth typestate
anthropic the Messages API as a judge-llm backend: wire types, schema transform, endpoints
openai OpenAI-compatible chat completions as a judge-llm backend: strict-schema transform, dialect knobs
embed Voyage and OpenAI-compatible embeddings, each tagged with its vector Space
bot Postgres adapters (resolver / retriever / call store), the Scryfall + Comprehensive Rules loaders
and embedder, the refresh schedule, judge.toml loader, prompts, Discord (own wire types, REST, gateway)
api HTTP adapter (axum): the JSON API, the web app, the /mcp transport and the admin listener
judgebot the one long-running binary: --discord --api --web --mcp --admin --jobs as launch-time roles, and
`judgebot ingest` (init, refresh, cards, rules, embed, …) over the loaders (bin)
eval gold-set harness: recall / answer / grade / rescore / show (bin)
agent the judge for other agents: sessions, lookups and the pipeline as judge-cli and judge-mcp
configure judge-config: a localhost page editing judge.toml and .env, checked by the loaders (bin)
web/ SolidJS + TypeScript web app (Vite)
site/ the documentation site (Astro + Starlight); docs/ is its source
data/ categories.yaml (generates the Category enum), aliases.yaml, notes.yaml
eval/ gold.yaml, published/ (graded runs), runs/ (yours, gitignored)
docs/ EXPLAINER.md (the tour), ARCHITECTURE.md (the reference), DECISIONS.md (why),
PROVIDERS.md (the model-provider reference), DEPLOYMENT.md (the runbook)
One process is one spend cap, one judge role and one Discord application, by design
(docs/DECISIONS.md D16). Tournament policy (MTR/IPG) is not covered: the bot declines
those questions rather than guessing.
AGPL-3.0-or-later. See LICENSE. If you run a modified version as a network service (Discord, the HTTP API or MCP), you must make your modified source available to its users. The bot does this for you: every remote interface shows the licence, the copyright, the source repository and the commit it was built from:
GET /api/about/help and /licenseabout tooljudge-cli aboutCI stamps the commit into the published image, and git rev-parse HEAD stamps it into a
local build. If you change anything, set JUDGE_SOURCE_URL in .env to the repository
with your changes, and every interface points there. That covers your obligation under
section 13.
The same interfaces show who runs the instance. The bot requires
JUDGE_OPERATOR_DISCORD (a Discord username) and the web app, API and MCP require
JUDGE_OPERATOR_EMAIL (a support address). Each shows the other contact too when it is
set. judge-cli and judge-mcp on stdio need neither.
This is unofficial Fan Content permitted under Wizards of the Coast's Fan Content
Policy, not approved or
endorsed by Wizards. Magic: The Gathering, the Comprehensive Rules, card text and
rulings are © Wizards of the Coast. Card data and rulings come from
Scryfall under its data guidelines.
An instance downloads both when it loads its data (judgebot ingest). The repository
carries only a short excerpt of the rules as a test fixture. Rule links go to the
independent Yawgatog rules mirror.
NOTICE has the full statement. The web app and every page of the documentation site
repeat this notice in their footers, and the bot's /help gives a short form of it.