feat(card-sorting): add GameMode type system for multiplayer support

Add GameMode type ('solo' | 'collaborative' | 'competitive' | 'relay') to
CardSortingConfig and CardSortingState. This establishes the foundation for
multiplayer features.

Changes:
- Add GameMode type and gameMode field to config/state interfaces
- Add JOIN_COLLABORATIVE_GAME, LEAVE_COLLABORATIVE_GAME, UPDATE_CURSOR_POSITION moves
- Add activePlayers, allPlayerMetadata, cursorPositions to state
- Set default gameMode to 'solo' in config

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Thomas Hallock
2025-10-23 17:05:56 -05:00
parent f7e9965b37
commit fd765335ef
3 changed files with 47 additions and 2 deletions

View File

@@ -32,6 +32,7 @@ const defaultConfig: CardSortingConfig = {
cardCount: 8,
showNumbers: true,
timeLimit: null,
gameMode: 'solo',
}
// Config validation function
@@ -59,6 +60,13 @@ function validateCardSortingConfig(config: unknown): config is CardSortingConfig
}
}
// Validate gameMode (optional, defaults to 'solo')
if ('gameMode' in c) {
if (!['solo', 'collaborative', 'competitive', 'relay'].includes(c.gameMode as string)) {
return false
}
}
return true
}

View File

@@ -15,10 +15,13 @@ export interface PlayerMetadata {
// Configuration
// ============================================================================
export type GameMode = 'solo' | 'collaborative' | 'competitive' | 'relay'
export interface CardSortingConfig extends GameConfig {
cardCount: 5 | 8 | 12 | 15 // Difficulty (number of cards)
showNumbers: boolean // Allow reveal numbers button
timeLimit: number | null // Optional time limit (seconds), null = unlimited
gameMode: GameMode // Game mode (solo, collaborative, competitive, relay)
}
// ============================================================================
@@ -40,6 +43,7 @@ export interface CardPosition {
rotation: number // degrees (-15 to 15)
zIndex: number
draggedByPlayerId?: string // ID of player currently dragging this card
draggedByWindowId?: string // ID of specific window/tab doing the drag
}
export interface PlacedCard {
@@ -68,13 +72,16 @@ export interface CardSortingState extends GameState {
cardCount: 5 | 8 | 12 | 15
showNumbers: boolean
timeLimit: number | null
gameMode: GameMode
// Game phase
gamePhase: GamePhase
// Player & timing
playerId: string // Single player ID
playerId: string // Single player ID (primary player in solo/collaborative)
playerMetadata: PlayerMetadata // Player display info
activePlayers: string[] // All active player IDs (for collaborative mode)
allPlayerMetadata: Map<string, PlayerMetadata> // Metadata for all players
gameStartTime: number | null
gameEndTime: number | null
@@ -85,6 +92,9 @@ export interface CardSortingState extends GameState {
placedCards: (SortingCard | null)[] // Array of N slots (null = empty)
cardPositions: CardPosition[] // Viewport-relative positions for all cards
// Multiplayer cursors (collaborative mode)
cursorPositions: Map<string, { x: number; y: number }> // Player ID -> cursor position
// UI state (client-only, not in server state)
selectedCardId: string | null // Currently selected card
numbersRevealed: boolean // If player revealed numbers
@@ -178,7 +188,7 @@ export type CardSortingMove =
userId: string
timestamp: number
data: {
field: 'cardCount' | 'showNumbers' | 'timeLimit'
field: 'cardCount' | 'showNumbers' | 'timeLimit' | 'gameMode'
value: unknown
}
}
@@ -198,6 +208,32 @@ export type CardSortingMove =
positions: CardPosition[]
}
}
| {
type: 'JOIN_COLLABORATIVE_GAME'
playerId: string
userId: string
timestamp: number
data: {
playerMetadata: PlayerMetadata
}
}
| {
type: 'LEAVE_COLLABORATIVE_GAME'
playerId: string
userId: string
timestamp: number
data: Record<string, never>
}
| {
type: 'UPDATE_CURSOR_POSITION'
playerId: string
userId: string
timestamp: number
data: {
x: number // % of viewport width (0-100)
y: number // % of viewport height (0-100)
}
}
// ============================================================================
// Component Props

View File

@@ -139,6 +139,7 @@ export const DEFAULT_CARD_SORTING_CONFIG: CardSortingGameConfig = {
cardCount: 8,
showNumbers: true,
timeLimit: null,
gameMode: 'solo',
}
export const DEFAULT_COMPLEMENT_RACE_CONFIG: ComplementRaceGameConfig = {