尚未偵測到更新。GitHub 發布版本後會顯示在這裡。
個人 GitHub 帳號下公開儲存庫的擁有者可以登入並驗證所有權,自行管理介紹,無需管理員核准。
登入並產生驗證檔案,在 30 分鐘內將其提交至此儲存庫的預設分支。其他分支或分叉儲存庫中的檔案無效。然後在下方檢查檔案,即可無需管理員核准直接管理專案。請保留檔案以維持管理權限。
如需登入,將前往登入頁面。登入後請回到此處,再次點擊所選的驗證按鈕。
在專案資料夾中執行 /nomorevibe verify,即可驗證所有權並自行管理介紹。
一分鐘完成驗證製作者的介紹僅有上面的一句話。 更多詳情請閱讀儲存庫 README。 儲存庫 README ↗
正在載入完整 README。
檢視完整 README ↗A browser game about late-game football play-calling. You get the score, the clock, the timeouts and 22 players on the field; you call the plays (27 of them), manage the clock and call the defense. Every decision is graded by playing the rest of the game out thousands of times for every option you had.
Rosters, jersey numbers and season statistics come from nflverse (CC-BY 4.0; see scripts/build-rosters.mjs, which also documents how the 0-100 game ratings are computed from those statistics). Game situations are made up. Results come from a simplified simulation written for this game; grades are its estimates, not official evaluations or real-world probabilities.
npm install
npm run dev # http://localhost:5173
npm test # engine tests (Vitest)
npm run build # type-check + production build into dist/
npm run preview # serve dist/ at http://localhost:4173
Requires Node 18 or newer.
Copy .env.example to .env.production (or set the same variables in your host's build settings):
| Variable | Required | What it does |
|---|---|---|
VITE_SITE_URL | Required for search | Your public address, e.g. https://your-domain.com (no trailing slash). Makes the social-preview image, canonical link and structured data absolute, and adds sitemap.xml plus its line in robots.txt. Without it, search engines get no sitemap and no canonical URL. |
VITE_GA_ID | Optional | Google Analytics 4 measurement ID. The site tag is built in (in src/game/analytics.ts); set this to use another, or off to load no analytics (for local builds). |
VITE_FEEDBACK_URL, VITE_SUPPORT_URL | Optional | The footer's Feedback and Support the developer links (https:// or mailto:). Not shown when empty. |
VITE_ADS, VITE_ADSENSE_CLIENT, VITE_ADSENSE_SLOT | Optional | Display ads, off by default. See MONETIZATION.md before setting them. |
Analytics events (when enabled): game_start, call_made, spot_complete (game complete), play_again, daily_complete, survivor_end, share (the full list is in src/game/analytics.ts).
No personal data is sent. If you enable analytics for visitors in the EU/UK, you may need a consent banner.
The build is a static site in dist/ with relative asset paths and hash-based navigation (#survivor,
#practice), so it needs no server rewrites. Any static host works:
npm run build, output directory
dist, Node 18+. Add VITE_SITE_URL (and optionally VITE_GA_ID) as build environment variables. Point your
domain at the project in the host's dashboard.npm run build locally with .env.production in place and
upload the contents of dist/.public/404.html is the not-found page (Netlify, Cloudflare Pages and GitHub Pages use it automatically).
The title, description and crawlable text (the pre-render fallback in index.html and the footer blurb in
src/App.tsx) describe the game in generic terms: a free football strategy and play-calling game built around the
two-minute drill. They deliberately don't lead with "NFL": the game is an independent fan game, and "NFL" is the
league's trademark (and "NFL 2 Minute Drill" is also the name of Sega's arcade game). Keep the no-affiliation disclaimer
(footer, About, fallback text) in place.
vite.config.ts adds JSON-LD (WebSite + VideoGame), robots.txt and sitemap.xml at build time from
VITE_SITE_URL. The sitemap lists only the indexable pages: / and /how-to-play/. The Worker's /leaderboard/ and
/stats/ pages are noindex, follow while they are thin; to index them later, flip INDEXABLE in
server/site/worker.js and add them back to PAGES in vite.config.ts. After deploying:
https://your-domain.com/sitemap.xml
and use URL Inspection → Request indexing on the home page.npm run indexnow after a deploy.marketing/). A new site with no inbound links ranks for very little,
whatever its tags say.The launch checklist is marketing/LAUNCH_CHECKLIST.md. Open search tasks are in TODO.md.
src/engine/: the football engine (pure TypeScript). sim.ts resolves plays and runs the clock, evaluate.ts
values decisions by simulation, scenario.ts builds the daily/survivor spots, worker.ts runs evaluations off the
main thread. Tests in sim.test.ts.src/game/: session (deterministic replay of saved picks), grading, storage, sharing, analytics.src/ui/: React screens and the SVG field renderer (field.ts).The game uses real teams and players. src/data/nfl-rosters.json is generated, never hand-edited:
node scripts/build-rosters.mjs 2025 # downloads nflverse data into data-cache/ (git-ignored) and rebuilds the JSON
scripts/build-rosters.mjs from that season's statistics, by the
formulas documented at the top of that script (rank among the league's starters at the position, mapped to 52–98).
Linemen, kickers and low-snap players have no usable individual stats and are marked estimated. These are this
game's own ratings, not official measurements and not any other game's ratings.validateRoster() (src/engine/roster.ts) runs on every team in the test suite: missing players,
duplicate or position-illegal jersey numbers, missing or out-of-range ratings, stat-derived ratings with no season.makeTeam) is still there as a fallback if you need to switch back.The Daily is one game a day, the same for everyone (by UTC date). Its situation type rotates through a weekly shuffle of seven kinds (two-minute drill, dig out of the hole, win it on this drive, close it out, goal-line stand, four downs to win, fourth-down call), and the two teams are never the same as the day before.
The end-of-day screen shows the score, a percentile against simulated play-callers (src/data/percentiles.json,
regenerate with npm run percentiles after balance changes), the coach profile, a share card and, when connected,
Everyone today: your rank, a histogram and today's best.
"Everyone today" is served by the site's own Worker (server/site/worker.js, configured in wrangler.jsonc): the
static game from dist/, and the leaderboard API under /v1/. Scores are kept in Durable Objects (one per day, plus
one for the all-days summary), which the migration in wrangler.jsonc creates on deploy, so there is nothing to set
up by hand. The worker stores a nickname, the score and the grade string per browser token per day; nothing
identifying. Names are cleaned and obvious abuse is replaced with "Coach". Scores are self-reported by the browser, so
treat the board as friendly competition, not a contest with prizes.
Run the site and the leaderboard locally with npm run build && npx wrangler dev. VITE_LEADERBOARD_URL points the
game at a different server, or off turns the board off; the Vite dev server has no Worker, so the board is off
there.