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

  1. Room is created and players join.

  2. First joined player becomes host.

  3. Host sends start_game (minimum 2 players required).

  4. Backend computes deck-wide indicator_bounds for stable normalization.

  5. 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

  6. 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.