The engine manages a complex lifecycle:
- WAITING: Managing player connections and ready status.
- PLAYING: The core loop of Draw -> Discard -> Action Resolution.
- GAME_OVER: Scoring calculation and point exchange.
The "Hu" (Win) detection uses a backtracking algorithm:
- Decomposition: The 14-tile hand is recursively broken down into all possible combinations of sets (triplets/sequences) and a single pair.
- Validation: Each interpretation is checked against the Fan definitions.
- Optimization: An Exclusion Map is applied to prevent double-counting (e.g., "Full Flush" already implies "One Suit").
- Multi-stage Dockerfiles: Separation of build and runner environments to minimize the production image footprint.
- Static Asset Serving & Proxy Fallback: The frontend Nginx handles SPA routing (
try_files) and provides a runtime fallback for Socket.IO traffic using theset $backend_urldirective. - Runtime Self-Healing: The Nginx template includes a conditional check: if
BACKEND_URLis empty, it defaults to the internal Docker service name, preventing container startup failures and simplifying deployment.