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 fun mobile game about guessing degen coin data
翻譯準備中
取自儲存庫 README
正在載入完整 README。 以下為已儲存的摘錄。
A lobby party game for phones. Spin a wheel, land on a number, and that number is
a coin's market-cap rank. Everyone guesses what it's worth at once. Closest wins,
worst pays.
Open two browser windows (one private, so they get separate identities) and host
a game in one, join in the other.
No database is needed to develop: without Upstash credentials the app falls back
to an in-memory store and says so at startup. State dies with the dev server.檢視完整 README ↗A lobby party game for phones. Spin a wheel, land on a number, and that number is a coin's market-cap rank. Everyone guesses what it's worth at once. Closest wins, worst pays.
npm install
echo "COINGECKO_API_KEY=your-demo-key" > .env.local
npm run dev
Open two browser windows (one private, so they get separate identities) and host a game in one, join in the other.
No database is needed to develop: without Upstash credentials the app falls back to an in-memory store and says so at startup. State dies with the dev server.
The first room you create builds the coin universe, which crawls 27 pages of the CoinGecko API and takes about 45 seconds. Every room after that is instant until the snapshot ages out.
Coins spins a market-cap rank and guesses price, market cap, ATH, FDV and 24h volume. NFTs spins an all-time-volume rank over Ethereum collections and guesses floor price, all-time volume, owners and sales. Punks spins a CryptoPunk's own index, 0–9999, and guesses what it last sold for.
They are the same game over a different universe. lib/universe.ts picks the
source; everything downstream — the state machine, scoring, every screen — is
indifferent to which one it got.
The two sources have opposite shapes, and that is the whole design:
| CoinGecko | OpenSea | |
|---|---|---|
| Ranked list | 27 requests, numbers included | 30 requests, no numbers at all |
| Per-entity numbers | free, in the list | one request each, at spin time |
| Budget | 10,000 credits/month | 120 per sub-minute window |
OpenSea's ranked list carries names and art but no floor price, and there is no batched stats endpoint. Pricing the whole ladder up front would cost one request per collection, so NFT mode stores only the ladder and fetches the numbers for the one collection a spin lands on: 30 requests to build, one per round. A 100-round game costs about 135.
That trade has one consequence worth knowing: the build can no longer filter
on numbers it hasn't fetched. A collection with no floor is only discovered on
landing, so applySpin walks to the next rank and burns the dead one. What can
be filtered up front — unverified, NSFW, disabled, art-less — is, because that
all arrives in the cheap list call.
Measured against the live API rather than assumed: thirty pages yield 2,876
collections, 96% of raw rows clear the filters (/collections/top is already
curated, so the safelist filter does less work than you would expect), and
5% of the ladder comes back unguessable — so a round costs 1.05 requests on
average. Slider bounds are set from the same crawl: floors run 0.00024 to 29.7
ETH, all-time volume 269 to 1.38M, owners 220 to 5.3K, sales 476 to 39K.
The number on the reel is the punk itself rather than a position in a ladder, because a punk's index says nothing about its value — so this mode has no difficulty tiers and the host screen hides the control.
Prices come from cryptopunks.app, which documents its own API at /llms.txt.
No key, no auth. One crawl of action=all-sales walks the entire history —
31 pages, ~30,500 sales back to June 2017, about 17 seconds — and folds it
into one row per punk. After that a round costs nothing: the prices are
local and the image is a URL the phone fetches itself, served immutable.
Three of its documented endpoints do not do what the docs say, so only the
crawl is trusted: /details?includeHistory=true returns no history at all,
batch-recent-history caps at three rows per punk, and hasNextPage is false
on every page including the first. The crawl stops when a page stops adding
anything rather than when the API claims to be done.
Two answers are not numbers, which is most of what makes this mode different:
Both are scored on the same axis as everything else, and the axis is left to
rank the near misses. Calling "never sold" outright is exact; failing that, the
lowest guess wins, because it was most nearly right that the punk was worth
nothing. A sale above the cap sits off the top of the axis, so the highest guess
takes it — and OVER_CAP_GUESS is tuned so that saying "over" beats pinning
the slider at 5,000 for all three sales that live up there, rewarding the call
rather than a guess at how far over. Nobody has to have named a bucket for
someone to have been closest to it.
The reveal carries the date of both the last and the highest sale. It has to: only 10% of punks have sold in the last year and 60% last changed hands four or more years ago, so 45 ETH in 2021 and 45 ETH today are not the same claim.
The documented 600/hour is not what a key reports. X-RateLimit-Limit comes
back as 120 against a sub-minute reset, and 67 requests in 40 seconds drew no
429 at all — far more headroom than this game asks for. lib/nfts.ts backs off
to X-RateLimit-Reset rather than trusting any of these numbers.
Get a free Demo key from the CoinGecko developer dashboard — no card required, 10,000 credits a month, of which this uses roughly 7,000.
The keyless public API returns 429 on the third consecutive request, and the
snapshot builder needs 27 in a row. CoinGecko's own docs say the keyless tier is
"not suitable for production workloads, scheduled polling, or high-frequency
updates."
UPSTASH_REDIS_REST_URL and
UPSTASH_REDIS_REST_TOKEN or KV_REST_API_URL and KV_REST_API_TOKEN
depending on how the store was added. The app reads both pairs, so nothing
needs renaming. REDIS_URL is the rediss:// protocol endpoint and is not
used — the client speaks REST.COINGECKO_API_KEY under Settings → Environment Variables, and
OPENSEA_API_KEY too if you want NFT mode. Either mode fails with a plain
503 naming the key it is missing, so a game without one is never a mystery.There is no cron to configure: the coin snapshot rebuilds itself lazily whenever a lobby is created and the existing one is more than three hours old, behind a lock so only one builder ever runs. Vercel's Hobby plan only permits daily cron fired within a ±59 minute window, which is no use for this.
Note that Vercel's Hobby plan is for personal, non-commercial projects.
| Resource | Ceiling | This uses |
|---|---|---|
| CoinGecko credits | 10,000/mo | ~7,000/mo |
| OpenSea requests | 120/window | ~30/ladder/day + ~1.05/round |
| cryptopunks.app | undocumented | ~31/history + 0/round |
| Upstash commands | 500,000/mo | ~1,200/game |
| Upstash storage | 256 MB | ~1.8 MB (all three snapshots) |
npm run dev # local server
npm test # 85 tests, no test framework — node --test runs the TS directly
npm run snapshot # rebuild the coin universe and print what it found
npm run snapshot:nft # rebuild the collection ladder, and measure the dead rate
npm run snapshot:punks # rebuild the punk sale history and report the split
npm run playthrough # drive a full game over HTTP against a running server
playthrough is the fastest way to check a change end to end:
npm run dev &
BASE=http://localhost:3000 npm run playthrough
MODE=nfts BASE=http://localhost:3000 npm run playthrough
MODE=punks BASE=http://localhost:3000 npm run playthrough
lib/types.ts domain model, and every constant both sides need
lib/score.ts log-squared error, winner/loser split, FDV merge, reveal tiers
lib/universe.ts which source a game draws from (server only)
lib/coins.ts CoinGecko: crawl, exclude, filter, chunk, lazy rebuild
lib/nfts.ts OpenSea: the ladder up front, the numbers per round
lib/punks.ts cryptopunks.app: the whole sale history, folded per punk
lib/room.ts the game state machine (server only)
lib/useRoomState.ts polling hook — the transport seam
components/phases/ one file per screen
Four runtime dependencies: next, react, @upstash/redis, zod.
A few decisions worth knowing before changing things:
market_cap_rank
field disagrees with its own market_cap_desc ordering — one sampled page held
ranks 2113–2588 interleaved, with all 250 rows mismatched.