strat-gameplay-webapp/backend/app/websocket/CLAUDE.md
Cal Corum 529c5b1b99 CLAUDE: Implement uncapped hit interactive decision tree (Issue #6)
Add full multi-step decision workflow for SINGLE_UNCAPPED and DOUBLE_UNCAPPED
outcomes, replacing the previous stub that fell through to basic single/double
advancement. The decision tree follows the same interactive pattern as X-Check
resolution with 5 phases: lead runner advance, defensive throw, trail runner
advance, throw target selection, and safe/out speed check.

- game_models.py: PendingUncappedHit model, 5 new decision phases
- game_engine.py: initiate_uncapped_hit(), 5 submit methods, 3 result builders
- handlers.py: 5 new WebSocket event handlers
- ai_opponent.py: 5 AI decision stubs (conservative defaults)
- play_resolver.py: Updated TODO comments for fallback paths
- 80 new backend tests (2481 total): workflow (49), handlers (23), truth tables (8)
- Fix GameplayPanel.spec.ts: add missing Pinia setup, fix component references

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-12 09:33:58 -06:00

3.2 KiB

WebSocket Module - Real-Time Game Communication

Purpose

Real-time bidirectional communication using Socket.io. Primary interface between players and game engine - all game actions flow through WebSocket events.

Architecture

Client (Browser)
    ↓ Socket.io
ConnectionManager
    ↓
Event Handlers
    ↓
Game Engine → StateManager → Database
    ↓
Broadcast to All Players

Structure

app/websocket/
├── connection_manager.py  # Connection lifecycle & broadcasting
└── handlers.py            # Event handler registration (20 handlers)

ConnectionManager

Manages connections, rooms, and broadcasting.

State:

  • user_sessions: Dict[str, str] - sid → user_id
  • game_rooms: Dict[str, Set[str]] - game_id → sids

Key Methods:

await manager.connect(sid, user_id)
await manager.disconnect(sid)
await manager.join_game(sid, game_id, role)
await manager.broadcast_to_game(game_id, event, data)
await manager.emit_to_user(sid, event, data)

Event Handlers (20 Total)

Connection Events

  • connect - JWT authentication
  • disconnect - Cleanup sessions

Game Flow

  • join_game - Join game room
  • start_game - Initialize game state
  • get_game_state - Request current state
  • get_box_score - Get statistics

Decision Submission

  • submit_defensive_decision - Defense strategy
  • submit_offensive_decision - Offense strategy

Manual Outcome Flow

  • roll_dice - Roll dice for play
  • submit_manual_outcome - Submit card result

Substitutions

  • submit_pinch_hitter - Batting substitution
  • submit_pitching_change - Pitcher substitution
  • submit_defensive_replacement - Field substitution

Uncapped Hit Decisions

  • submit_uncapped_lead_advance - Lead runner advance choice (offensive)
  • submit_uncapped_defensive_throw - Throw to base choice (defensive)
  • submit_uncapped_trail_advance - Trail runner advance choice (offensive)
  • submit_uncapped_throw_target - Throw at lead or trail (defensive)
  • submit_uncapped_safe_out - Declare safe or out from card (offensive)

Lineup

  • get_lineup - Get team lineup

Event Pattern

@sio.event
async def event_name(sid, data):
    try:
        # 1. Validate input
        # 2. Get game state
        # 3. Process action
        # 4. Broadcast result
    except Exception as e:
        await emit_error(sid, str(e))

Key Events Emitted

Event Recipient Purpose
game_state_update Room State changed
play_resolved Room Play completed
decision_required User Need input
error User Error occurred
dice_rolled Room Dice result

Common Tasks

Broadcasting State Update

state_dict = state.model_dump()
await manager.broadcast_to_game(game_id, "game_state_update", state_dict)

Error Handling

await manager.emit_to_user(sid, "error", {"message": str(e)})

References

  • Protocol Spec: ../../.claude/implementation/websocket-protocol.md
  • Game Engine: See ../core/CLAUDE.md
  • Frontend Integration: See frontend-sba/composables/CLAUDE.md

Handlers: 20/20 implemented | Updated: 2026-02-11