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
Checkmate Prep: 상대를 미리 파악하고 오프닝·함정·연습 문제를 제공합니다
Checkmate Prep: know your next opponent before the game. Opening prep, traps and drills for your games against the people you play on chess.com.
게임JavaScript · PythonGitHub @Checkmate-Prep온라인운영자 미확인
써 보셨어요? 한 번만 눌러 주세요. 로그인은 필요 없어요.
아직 감지된 업데이트가 없습니다. GitHub 릴리스가 나오면 여기에 보입니다.
개인 계정의 공개 저장소 소유자는 GitHub로 로그인해 운영자를 확인하고 소개를 직접 관리할 수 있습니다. 관리자 승인은 필요하지 않습니다.
로그인한 뒤 확인 파일을 만들어 이 저장소의 기본 브랜치에 30분 안에 커밋해 주세요. 다른 브랜치나 포크의 파일은 인정하지 않습니다. 파일을 올린 뒤 아래에서 확인하면 관리자 승인 없이 관리할 수 있습니다. 자격을 유지하려면 파일을 남겨 두세요.
로그인이 필요하면 로그인 화면으로 이동합니다. 로그인 후 이곳으로 돌아와 원하는 확인 버튼을 다시 눌러 주세요.
프로젝트 폴더에서 /nomorevibe verify를 실행하면 소유권을 확인하고 소개를 직접 관리할 수 있습니다.
1분 만에 확인하기Chess coach to prepare and analyze chess games with friends
저장소 README에서 가져옴
README 전체를 불러오는 중입니다. 아래는 저장된 발췌입니다.
Know your next opponent before the game. Checkmate Prep prepares you for games against the people you play on chess.com. This repository, chess-coach, holds the web app and the personal pipeline it grew out of.
It started as a personal chess coach for preparing games against friends on chess.com.README 전문 보기 ↗Know your next opponent before the game. Checkmate Prep (https://checkmateprep.com) prepares you for games against the people you play on chess.com. This repository, chess-coach, holds the web app and the personal pipeline it grew out of.
It started as a personal chess coach for preparing games against friends on chess.com.
It downloads your games and your friends' games from the chess.com public API, runs Stockfish over them, and builds a profile of each player: openings and how they score with them, where their mistakes happen (opening, middlegame or endgame), how they handle the clock, and positions where they repeat the same mistake. The conclusions go into notes.py, and build_report.py turns everything into a prep book page with game plans, opening traps and drills from your own games.
How it's built: this project is being built iteratively with Claude Code. docs/build-log.md records every request, word for word, and what came of it.
pip install -r requirements.txt
Stockfish 19 downloads into bin/ on the first analysis run (the macOS build), unless a stockfish is already on your PATH.
python3 coach.py refresh # download + analyze you and all friends
python3 coach.py refresh pepex654 # one player
python3 coach.py explore gregmolin white "e4 c6 Nc3 d5" # opening explorer
python3 build_report.py # rebuild report.html
Players are set in coach.py (ME, FRIENDS). The engine analysis is cached per game in data/, so a refresh only analyzes new games.
| File | What it does |
|---|---|
fetch.py | Downloads monthly game archives from api.chess.com |
analyze.py | Stockfish evaluation of every move (cached in data/<user>.analysis.json) |
profile.py | Style, opening and weakness statistics (data/<user>.profile.json) |
explore.py | What a player chose next from any position, and their score |
notes.py | The coach's written conclusions and prepared lines. Kept on your own machine only (gitignored, since this repo is public); it feeds report.html |
build_report.py | Renders the prep book (report.html) |
build_app.py | Versions the web app's offline cache, and writes an empty app/prep.json (hand-written prep never goes into the app) |
app/ is an installable web app (PWA). Its home is https://checkmateprep.com, served by the Cloudflare Worker (with accounts). The GitHub Pages copy at https://checkmate-prep.github.io/chess-coach/ stays up without accounts. Open it on a device and choose Add to Home Screen. Anyone can use it: enter your chess.com username, then add the people you play. No account is needed, because everything used is public on chess.com. An optional account keeps your opponents, names, drill progress, Stockfish's analysis and AI plans the same on your phone and laptop.
Everything is stored in the device's browser (IndexedDB). The engine is Stockfish 19 lite (single-threaded WASM, about 1.8 MB) in a Web Worker. It runs at roughly 10 seconds per game review and 1–3 minutes per trap scan.
Hand-written prep (notes.py) is a local file, kept out of git because this repo is public. It only feeds the prep book (report.html); the app never shows it and ships an empty app/prep.json. Pushing app/ to main redeploys the site.
Signing in is optional. It keeps your chess.com username, your opponents, the names you gave them and your drill progress the same on every device. It also keeps what a device worked out, so another device doesn't redo it: Stockfish's review of each game, the trap scans and Claude's game plans. Games themselves come from the shared game store (below), or from chess.com.
worker/) with a D1 database. Each account has one small document (app/syncdoc.js). On every change, and whenever the app comes back into view, a device sends its copy and gets back the merge with the other devices'. For each opponent the latest change wins; drill progress from all devices adds up. Removals reach the other devices too.app/results.js, rules in app/resultsdoc.js, served by worker/results.js) are kept in D1 too, one row per result: each reviewed game, each trap scan (per colour) and each AI plan, for each player. After each sync a device pulls the rows that changed since its last pull, then uploads what it computed since (20 seconds after the last new result). A game's review never changes; the trap scan of more games and the newest plan win. On a new device the automatic analysis waits for that first pull, so it only runs Stockfish on what's still missing, and the AI plan shows without being written (and paid for) again./api) the app works as before, without the Sign in button.worker/account.js, when the Worker has the Management API credentials below) and clears the device. The public pages are /privacy and /delete-account (the link for the Play listing). The shared game store holds nothing about accounts, so it isn't touched. Signing out also clears the device, so the next visit starts from the welcome screen. Usage stats are separate (below).| File | What it does |
|---|---|
app/sync.js | Sign-in (Auth0 SPA SDK, vendored in app/vendor/auth0/), when to sync, applying changes from other devices |
app/syncdoc.js | What syncs and how two copies merge; shared by the app and the Worker |
app/results.js, app/resultsdoc.js, worker/results.js | Analysis results on the account: when they upload and pull, what's accepted and which copy wins; /api/results |
worker/games.js, scripts/forget-player.mjs | The shared game store (/api/games) and removing a player from it |
worker/account.js | Deletes the Auth0 user when an account is deleted |
worker/index.js, worker/sync.js, worker/auth.js | /api/config (is sign-in set up?), /api/sync (merge and store, delete), checking Auth0 tokens |
worker/events.js, app/track.js | /api/event: which screens are opened (see Monitoring) |
wrangler.toml | The Worker and its environments (dev, test, production): Auth0 settings, D1 database, usage stats dataset, rate limit |
.github/workflows/deploy.yml | Deploys main to production, and each push to an open pull request to dev (dev shows whichever PR pushed last). Run it by hand to deploy test, or any branch to any environment |
tests/syncdoc.test.mjs, tests/resultsdoc.test.mjs, tests/results.test.mjs, tests/games.test.mjs | Merge rules, results and the game store (the Worker's SQL runs on an in-memory SQLite), run with the other app tests (npm test) |
When the app is served by the Worker, it downloads games through /api/games (worker/games.js) instead of calling chess.com itself. The Worker keeps one copy of each player's games in an R2 bucket (GAMES), shared by every user: games/<player>/archives.json (their list of months, reused for an hour) and games/<player>/<YYYY-MM>.json (one month's games). A month fetched after it ended never changes and is never fetched again; the current month is fetched again once its copy is an hour old. Games are stored from neither player's side and without the PGN: moves, clocks, ratings, results, time control and opening (neutral in app/chesscom.js), about 1 KB each.
ops/r2/games-lifecycle.json) that deletes files 180 days after they were written. The Cloudflare API token needs R2 edit access (the Edit Cloudflare Workers template includes it); without it the deploy warns and carries on.npm run forget-player -- <username> --env production puts them on a deny-list (deny:<username> in the PREP KV namespace), so the Worker never stores them again, and deletes their files. A player chess.com no longer knows (404) is deleted automatically.chess-coach-games-dev, -test, -prod).ops/auth0/link-accounts.js) runs first in the Login flow. When someone signs in with an email that is verified and another user already has the same verified email (a password account, then "Continue with Google", or the other way round), it links the new login to the oldest such user and the token carries that user's id, so the synced data is the same whichever way they sign in. An unverified email is never linked. It never blocks a sign-in: if Auth0 or the Management API fails it logs and the login goes on as it was. Deleting the account in the app deletes the Auth0 user together with every login linked to it. People who signed in both ways before this existed have two users with two sets of data: the Action links them at their next login, and ops/auth0/merge-accounts.mjs (see its header) prints the SQL that moves the old user's data onto the other; without it, the second set simply stays unused.ops/auth0/notify-signup.js) sends a phone notification through ntfy.sh the first time someone signs in to the production application, with their email and how they signed in. Dev and test sign-ins don't notify. It's deployed by ops/auth0/deploy-actions.mjs through the Auth0 Management API, and runs after each production deploy. It only touches this one Action and its place in the Login flow, and running it again changes nothing.app/track.js) sends its name to /api/event (worker/events.js), which writes it to Workers Analytics Engine. A screen that opens again after 30 minutes away counts as a new visit. Stored: the screen (me, prep, prep-opp, explore, drill, drill-item, setup, add, welcome, start, signin), a random id for the device, the Auth0 user id when signed in, and the country. Never usernames, opponents, games, the IP address or the browser. Data is kept 3 months. Settings has a Share usage stats switch (on by default). Nothing is sent on GitHub Pages or a static server.One-time setup for the sign-up notification:
openssl rand -hex 16). Anyone who knows it can read it. Install the ntfy app on your phone and subscribe to it.read:actions, create:actions, update:actions, read:branding, update:branding (the last two set the favicon of the login page), read:users and update:users (the account-linking Action looks users up by email and links them at login, with these same credentials, which the deploy stores as that Action's secrets) and delete:users (the Worker uses it to delete a user's Auth0 login when they delete their account). Permissions are granted under Applications → Deploy actions → API Access: click Edit next to the Auth0 Management API, then open Client Access (the machine-to-machine grant the script uses), not User Access: tick them, then Save. Branding is a tenant setting, so the favicon applies to every environment (every environment signs in at login.checkmateprep.com); that is why only the production deploy sets it. When the script gains a new Management API call, add its permission there too, or the production deploy fails with a 403.production, add the variable AUTH0_MGMT_CLIENT_ID and the secret AUTH0_MGMT_CLIENT_SECRET (from that application), and the secret NTFY_TOPIC. The Client ID isn't secret; a secret of that name also works. The next production deploy creates the Action; until then the deploy says it was skipped. To run it by hand: AUTH0_MGMT_CLIENT_ID=… AUTH0_MGMT_CLIENT_SECRET=… NTFY_TOPIC=… npm run auth0:deploy. The deploy also uploads AUTH0_MGMT_CLIENT_ID and AUTH0_MGMT_CLIENT_SECRET to the Worker, which is how deleting an account deletes the login too. Add the same two to the dev and test environments (same tenant, same application) to test it there; an environment without them deletes only the synced data and leaves the Auth0 user, and the deploy says so. Add -- --force to redeploy after changing the topic, or -- --branding-only to set just the login page favicon (no NTFY_TOPIC needed).The product domain is checkmateprep.com, registered at Cloudflare Registrar in the same Cloudflare account as the Worker. The browser keeps each user's data (games, analysis, settings, the installed app) per address, so this address must never change once people use it; the host behind it can.
| Address | What it is |
|---|---|
checkmateprep.com | The app and /api (production Worker, set as a custom domain in wrangler.toml) |
www.checkmateprep.com | Redirects to checkmateprep.com (worker/index.js) |
login.checkmateprep.com | Auth0 sign-in page (Auth0 custom domain, set up below) |
dev.checkmateprep.com, test.checkmateprep.com | The dev and test environments (their own Workers, databases and Auth0 application). No environment has a workers.dev address, so each one keeps its data in one place |
checkmateprep.com and www; if a record already exists on either name (a parking page, for example), delete it first or the deploy fails.privacy@, contact@ and [email protected] to the project mailbox (listed in docs/services.md), for the store listings, Auth0, billing and user mail. Sending sign-in emails needs a sending provider (below).checkmateprep.com was registered in a personal Cloudflare account, so production (the Worker and its D1 database) runs there for now. Cloudflare only lets a domain move between accounts 10 days after registration. Then move everything to the project account together; users notice nothing, because the address and Auth0 stay the same.
Before you start (in the personal account):
login CNAME for Auth0 and any Email Routing records. A move keeps none of the old account's settings.npm install done, and the personal account's token and ID at hand.export CLOUDFLARE_API_TOKEN=<personal token> CLOUDFLARE_ACCOUNT_ID=<personal account id>
npx wrangler d1 export DB --remote --env production --table docs --no-schema --output prod.sql
sed 's/^INSERT INTO/INSERT OR IGNORE INTO/' prod.sql > prod-import.sql
prod.sql holds every account's synced data: keep it private and out of git.login CNAME needs the proxy off. Then check that Auth0 still shows login.checkmateprep.com as Ready.production (and dev, test), replace the secret CLOUDFLARE_API_TOKEN and the variable CLOUDFLARE_ACCOUNT_ID with the project account's (the token needs Workers, D1 edit and the checkmateprep.com zone).production, then dev and test (their data is test data: no export needed). This creates the Worker, the custom domains and an empty D1 database in the project account. If it fails because checkmateprep.com or www already has a record, delete that record and run it again.INSERT OR IGNORE keeps a row written since, and devices re-send their own data on their next sync.
export CLOUDFLARE_API_TOKEN=<project token> CLOUDFLARE_ACCOUNT_ID=<project account id>
npx wrangler d1 execute DB --remote --env production --command "CREATE TABLE IF NOT EXISTS docs (sub TEXT PRIMARY KEY, doc TEXT NOT NULL, ver INTEGER NOT NULL, updated INTEGER NOT NULL)"
npx wrangler d1 execute DB --remote --env production --file prod-import.sql
www redirects, and signing in shows your opponents.chess-coach, chess-coach-dev, ) and their D1 databases and KV namespaces in the personal account, delete the personal account's API token, and delete and .One-time setup:
Auth0: the free plan allows one tenant, checkmateprep.us.auth0.com (US region), shared by all environments. It has one API, identifier https://api.checkmateprep.com (AUTH0_AUDIENCE; an identifier only, nothing needs to answer there), signing algorithm RS256, with Allow Offline Access on so the app stays signed in. It has two Single Page Applications, each with Refresh Token Rotation on, and each authorized on the API (APIs → the API → Application Access → the application → User Access: Authorized). Without that, sign-in comes back with "Client … is not authorized to access resource server":
| Application | Used by | Callback, logout URLs | Web origins, CORS | AUTH0_DOMAIN |
|---|---|---|---|---|
| Checkmate Prep | production | https://checkmateprep.com/ | https://checkmateprep.com | login.checkmateprep.com |
| Checkmate Prep - non prod | dev, test, npm run dev | http://localhost:8787/, https://dev.checkmateprep.com/, https://test.checkmateprep.com/ | the same without / | login.checkmateprep.com |
Users are shared by both applications, so test sign-ins show up in the production user list; delete them afterwards. Synced data stays separate (each environment has its own D1 database). The password database (Username-Password-Authentication) is off for both applications (Connections tab), so sign-in is Google or an email code only; turning it back on would let people sign up with an address they never verified. Under Authentication, turn on Google and/or Passwordless Email. Auth0's built-in email sender is for testing only: for sign-in codes in production, set up your own email provider in Auth0 (Resend, Amazon SES, …), sending from an address on checkmateprep.com and adding the provider's SPF and DKIM records in Cloudflare DNS. For a branded sign-in page, add the Auth0 custom domain login.checkmateprep.com (free plan, needs a card on file): create the CNAME Auth0 gives you in Cloudflare DNS with the proxy off (grey cloud), then use login.checkmateprep.com as AUTH0_DOMAIN (done for all environments; both applications sign in there, so the sign-in page looks the same on dev). Google's OAuth client needs https://login.checkmateprep.com/login/callback as a redirect URI; the tenant-address one () is only needed for sign-ins that bypass the custom domain, such as the Auth0 dashboard's connection tests. The Management API (account deletion, the deploy script) stays on the tenant address.
The first deploy of each environment creates its D1 database and its R2 bucket for the game store, and the Worker creates its tables on first use. Production is then at https://checkmateprep.com, and dev and test at https://dev.checkmateprep.com and https://test.checkmateprep.com. All three must deploy to the Cloudflare account that holds the checkmateprep.com zone, since deploying creates their DNS records.
To run the Worker locally (Node 22+):
npm install
npm test # app tests, including the merge rules
cp .dev.vars.example .dev.vars # optional: a dev Auth0 tenant's values turn on accounts
npm run dev # app + API at http://localhost:8787
On an opponent's Prep page, Write the plan sends their statistics, lines, head-to-head record and any traps Stockfish found to Claude, which writes a plan in the same format as the hand-written prep. The plan is saved on the device, and its lines show up in Drill. The writing runs on the server as a Cloudflare Workflow (PrepWorkflow in worker/prep.js), and the app checks on it every few seconds. So you can leave the page or close the app meanwhile: the plan is finished and cached anyway, and the app picks it up when it opens again. Tapping again while it's being written joins the same job at no extra cost. While it writes, the card shows the stage (reading, thinking, writing each line), the time so far and short summaries of what Claude is considering. The Workflow saves that progress to D1 every few seconds (table prep_progress, deleted when the plan is done).
The Anthropic API key can't live in a public website, so the app is also served by a Cloudflare Worker (worker/prep.js) that holds the key and calls Claude. The button only appears when the app is served by that Worker. On GitHub Pages and on a plain static server it stays hidden.
The Worker limits cost in four ways. It only accepts requests from the app's own origin. It has a per-minute rate limit per IP address. It has daily caps per IP address and overall. It caches each plan for 30 days, so identical statistics never pay twice. The real backstop is a spend limit on the Anthropic workspace that holds the key.
Everything except secrets is in the repo:
| File | What it does |
|---|---|
wrangler.toml | The Worker and its environments (dev, test, production): model, daily limits, rate limits, KV cache |
worker/prep.js, worker/prompt.js | The /api/prep endpoint, the instructions for Claude and the output schema |
.github/workflows/deploy.yml | Deploys main to production, and each push to an open pull request to dev (dev shows whichever PR pushed last). Run it by hand to deploy test, or any branch to any environment |
One-time setup:
dev, test, production (set up as in Accounts and sync), add that environment's key as the secret ANTHROPIC_API_KEY. Without it the Worker deploys, but the app hides AI prep.The first deploy of each environment creates its KV namespace. /api/health answers {"ai":true} once the key is in place.
To run it locally (Wrangler needs Node 22+):
npm install
cp .dev.vars.example .dev.vars # add a dev-workspace key
npm run dev # app + API at http://localhost:8787
npx live-server app --port=8766
This serves app/ at http://localhost:8766 and reloads the page on every save. Offline mode is turned off on localhost so edits always show up; it only runs on the published site.
chess.com's public API exposes puzzle ratings but not puzzle history, so tactical drills come from positions in your real games.
CLOUDFLARE_ACCOUNT_ID=… CLOUDFLARE_API_TOKEN=… npm run stats prints active devices and signed-in accounts per day, screens, hours of the day (UTC) and the latest devices. Add -- --env dev or -- --days 7 to change the environment or the period. The token needs Account Analytics: Read; a separate read-only token is best.chess-coach-testprod.sqlprod-import.sqlhttps://checkmateprep.us.auth0.com/login/callbackwrangler.toml: AUTH0_DOMAIN, AUTH0_CLIENT_ID and AUTH0_AUDIENCE per environment, and AUTH0_NATIVE_CLIENT_ID (production only: the Auth0 Native application the iOS app signs in with, returned by /api/config as auth.nativeClientId). They're public values, not secrets. Production uses the Checkmate Prep application; dev and test share Checkmate Prep - non prod. Left empty, an environment runs without accounts.
Cloudflare: create an API token from the Edit Cloudflare Workers template (with D1 and R2 edit access), and note your account ID.
GitHub: in Settings → Environments, create dev, test and production, each with the secret CLOUDFLARE_API_TOKEN and the variable CLOUDFLARE_ACCOUNT_ID (not secret; a secret of that name also works). All three use the Cloudflare account that holds checkmateprep.com, so the same token and ID work for each. Until then, the deploy workflow skips Cloudflare and says so. Pull requests deploy to dev from their own branch, so the dev environment's Deployment branches and tags must allow all branches (the default, "No restriction").