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,即可驗證所有權並自行管理介紹。
一分鐘完成驗證正在載入完整 README。 以下為已儲存的摘錄。
A daily football trivia puzzle with darts scoring mechanics — play at trivia-501.vercel.app
Trivia 501 is a Wordle-style daily puzzle game that mashes up football trivia with the scoring system of darts 501. Each day you get a question (e.g. "Appearances for Arsenal in the Premier League") and a target score. You name players whose real-world stats match the question, and their numbers get deducted from your score. The goal? Hit exactly zero — just like finishing a leg of darts.檢視完整 README ↗A daily football trivia puzzle with darts scoring mechanics — play at trivia-501.vercel.app
Trivia 501 is a Wordle-style daily puzzle game that mashes up football trivia with the scoring system of darts 501. Each day you get a question (e.g. "Appearances for Arsenal in the Premier League") and a target score. You name players whose real-world stats match the question, and their numbers get deducted from your score. The goal? Hit exactly zero — just like finishing a leg of darts.
The game has two modes:
Daily Challenge — one question per category per day, same starting score for everyone, one trust-based attempt. When you finish, you get an emoji grid (🟩 valid throw, 🟥 bust, 🎯 checkout) to share with friends. No leaderboards — the social mechanic is comparing results, not competing for rank.
Free Play — pick any category and question, set your own target score, and play on your own terms. No daily limit, no pressure.
The entire thing runs as a single-player experience. Real-time multiplayer, MMR ranking, league tiers, and matchmaking were all designed, partially scaffolded, and then deliberately cut when it became clear they didn't serve the core product. The game is better as a daily puzzle you play and share than as a competitive ladder — and the code is cleaner for it.
[Vercel] [Fly.io] [Supabase]
Next.js 16 + React 19 ── /api/* ──▶ Spring Boot 4.0.6 ──────────▶ PostgreSQL 15
Java 25 pgBouncer :6543
| Layer | Technology | Why |
|---|---|---|
| Frontend | Next.js 16 (App Router) + React 19 + TypeScript | Migrated from SvelteKit mid-project for broader ecosystem and hiring-pool alignment. App Router with "use client" pages keeps the SPA feel. |
| Styling | Tailwind CSS v4 | Utility-first, with semantic CSS custom properties (--color-surface, --color-ink, etc.) for dark/light theme switching via data-theme. No config file — everything in globals.css with @theme inline. |
| State | React Context + useState | No Redux, no Zustand. The app has two contexts (auth + toasts) and local state in custom hooks. Simple enough that a state library would be overkill. |
| Backend | Spring Boot 4.0.6 + Java 25 | Chosen for its mature ecosystem (security, JPA, scheduling, Flyway) and because Java's type system catches the kind of bugs that game logic is prone to. |
| Database | PostgreSQL 15 (Supabase) | ACID compliance for game state, GIN trigram indexes for fuzzy player-name matching, and JSONB for flexible question configs. |
| Auth | Supabase Auth (Google OAuth) + anonymous guest sessions | OptionalJwtFilter creates a UUID cookie for guest players with a 24-hour sliding expiry. No sign-in wall anywhere in the core game loop. Social login uses HTTPOnly cookies via @supabase/ssr. |
| Data | ScraperFC (Python microservice) | A separate Python service populates the database via batch jobs. The backend never calls external APIs during gameplay — all answer validation reads from cached database tables. |
| Deployment | Vercel (frontend) + Fly.io (backend) | Vercel proxies /api/* to Fly.io via next.config.ts rewrites. Supabase handles the database with pgBouncer on port 6543. |
| Testing | Vitest + Playwright / JUnit 5 + TestContainers | 135 frontend behaviour tests, 252 backend tests. TestContainers spins up real PostgreSQL for integration tests. |
The client updates optimistically — type a name, see it deducted from your score — but the server has the final word. Every move runs through GameStateMachine, which validates darts scores, checks for busts, enforces the checkout window, and rejects duplicate answers. The frontend is a remote control, not an authority.
The payoff is that cheating is genuinely hard: you can't fabricate a share result without the server's sign-off, and the answer pool is pre-cached so you can't probe the live API.
All player statistics live in the answers table, populated in advance by the Python scraper. When you submit a player name, the backend hits a GIN-trigram index on PostgreSQL — not an external football API. This keeps response times predictable (< 200ms p95) and avoids the rate limits and cost of live API calls.
The game needs accent-insensitive substring matching ("aguero" → "Sergio Agüero"), which PostgreSQL handles natively with unaccent() and gin_trgm_ops indexes. The entities table powers the autocomplete dropdown without revealing which names are correct answers — it's intentionally decoupled from the answers table that drives validation.
Daily challenges are pre-selected by a midnight cron job, but if the cron misses (server restart, deployment), the GET /{categorySlug} endpoint lazily creates the challenge on first access. The design means the daily challenge is never "down" — it's either pre-computed or created just-in-time.
The single best engineering decision on this project was deleting code. Multiplayer (player2_* columns, WebSocket STOMP protocol, matchmaking queue), the 36-rank MMR/league system, freemium subscriptions, and AI opponents were all designed and partially built. When the product direction clarified toward single-player daily puzzles, all of it came out — ~2,000 lines of production code and tests. The engine is now a clean solo-only state machine with no isSolo branching.
Start with the frontend framework I'd ship with. The project began in SvelteKit, which was great to work with, but migrating to React mid-stream cost weeks. If I'd known the hiring landscape better at the start, I'd have picked React from day one.
Default to solo until multiplayer is proven. The original schema was built for 1v1 matches — player2_id, player2_score, close-finish rules, the works. That scaffolding sat in the codebase for months adding cognitive overhead to every change before I finally stripped it out. When multiplayer comes back, it'll be designed from a real product spec, not a hypothetical one.
Ship the cheap version of social features first. Emoji-grid sharing took an afternoon and gives the game its viral hook. I'd front-load more of those zero-backend social mechanics earlier in the build.
CSV seed data from the start. Early category migrations used inline SQL INSERTs — 12,000-line Flyway files that were unreadable and unmaintainable. Switched to CSV data files + Java migrations; the CSVs are diffable, reviewable, and trivially regeneratable from the scraper.
# Backend (requires Java 25)
cd backend
./mvnw spring-boot:run # → http://localhost:8080
# Frontend
cd frontend-react
npm install
npm run dev # → http://localhost:3000
# API calls proxy to localhost:8080
# Database (Docker for local dev)
docker run -d \
--name trivia501-postgres \
-e POSTGRES_DB=trivia501 \
-e POSTGRES_USER=trivia501 \
-e POSTGRES_PASSWORD=dev_password \
-p 5432:5432 \
postgres:15
Set DB_URL, DB_USERNAME, and DB_PASSWORD as environment variables pointing at your database. Flyway migrations run automatically on startup.
The backend health check: /actuator/health
Most of these were written during the planning phase. Some still reference deferred multiplayer features — I've kept them because the design thinking is sound, even if the implementation isn't current.
MIT
Built by Paul O'Neill — a portfolio project showing full-stack development, game design, system architecture, and the judgement to delete code that isn't pulling its weight.