A data-driven Steam game guessing game — built for daily play, explainable clues, and future multiplayer.
SteamGuess is a Wordle-like web game for identifying Steam games from structured clues. Search by a Chinese or English title, submit a guess, and progressively narrow the answer using price, player activity, review metrics, release date, companies, and Steam user tags.
The project is intentionally split into two layers:
- Playable experience — a fast Search catalog whose scored rows form the answer pool.
- Data and feedback platform — a persistent catalog, enrichment jobs, difficulty feedback, and the foundation for multiplayer.
| Entry | Description |
|---|---|
/ |
Mode selection: single-player or multiplayer |
/singleplayer |
Main guessing game with configurable clue fields and hints |
/multiplayer |
Private room, same-question race for 2–8 players |
/labeler |
Internal SQLite-backed difficulty manager; disabled in production by default |
/api/health |
Service health check |
- Chinese/English game-name search with keyboard navigation.
- Ten-guess single-player loop with duplicate-guess prevention.
- Five preset difficulty pools: Beginner, Easy, Normal, Hard, and Hell.
- Custom pools from uploaded AppIDs or a public Steam profile.
- Mainland-China regular prices in CNY; promotional prices are deliberately excluded from statistics.
- Seven-day player peak when samples are available; SteamSpy's
ccuis treated as a historical peak, not live online count. - Screenshot and review hints when the catalog already contains the required source data.
- Post-game difficulty feedback on a 0–100 scale or through preset levels.
- Server-side persistence for sessions, outcomes, and feedback.
- Multiplayer room codes with one-click copy, ready checks, server-authoritative rounds, surrender, rematch, and reconnect support.
SteamSpy request=all ─┐
Steam Storefront API ─┼─> catalog JSON ─> catalog SQLite ─> Search snapshot ─> web client
Steam Reviews API ────┤ └─> enrichment checkpoints
Steam PICS (optional) ┘
web client ──HTTP API──> Node.js service ──> runtime SQLite
Socket.IO ─> multiplayer room engine
| Area | Location | Responsibility |
|---|---|---|
| Frontend | src/ |
React UI, game engine, search, hints, settings, multiplayer client |
| HTTP/API server | server/ |
Static serving, API routes, rate limits, migrations, runtime persistence |
| Catalog pipeline | scripts/catalog/ |
Discovery, normalization, enrichment, publishing, import, status |
| Operations | scripts/ops/ |
Production weekly runner, release validation, backup and smoke tools |
| Public artifacts | public/ |
Browser-ready runtime game snapshot |
| Documentation | docs/ |
Pipeline, schema, labeler, multiplayer research and operations |
The catalog database and player/runtime database are separate. This keeps a catalog refresh independent from player sessions and feedback.
Requirements: Node.js 24+, npm, and Python 3.12+ for catalog tooling.
npm ci
npm run devOpen the Vite URL and start at /. To run the production-shaped server locally:
npm run build
npm startThe default server listens on 0.0.0.0:4173.
Run the full local release gate before deployment:
npm run release:checkIt covers frontend linting, frontend/backend tests, data-pipeline tests, TypeScript compilation, production build, and deployment preflight checks. Useful focused commands:
npm run lint
npm test
npm run test:data
npm run build
npm run release:preflightThe browser reads a published Search snapshot. Rows with a valid difficulty are also eligible as answers. The weekly workflow is incremental and resumable:
- Fetch SteamSpy
request=allpages0..19(the top 20 pages). - Normalize and deduplicate by unique AppID.
- Keep the first
1,000eligible ranked rows in the Active window and retain later candidates as reserve data. - Enrich the first
4,000eligible ranked rows when PICS, Storefront, or review fields are missing. - Save raw pages and enrichment state as checkpoints.
- Publish Search data, derive the scored answer pool, validate membership consistency, and update catalog SQLite atomically.
- Preserve the previous successful snapshot and staging directory on failure.
The runtime relationship is:
Playable answers ⊆ Search guesses ⊆ Active rank window
The production entry point is:
./scripts/ops/run_weekly_catalog.shImportant defaults:
SteamSpy pages: 0..19
Delay between SteamSpy pages: 120 seconds
Storefront delay: 5 seconds
Reviews delay: 5 seconds
SteamSpy retries: 2
Review retries: 3
Active catalog limit: 1,000
Detail enrichment limit: 4,000
A failed run can be resumed by running the same command again. Staging is kept at data/catalog/.weekly-work/current. Relevant overrides include:
STEAMGUESS_ACTIVE_LIMIT=1000
STEAMGUESS_DETAIL_LIMIT=4000
STEAMGUESS_STEAMSPY_INTERVAL=120
STEAMGUESS_STEAMSPY_RETRIES=2
STEAMGUESS_STEAMSPY_RETRY_DELAY=30
STEAMGUESS_STOREFRONT_DELAY=5
STEAMGUESS_REVIEWS_DELAY=5
STEAMGUESS_REVIEWS_RETRIES=3
STEAMGUESS_REVIEWS_RETRY_DELAY=30For a dry plan from an existing catalog:
STEAMGUESS_WEEKLY_FROM_EXISTING=1 \
STEAMGUESS_WEEKLY_SKIP_ENRICHMENT=1 \
./scripts/ops/run_weekly_catalog.shFurther details: docs/data-pipeline.md,
docs/catalog-pipeline.md,
docs/data-schema.md, and
docs/chinese-game-names.md.
Schema changes are tracked through schema_migrations. The server refuses to open a database newer than the schema it supports.
npm run db:backup
npm run db:backup-catalog
npm run db:stats
npm run data:catalog-statusPersist data/ in production, schedule backups, copy backups off-host, and perform a real restore drill before launch. The catalog database is intentionally not committed as a compressed bootstrap artifact: it is large, mutable operational state and stale snapshots previously caused old schema and Labeler data to reappear on new machines. Transfer a db:backup-catalog backup or restore the production volume instead.
Docker Compose is available for a deployment-shaped setup:
docker compose up -d --build
docker compose pscp .env.example .envSTEAM_WEB_API_KEY is server-only and is used for public Steam profile/library imports. It is not required for the catalog's review endpoint. Never put it in frontend code or commit it to Git.
The service applies request size limits, write/profile-import rate limits, upstream timeouts, security headers, and SQLite migrations. Set STEAMGUESS_TRUST_PROXY=true only when the service is behind a trusted reverse proxy. The internal difficulty manager requires both an explicit production build flag and a server-side admin token:
VITE_LABELER_ENABLED=true
STEAMGUESS_ADMIN_TOKEN=replace-with-a-strong-secretThe multiplayer MVP supports private rooms for 2–8 players, BO1/BO3/BO5, ready checks, room-code sharing, server-authoritative answer selection and scoring, round timers, surrender, rematch, and short reconnect recovery.
Docker Compose now enables Redis by default. Multiple Node.js processes on the same host can share active rooms, Socket.IO broadcasts, room locks, and reconnect state. If Redis is not configured, local development falls back to the single-process MemoryRoomStore.
Redis is intentionally configured without disk persistence for this release. Restarting Redis or the whole host ends active rooms, and completed-match SQLite writes are currently best-effort after the authoritative Redis state transition. Durable recovery across host restarts, a result outbox, leaderboards, matchmaking, and social features remain out of scope.
See docs/multiplayer-research.md for the implementation direction.
- Single-player guessing loop and difficulty pools
- Persistent player feedback and catalog database
- Resumable weekly catalog staging
- Screenshot/review hint interfaces
- Multiplayer MVP foundation
- Redis-backed shared multiplayer room state and cross-instance reconnects
- More complete Chinese metadata and review coverage
- Durable room recovery and reliable match-result outbox
- Matchmaking, rankings, and social features
SteamGuess code and generated data are maintained separately. Steam metadata, images, tags, and reviews remain subject to their respective providers' terms and copyrights. Do not redistribute upstream data without checking the applicable terms.