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 发布版本后会显示在这里。
个人 GitHub 账号下公开仓库的所有者可以登录并验证所有权,自行管理介绍,无需管理员批准。
登录并生成验证文件,在 30 分钟内将其提交到此仓库的默认分支。其他分支或分叉仓库中的文件无效。然后在下方检查文件,即可无需管理员批准直接管理项目。请保留文件以维持管理权限。
如需登录,将前往登录页面。登录后请回到此处,再次点击所选的验证按钮。
在项目文件夹中运行 /nomorevibe verify,即可验证所有权并自行管理介绍。
一分钟完成验证A strategy game of the seven Great Kings of the Late Bronze Age with no diplomacy rules: every treaty and betrayal is yours, and every word travels with an envoy who may soften it, sell it or lie. Play the kings yourself, watch Claude play them, or mix the two. Runs in the browser on desktop and phone; C# engine, Blazor WebAssembly.
翻译准备中
取自仓库 README
正在加载完整 README。
查看完整 README ↗A strategy game with no diplomacy rules, set among the seven Great Kings of the Late Bronze Age (c. 1360–1330 BCE) and the city-states between them. The engine enforces physics: movement, combat, production and payment. Every treaty, alliance and betrayal is invented by the players. Kings never speak to each other directly: every word travels with an envoy who has motives of their own.
This is the prototype described in the design document Amarna Letters — Prototype Design Document. Its job is to test one hypothesis: if communication is delayed, mediated by people with their own motives, and impossible to verify fully, then norms of trust, reputation and enforcement emerge on their own.
Play it in your browser: https://haaktuah.github.io/amarna-letters/
Play a king or a city yourself, watch Claude play them all, or mix the two. With no API key, every king and envoy is a script player, and a whole game costs nothing.
The message trail. Every message exists in three versions: what the king wrote, what the envoy said at the foreign court, and what the envoy reported home. The gaps between them are the game.
You are one of thirty powers: the seven Great Kings, Egypt, Hatti, Babylonia, Mitanni, Assyria, Arzawa and Alashiya, or one of the city-states and chiefs between them, Byblos, Amurru, Ugarit and the rest, on one scoreboard: each power is judged by its growth against par for its starting position, so a city that holds its own can win as well as an empire. Each season you read what reached your court, receive foreign envoys, and give your orders in secret. Every king orders at the same time, and the engine resolves everything at once. Three seasons make a year.
You see only what your kingdom can see: your own land, the provinces where your troops stand or your spies watch, and one step beyond them. Everything else you know from letters, reports and rumours, and those can be late, shaded or false.
Armies and fleets.
Land and gold.
Envoys and letters.
Tablets.
Court and spies.
Every capital has walls of level 3. The great fortresses (Carchemish, Qadesh, Tyre, Megiddo, Gaza, Mari, Susa, Memphis, Kanesh and Nineveh) have 2, and a few walled towns have 1.
A king's strength is ten times the yield of the land he holds, plus his gold, minus back pay he owes. Conquered land counts as it is absorbed: a third in its first season, two thirds in the second, all of it from the third. The winner is whoever grew most relative to their own start.
Any season may be the last, from the first: early on it is very unlikely, and it grows likelier with every year, so that half of all ages have ended by 1332 BCE (season 44). The engine draws the last season once, in secret, at the start, with a hard cap drawn from 80 to 100 seasons; nothing shown reads it. The median and the cap's range (or none) are table settings, and a table can play an open age with no end, from the start or by playing on once the age has ended.
A kingdom falls with its capital: its remaining land passes to the conqueror, and its troops disband. A fallen player watches the rest of the game in god view (a table setting).
There is one winner. Shared victory for sworn allies is a house rule, off by default.
Every king reads every power's opening position. The script kings each play a temperament of their own, varied a little in every game.
| Power | Capital | Tier | Starts with | How the script king plays |
|---|---|---|---|---|
| Egypt | Akhetaten | Strong | armies in Memphis and Akhetaten, a fleet in the Nile Delta, 600 shekels | "Pharaoh of the gilded gifts": generous, proud and diplomatic; buys friends with gold "as plentiful as dust" |
| Hatti | Hattusa | Strong | armies in Hattusa and Kanesh, a fleet in Kizzuwatna, 300 shekels | "The conqueror of Anatolia": the most aggressive king, vengeful, little given to trade |
| Babylonia | Babylon | Strong | armies in Babylon, Sippar and Dur-Kurigalzu, 650 shekels | "Keeper of the old order": the proudest and greediest; cautious, and will not call Assyria brother unless they are friends |
| Mitanni | Washukanni | Middle | armies in Washukanni, Taidu and Terqa, 500 shekels | "Seeker of a patron": the most diplomatic and among the most honest; squeezed between Hatti and Assyria |
| Assyria | Ashur | Middle | armies in Ashur, Nineveh and Ekallatum, 350 shekels | "The upstart who would be brother": aggressive and cunning, hungry to be counted a Great King |
| Arzawa | Apasa | Weak | armies in Mira and Apasa, a fleet in Seha, 300 shekels | "The western opportunist": the most cunning and least honest |
| Alashiya | Enkomi | Weak | a fleet in Enkomi, 350 shekels | "The copper merchant": the island trader, the most cautious and least aggressive |
Between the great kingdoms lie the city-states, such as Ugarit, Byblos, Qadesh and Elam, and the Kaska, Lukka and Sutean chiefs: twenty-three powers of their own, most of them vassals of Egypt or Mitanni by sealed tablets, with the same rules and the same goal as the kings. Alashiya is an island, reached only by sea.
A script king sees only what its seat sees. It remembers who wronged it and who kept faith, and checks its orders against what its rivals are likely to do by playing the season out with the engine's own adjudicator. When one king runs away with the game, the others swear coalitions against him and strike at his land together; a king attacked by a strong neighbour asks the neighbour's other neighbours for a defence pact. Script kings give standing orders too. The god view lists each king's temperament under Standings. Every rule they follow, with its numbers, is in docs/DESIGN-NOTES.md under "Script kings".
No court has missions, goals or rules of its own. Each plays differently because of what it has, what it lacks and where it stands, and the same court plays differently from game to game as floods fail, roads close and rivals change. This is the picture the game is growing towards; for now, position and relations do most of the work.
The Levant, the boiling pot. Between the great kingdoms lies the board's centre: a crowd of vassal cities where the spheres of Egypt, Mitanni and Hatti meet. Each ruler protests his loyalty to Pharaoh while taking his neighbours' towns and denouncing them as traitors, and most of the surviving letters are theirs. Rib-Hadda of Byblos wrote more than sixty, begging for troops and grain against Abdi-Ashirta of Amurru and then his son Aziru, who played Egypt against Hatti and in the end went over to Hatti. It is where the great kings' spheres meet, and where the truth is hardest to find.
Eighteen people move between the courts: eight with names attested in the Amarna letters (Mane, Keliya, Pirissi and others, seven drawn each game) and the rest named in their region's style. Each power starts with two envoys; the rest are free agents, looking for a master.
Each has traits from 0 to 100 (loyalty, greed, competence, ambition, courage, and sympathy for a court), secrets at three levels of secrecy, and a memory that travels with them to a new master. Nobody shows you their traits. You learn them from what they do, from background checks, and from what others tell you.
What an envoy can do to your words, and why:
Servants are paid every season. One left unpaid loses loyalty and is owed back pay, and may give notice. When a contract ends, the servant names a price to stay.
Spies watch a court or a province and send reports home. Watchmen catch them, and a caught spy can be ransomed, or turned into a double agent who sends home poisoned reports.
Envoys are script characters in the Free and Local agents presets and Claude Haiku in the others; Customize can give them Sonnet.
Pick a preset, choose whether to watch or take a seat, and begin.
| Preset | Kings | Envoys and spies | Narrator | Rough cost per 20-season game |
|---|---|---|---|---|
| Free | Script kings | Script | Template | $0, no key needed |
| Budget | Haiku 5.5 | Haiku 5.5 | Haiku 5.5 | under $1 |
| Standard | Sonnet 5.5 | Haiku 5.5 | Sonnet 5.5 | about $9 |
| Showcase | Opus 5.5 | Haiku 5.5 | Opus 5.5 | about $20 |
| Local agents (this computer only) | Claude Code agents | Script | Template | your Claude subscription, no key |
Envoys stay on Haiku even in Showcase: they make the most calls, and the design wants fallible messengers. Customize the table sets each king separately (script, human, Haiku, Sonnet or Opus, for model-against-model experiments, and a local agent when the app runs on your computer), Sonnet envoys, the narrator, thinking effort and the house rules. The estimates are rough; the meter in the game shows the real spend, and the game pauses at a spending cap set from the estimate (you can change it).
Model seats use your own Anthropic API key: calls go straight from the page to the Anthropic API, and the key is never stored outside this tab. Use a dedicated key with a spend limit set in the Anthropic Console. Simulate the API (under Customize) tries model seats with canned replies; nothing leaves the browser. When a model call fails, a script king or character makes that decision instead, the failure is listed at the top of the side panel, and the game goes on.
Local agents is local play, for your own games: you take a seat, and the other kings are guarded Claude Code agents
answered in a Claude Code session on your Claude subscription, through a small bridge on this computer
(node sim/bridge.js). It appears only when the app runs on your computer. The steps are under "Local play" in
sim/README.md, and the session's start prompt is docs/PLAY-PROMPT.md.
Long games become affordable when model calls scale with events, not with time. Sleeping
seats are on by default: after giving its orders, a model king sleeps, and it never chooses when
it wakes. The world goes on, its units hold and its scheduled orders run, each following its unit
(a march stopped by a bounce tries the step again, and a new levy can be given orders ahead), and
its standing orders answer, in the same season, a unit it sees setting out to enter a province or
to attack one of its own. It is woken only when something concerns it: an envoy at the door,
foreign troops in its land or arriving next to its homeland or a walled city it holds, land or
troops lost, a failed scheduled or standing order, a servant giving notice, or a ransom demanded.
Everything else waits in a digest, and a hidden safety cap wakes it after a long sleep. Script and
human kings play every season. Switch it off (Customize in the web app, --sleep off in the batch
runner) to ask every king every season. Standing orders are every king's, awake or asleep: script
kings give them, and a human gives them in the orders panel. The details are in docs/DESIGN-NOTES.md.
In games without a human, the god view shows everything the engine knows: true map, every treasury, every character's hidden traits and facts and who knows them, and the message trail: for every message, what the king wrote, what the envoy said abroad, and what the envoy reported home. Switch to any power to see exactly what it saw. The side panel's tabs show the season's events, the trail, the people, the provinces, the standings and the rules. The timeline steps back through the seasons; signals mark sealed tablets, accusations, forgeries, broken promises and more. After the game, compare two nations' self-serving annals with the true record.
The map is a museum map of the Late Bronze Age: coasts, rivers, mountains, deserts and fields, each power in its colour with an emblem and a border line of its own (so it reads without colour), armies as chariots and fleets as ships. Step the timeline and each season's moves, attacks, supports, bounces and captures are drawn; envoys, spies and gold appear on the road and at foreign courts. In a power's view the fog of war fades with the age of its sight, and uncertain things (old sightings, rumours, spy reports) are drawn as such. Tap anyone to follow their life: their path, their masters, their missions, what they said and reported.
A link to play?watch=7 (any seed) starts a game of script kings to watch. Games are saved in the
browser, and a game's log can be exported and loaded again to replay it exactly.
It is built phone first: the map fits the screen with pinch-zoom, the panels open from it, and the app installs to the home screen and starts from cache.
You need Node 22 or later.
npm ci
npm run dev --workspace src/web
Then open http://localhost:5183, pick a preset, choose whether to watch or take a seat, and begin.
scripts/test.sh # type-check, the engine's determinism lint, and every test (Vitest)
scripts/sim.sh --games 50 --seed 1 --verify-replay
The batch runner plays games unattended and prints wins by power and the success signals.
With ANTHROPIC_API_KEY set it can run model games too (--preset budget, or per role:
--leaders sonnet --characters haiku --narrator sonnet), stopping at --budget dollars; every
such game costs real money. --no-primer runs the
customs-primer experiment; --out logs keeps every game's log. --versus solo measures the
script king against the frozen first version (one king at a time, against six of the old), and
--versus baseline and --focus <Power> mix the two. Script games run on every core.
--sleeping-scripts lets script kings sleep as model kings do; they then schedule conditional orders ahead, which
--no-plan-ahead turns off and --plan-ahead-half gives to half of them, to measure what it is worth.
The measure of call size (item 6b) shadows script games with model kings and splits their requests into parts:
node --experimental-strip-types --no-warnings src/batch/src/measure.ts --games 20 --seed 1 (--per-audience for
one call per audience, --dump DIR for sample requests). It calls no model.
--sleep off turns off sleeping seats, --sleep-cap N sets
the hidden safety cap on a sleep, --max-turns N stops a game after season N at the latest (a known cap), --cap 80-100 or --cap none sets
the hard cap's range, and --median N the season by which half of all ages have ended (44 by default).
--file-seats DIR makes every king a file seat:
it writes what a model king would see to its own folder and waits for a JSON reply file, which is
how sim/ plays a game with Claude Code agents as the kings (see sim/README.md).
Changes go in through pull requests: every pull request builds and runs the tests
(.github/workflows/pages.yml) and gets a playable preview at its own address,
https://haaktuah.github.io/amarna-letters/pr-<number>/, linked in a comment on the pull request and removed when it
closes (.github/workflows/preview.yml). Merging into main publishes the browser game at the site's root. Both are
written to the gh-pages branch, which Pages serves: main to its root, each pull request to its own pr-<number>/
folder (scripts/site-prepare.sh, scripts/site-publish.sh). No pull request's own code can write to that branch, so
none can replace the root. To publish the static files somewhere else, such as S3:
npm run build --workspace src/web
The site is src/web/dist. If it is served from a sub-path, set <base href> in its
index.html to that path.
Every game is deterministic: the same seed and the same decisions give the same game, and every log replays exactly.
| Package | What it is |
|---|---|
src/engine | The deterministic engine: map, state, orders, validator, adjudicator, economy, envoys, tablets, spies, catastrophes, scoring. Pure TypeScript, no I/O; a lint config keeps out clocks, unseeded randomness, culture-sensitive text and inexact Math. |
src/game | Seats, character minds and the narrator as interfaces; the runner that records every decision; script kings (temperaments, memory, a look-ahead over likely rival orders) and script characters; signal detection. |
src/llm | Claude leaders (Sonnet 5.5 / Opus 5.5), characters (Haiku 5.5) and narrator (Sonnet 5.5): prompts, JSON schemas, parsing, cost accounting, the API simulator, file seats and the local bridge. Calls are posted with fetch, in the browser and the batch runner alike. |
src/web | Vite and React app: setup, SVG map, human orders and audiences, spectator views, timeline, chronicles, IndexedDB saves. The engine, the script kings and characters run in a Web Worker; the page only shows what it sends. |
src/batch | Node runner for unattended games, on worker threads. |
src/*/test | The tests (Vitest), ported from the C# suite: engine, script kings and characters, model layer against a simulated API, and the web map's scenes. |
tests/fixtures | Recorded model games (JSON lines), kept as data. The C# build the game was converted from (build order item 5+) was removed after the fix pass; it is kept on the branch archive/csharp-final (commit 5b4caa2). |
docs/DESIGN-NOTES.md records every decision and simplification made while
building, and every rule as implemented. docs/BUILD-PROGRESS.md tracks the
build order item by item, with each item's report in docs/rounds/.
The game was inspired by Irving Finkel's Curator's Corner episode 100, "Reading the Pharaoh's private mail with Irving Finkel" (The British Museum, https://www.youtube.com/watch?v=3MZq_ZQDhJA), and leans on William L. Moran's The Amarna Letters for the translations of the letters themselves.