strat-gameplay-webapp/frontend-sba/types/game.ts
Cal Corum 2b8fea36a8 CLAUDE: Redesign dice display with team colors and consolidate player cards
Backend:
- Add home_team_dice_color and away_team_dice_color to GameState model
- Extract dice_color from game metadata in StateManager (default: cc0000)
- Add runners_on_base param to roll_ab for chaos check skipping

Frontend - Dice Display:
- Create DiceShapes.vue with SVG d6 (square) and d20 (hexagon) shapes
- Apply home team's dice_color to d6 dice, white for resolution d20
- Show chaos d20 in amber only when WP/PB check triggered
- Add automatic text contrast based on color luminance
- Reduce blank space and remove info bubble from dice results

Frontend - Player Cards:
- Consolidate pitcher/batter cards to single location below diamond
- Add active card highlighting based on dice roll (d6_one: 1-3=batter, 4-6=pitcher)
- New card header format: [Team] Position [Name] with full card image
- Remove redundant card displays from GameBoard and GameplayPanel
- Enlarge PlayerCardModal on desktop (max-w-3xl at 1024px+)

Tests:
- Add DiceShapes.spec.ts with 34 tests for color calculations and rendering
- Update DiceRoller.spec.ts for new DiceShapes integration
- Fix test_roll_dice_success for new runners_on_base parameter

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-24 00:16:32 -06:00

357 lines
8.1 KiB
TypeScript

/**
* Game State and Play Result Types
*
* TypeScript definitions matching backend Pydantic models exactly.
* These types ensure type safety between frontend and backend.
*
* Backend Reference: app/models/game_models.py
*/
/**
* Game status enumeration
*/
export type GameStatus = 'pending' | 'active' | 'paused' | 'completed' | 'abandoned'
/**
* Inning half
*/
export type InningHalf = 'top' | 'bottom'
/**
* Game mode
*/
export type GameMode = 'live' | 'async' | 'vs_ai'
/**
* Game visibility
*/
export type GameVisibility = 'public' | 'private'
/**
* League identifier
*/
export type LeagueId = 'sba' | 'pd'
/**
* Decision phase in the play workflow
*
* Standardized naming (2025-01-21): Uses backend convention 'awaiting_*'
* for clarity about what action is pending.
*/
export type DecisionPhase = 'awaiting_defensive' | 'awaiting_stolen_base' | 'awaiting_offensive' | 'resolution' | 'complete'
/**
* Lineup player state - represents a player in the game
* Backend: LineupPlayerState
*/
export interface LineupPlayerState {
lineup_id: number
card_id: number
position: string
batting_order: number | null
is_active: boolean
position_rating?: {
range: number
error: number
innings: number
} | null
}
/**
* Core game state - complete representation of active game
* Backend: GameState
*/
export interface GameState {
// Identity
game_id: string
league_id: LeagueId
// Teams
home_team_id: number
away_team_id: number
home_team_is_ai: boolean
away_team_is_ai: boolean
// Team display info (from game_metadata, stored at creation time)
home_team_name?: string | null // Full name: "Chicago Cyclones"
home_team_abbrev?: string | null // Abbreviation: "CHC"
home_team_color?: string | null // Hex color without #: "ff5349"
home_team_dice_color?: string | null // Dice color hex without #, default "cc0000"
home_team_thumbnail?: string | null // Team logo URL
away_team_name?: string | null
away_team_abbrev?: string | null
away_team_color?: string | null
away_team_dice_color?: string | null // Dice color hex without #, default "cc0000"
away_team_thumbnail?: string | null
// Creator (for demo/testing - creator can control home team)
creator_discord_id: string | null
// Resolution mode
auto_mode: boolean
// Game state
status: GameStatus
inning: number
half: InningHalf
outs: number
balls: number
strikes: number
home_score: number
away_score: number
// Runners (direct references)
on_first: LineupPlayerState | null
on_second: LineupPlayerState | null
on_third: LineupPlayerState | null
// Current players
current_batter: LineupPlayerState
current_pitcher: LineupPlayerState | null
current_catcher: LineupPlayerState | null
current_on_base_code: number
// Batting order tracking
away_team_batter_idx: number // 0-8
home_team_batter_idx: number // 0-8
// Decision tracking
pending_decision: DecisionPhase | null
decision_phase: DecisionPhase
decisions_this_play: Record<string, boolean>
pending_defensive_decision: DefensiveDecision | null
pending_offensive_decision: OffensiveDecision | null
// Manual mode
pending_manual_roll: RollData | null
// Play history
play_count: number
last_play_result: string | null
// Timestamps
created_at: string
started_at: string | null
completed_at: string | null
}
/**
* Defensive strategic decision
* Backend: DefensiveDecision
*/
export interface DefensiveDecision {
infield_depth: 'infield_in' | 'normal' | 'corners_in'
outfield_depth: 'normal' | 'shallow'
hold_runners: number[] // Bases to hold (e.g., [1, 3])
}
/**
* Offensive strategic decision
* Backend: OffensiveDecision
*
* Session 2 Update (2025-01-14): Replaced approach/hit_and_run/bunt_attempt with action field.
*/
export interface OffensiveDecision {
action: 'swing_away' | 'steal' | 'check_jump' | 'hit_and_run' | 'sac_bunt' | 'squeeze_bunt'
steal_attempts: number[] // Bases to steal (2, 3, or 4) - only used when action="steal"
}
/**
* Dice roll data from server
* Backend: AbRoll (simplified for frontend)
*/
export interface RollData {
roll_id: string
d6_one: number
d6_two_a: number
d6_two_b: number
d6_two_total: number
chaos_d20: number
resolution_d20: number
check_wild_pitch: boolean
check_passed_ball: boolean
chaos_check_skipped: boolean // True when no runners on base (WP/PB irrelevant)
timestamp: string
}
/**
* Play outcome enumeration (matches backend PlayOutcome enum values)
* Note: These are the STRING VALUES, not enum names
*/
export type PlayOutcome =
// Standard outs
| 'strikeout'
| 'groundball_a'
| 'groundball_b'
| 'groundball_c'
| 'flyout_a'
| 'flyout_b'
| 'flyout_bq'
| 'flyout_c'
| 'lineout'
| 'popout'
// Walks and hits by pitch
| 'walk'
| 'intentional_walk'
| 'hbp'
// Hits (capped - specific bases)
| 'single_1'
| 'single_2'
| 'single_uncapped'
| 'double_2'
| 'double_3'
| 'double_uncapped'
| 'triple'
| 'homerun'
// Interrupt plays (baserunning events)
| 'stolen_base'
| 'caught_stealing'
| 'wild_pitch'
| 'passed_ball'
| 'balk'
| 'pick_off'
// Errors
| 'error'
| 'x_check'
// Ballpark power (PD specific)
| 'bp_homerun'
| 'bp_flyout'
| 'bp_single'
| 'bp_lineout'
/**
* Runner advancement during a play
*/
export interface RunnerAdvancement {
from: number // 0=batter, 1-3=bases
to: number // 1-4=bases (4=home/scored)
lineup_id: number // Player's lineup ID for name lookup
is_out?: boolean // Runner was out during advancement (renamed from 'out')
}
/**
* Play resolution result from server
* Backend: PlayResult
*/
export interface PlayResult {
// Play identification
play_number: number
inning?: number // Inning when play occurred
half?: InningHalf // 'top' or 'bottom'
outcome: PlayOutcome
// Play description
description: string
hit_type?: string
// Results
outs_recorded: number
runs_scored: number
// Player identification for display
batter_lineup_id?: number // Batter's lineup ID for name lookup
// Runner advancement
runners_advanced: RunnerAdvancement[]
batter_result: number | null // Where batter ended up (1-4, null=out)
// Updated state snapshot
new_state: Partial<GameState>
// Categorization helpers
is_hit: boolean
is_out: boolean
is_walk: boolean
is_strikeout: boolean
// Roll reference (if manual mode)
roll_id?: string
// X-Check details (if defensive play)
x_check_details?: XCheckResult
}
/**
* X-Check resolution audit trail
*/
export interface XCheckResult {
check_position: string
defense_range: number
error_rating: number
d20_roll: number
error_3d6: number
base_result: string
error_result: string
final_outcome: PlayOutcome
held_runners: string[]
}
/**
* Decision prompt from server
* Backend: DecisionPrompt (WebSocket event)
*/
export interface DecisionPrompt {
phase: DecisionPhase
role: 'home' | 'away'
timeout_seconds: number
options?: string[]
message?: string
}
/**
* Manual outcome submission to server
* Backend: ManualOutcomeSubmission
*/
export interface ManualOutcomeSubmission {
outcome: PlayOutcome
hit_location?: string // Required for groundballs/flyballs
}
/**
* Game list item for active/completed games
* Note: Field names match backend GameListItem response
*/
export interface GameListItem {
game_id: string
league_id: LeagueId
home_team_id: number
away_team_id: number
status: GameStatus
// Enriched team info from backend
home_team_name?: string
away_team_name?: string
home_team_abbrev?: string
away_team_abbrev?: string
home_score: number
away_score: number
inning?: number
half?: InningHalf
// External schedule reference (for linking to SBA/PD schedule systems)
schedule_game_id?: number
}
/**
* Game creation request
*/
export interface CreateGameRequest {
league_id: LeagueId
home_team_id: number
away_team_id: number
game_mode: GameMode
visibility: GameVisibility
}
/**
* Game creation response
*/
export interface CreateGameResponse {
game_id: string
status: GameStatus
created_at: string
}