Beat Desty mode - Developer documentation ========================================= 1) Scope -------- This document describes Beat Desty mode implementation in Earth Pulse. Beat Desty is a multiplayer room mode where each human player compares one chosen indicator against Desty's card each round. 2) Main components ------------------ Backend ^^^^^^^ * ``apiserver/apiserver/routers/beat_desty_game.py`` * ``apiserver/apiserver/controllers/beat_desty_game_controller.py`` * ``apiserver/apiserver/services/beat_desty_game_service.py`` * ``apiserver/apiserver/models/beat_desty_room.py`` * ``apiserver/apiserver/models/beat_desty_player.py`` Frontend ^^^^^^^^ * ``frontend/engines/BeatDestyEngine.ts`` * ``frontend/class/BeatDestyGame.ts`` * ``frontend/types/BeatDestyGame.ts`` * ``frontend/composables/useGameWebSocket.ts`` * ``frontend/pages/beat-desty-game-board.vue`` 3) Transport and endpoints -------------------------- REST ^^^^ * ``POST /beat_desty/create_room`` Room defaults: * ``max_players=40`` * ``total_rounds=6`` * ``round_duration_seconds=60`` WebSocket ^^^^^^^^^ * ``GET WS /beat_desty/ws/{room_id}/{pseudo}`` * ``GET WS /beat_desty/ws/{room_id}/{pseudo}/{token}`` Core events: * server to client: ``game_context``\ , ``player_joined``\ , ``game_start``\ , ``round_start``\ , ``round_result``\ , ``game_finish``\ , ``player_leave``\ , ``error`` * client to server: ``start_game`` (host only), ``selected_card`` 4) Room model and constraints ----------------------------- ``BeatDestyRoom`` stores in-memory state: * player list and host (\ ``admin_player_id``\ ) * rounds (\ ``current_round``\ , ``total_rounds``\ ) * dealt cards and anti-reuse list (\ ``used_card_ids``\ ) * per-round selections (\ ``players_selections``\ ) * cumulative score map (\ ``players_scores``\ ) * precomputed indicator bounds (\ ``indicator_bounds``\ ) * round cards (\ ``current_desty_card``\ , ``players_current_cards``\ ) Before start, the service checks there are enough cards to complete all remaining rounds for the current player count. 5) Indicator model ------------------ Default indicators depend on card type: * city / nuts-3: climate-oriented defaults (mostly year 2039) * forest: dedicated forest defaults (year 2025) Round safety rules: * all dealt cards are same type * cards are dealt without reuse * for each player, available indicators are filtered to values present on both the player's card and Desty's card * if at least one player has no comparable indicator, round is aborted and room is finished 6) Game flow ------------ #. Room is created and players join. #. First joined player becomes host. #. Host sends ``start_game`` (minimum 2 players required). #. Backend computes deck-wide ``indicator_bounds`` for stable normalization. #. Each round: * one Desty card + one card per active player are dealt * each player receives only their own compatible indicators * player sends ``selected_card`` payload (\ ``indicator_type_id``\ , ``direction``\ , and period fields from client) * backend resolves outcomes vs Desty and broadcasts ``round_result`` #. After final round (or early finish condition), backend broadcasts ``game_finish`` and persists scores. 7) Scoring and winner resolution -------------------------------- Each player gets a normalized round score in ``[0, 100]`` using logarithmic scaling: * score is normalized with deck bounds for the selected indicator * ``direction=lower`` inverts scoring * cumulative score is tracked in ``players_scores`` Round winners are players with highest round score. Final winner is first leaderboard entry after descending sort. 8) Persistence and leaderboard ------------------------------ * ``create_game_record`` creates DB game row at start * ``finish_game_record`` updates status to finish * ``record_leaderboard_scores`` stores final scores with ``game_type=BEAT_DESTY`` 9) Frontend runtime behavior ---------------------------- ``BeatDestyEngine`` maps WS events to UI state: * ``round_start``\ : sets deadline, current card, Desty card, allowed indicators * ``round_result``\ : updates per-player scores and shows result modal sequence * ``game_finish``\ : stores leaderboard and winner for end screen 10) Operational notes --------------------- * Pseudo toxicity checks are enforced at WS join. * Host-only start is enforced in waiting phase. * Auto-finish conditions handle low active-player scenarios. * Round indicator resolution is scoped to allowed ``(indicator_type_id, month, year)`` tuples to avoid ambiguous value lookup.