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.pyapiserver/apiserver/controllers/beat_desty_game_controller.pyapiserver/apiserver/services/beat_desty_game_service.pyapiserver/apiserver/models/beat_desty_room.pyapiserver/apiserver/models/beat_desty_player.py
Frontend
frontend/engines/BeatDestyEngine.tsfrontend/class/BeatDestyGame.tsfrontend/types/BeatDestyGame.tsfrontend/composables/useGameWebSocket.tsfrontend/pages/beat-desty-game-board.vue
3) Transport and endpoints
REST
POST /beat_desty/create_room
Room defaults:
max_players=40total_rounds=6round_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,errorclient 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_boundsfor 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_cardpayload (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_finishand 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=lowerinverts scoringcumulative 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_recordcreates DB game row at startfinish_game_recordupdates status to finishrecord_leaderboard_scoresstores final scores withgame_type=BEAT_DESTY
9) Frontend runtime behavior
BeatDestyEngine maps WS events to UI state:
round_start: sets deadline, current card, Desty card, allowed indicatorsround_result: updates per-player scores and shows result modal sequencegame_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.