Compare commits

...

6 Commits

Author SHA1 Message Date
semantic-release-bot
f3080b50d9 chore(release): 3.17.11 [skip ci]
## [3.17.11](https://github.com/antialias/soroban-abacus-flashcards/compare/v3.17.10...v3.17.11) (2025-10-15)

### Bug Fixes

* **memory-quiz:** fix playMode persistence by updating validator ([de0efd5](de0efd5932))
2025-10-15 17:51:21 +00:00
Thomas Hallock
de0efd5932 fix(memory-quiz): fix playMode persistence by updating validator
ROOT CAUSE FOUND:
The MemoryQuizGameValidator.getInitialState() method was hardcoding
playMode to 'cooperative' and not accepting it as a config parameter.

Even though socket-server.ts was passing playMode from the saved config,
the validator's TypeScript signature didn't include it:

BEFORE:
```typescript
getInitialState(config: {
  selectedCount: number
  displayTime: number
  selectedDifficulty: DifficultyLevel
}): SorobanQuizState {
  return {
    // ...
    playMode: 'cooperative',  // ← ALWAYS HARDCODED!
  }
}
```

AFTER:
```typescript
getInitialState(config: {
  selectedCount: number
  displayTime: number
  selectedDifficulty: DifficultyLevel
  playMode?: 'cooperative' | 'competitive'  // ← NEW!
}): SorobanQuizState {
  return {
    // ...
    playMode: config.playMode || 'cooperative',  // ← USES CONFIG VALUE!
  }
}
```

Also added comprehensive debug logging throughout the flow:
- socket-server.ts: logs room.gameConfig, extracted config, and resulting playMode
- RoomMemoryQuizProvider.tsx: logs roomData.gameConfig and merged state
- MemoryQuizGameValidator.ts: logs config received and playMode returned

This will help identify any remaining persistence issues.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-15 12:50:25 -05:00
semantic-release-bot
c9e5c473e6 chore(release): 3.17.10 [skip ci]
## [3.17.10](https://github.com/antialias/soroban-abacus-flashcards/compare/v3.17.9...v3.17.10) (2025-10-15)

### Bug Fixes

* **memory-quiz:** persist playMode setting across game switches ([487ca7f](487ca7fba6))
2025-10-15 17:47:48 +00:00
Thomas Hallock
487ca7fba6 fix(memory-quiz): persist playMode setting across game switches
The socket-server was missing playMode when creating the initial session
for memory-quiz games. It was only loading selectedCount, displayTime, and
selectedDifficulty from the saved config, causing playMode to always reset
to the default 'cooperative' even when 'competitive' was saved.

Now includes playMode in the initial state config:
- selectedCount
- displayTime
- selectedDifficulty
- playMode (NEW)

This ensures the playMode setting persists when users:
1. Set playMode to 'competitive'
2. Go back to game selection
3. Select memory-quiz again
4. PlayMode is still 'competitive' (not reset to 'cooperative')

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-15 12:46:48 -05:00
semantic-release-bot
8f7eebce4b chore(release): 3.17.9 [skip ci]
## [3.17.9](https://github.com/antialias/soroban-abacus-flashcards/compare/v3.17.8...v3.17.9) (2025-10-15)

### Bug Fixes

* **arcade:** read nested gameConfig correctly when creating sessions ([94ef392](94ef39234d))
2025-10-15 17:45:11 +00:00
Thomas Hallock
94ef39234d fix(arcade): read nested gameConfig correctly when creating sessions
The session initialization was looking for settings at the wrong level:
- Was reading: room.gameConfig.gameType (undefined, falls back to default)
- Should read: room.gameConfig.matching.gameType (saved value)

gameConfig is structured as:
{
  "matching": { "gameType": "...", "difficulty": ..., "turnTimer": ... },
  "memory-quiz": { "selectedCount": ..., "displayTime": ..., ... }
}

This caused the session to be created with default settings even though
the settings were saved in the database. The client would load the correct
settings from roomData.gameConfig, but then the socket would immediately
overwrite them with the session's default state.

Now properly accesses the nested config for each game type.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-15 12:44:10 -05:00
5 changed files with 110 additions and 21 deletions

View File

@@ -1,3 +1,24 @@
## [3.17.11](https://github.com/antialias/soroban-abacus-flashcards/compare/v3.17.10...v3.17.11) (2025-10-15)
### Bug Fixes
* **memory-quiz:** fix playMode persistence by updating validator ([de0efd5](https://github.com/antialias/soroban-abacus-flashcards/commit/de0efd59321ec779cddb900724035884290419b7))
## [3.17.10](https://github.com/antialias/soroban-abacus-flashcards/compare/v3.17.9...v3.17.10) (2025-10-15)
### Bug Fixes
* **memory-quiz:** persist playMode setting across game switches ([487ca7f](https://github.com/antialias/soroban-abacus-flashcards/commit/487ca7fba62e370c85bc3779ca8a96eb2c2cc3e3))
## [3.17.9](https://github.com/antialias/soroban-abacus-flashcards/compare/v3.17.8...v3.17.9) (2025-10-15)
### Bug Fixes
* **arcade:** read nested gameConfig correctly when creating sessions ([94ef392](https://github.com/antialias/soroban-abacus-flashcards/commit/94ef39234d362b82e032cb69d3561b9fcb436eaf))
## [3.17.8](https://github.com/antialias/soroban-abacus-flashcards/compare/v3.17.7...v3.17.8) (2025-10-15)

View File

@@ -210,19 +210,32 @@ export function RoomMemoryQuizProvider({ children }: { children: ReactNode }) {
// Settings are scoped by game name to preserve settings when switching games
const mergedInitialState = useMemo(() => {
const gameConfig = roomData?.gameConfig as Record<string, any> | null | undefined
console.log('[RoomMemoryQuizProvider] Initializing - gameConfig:', gameConfig)
console.log(
'[RoomMemoryQuizProvider] Initializing - Full roomData.gameConfig:',
JSON.stringify(gameConfig, null, 2)
)
if (!gameConfig) {
console.log('[RoomMemoryQuizProvider] No gameConfig, using initialState')
console.log(
'[RoomMemoryQuizProvider] No gameConfig, using initialState with playMode:',
initialState.playMode
)
return initialState
}
// Get settings for this specific game (memory-quiz)
const savedConfig = gameConfig['memory-quiz'] as Record<string, any> | null | undefined
console.log('[RoomMemoryQuizProvider] Loading saved config for memory-quiz:', savedConfig)
console.log(
'[RoomMemoryQuizProvider] Extracted memory-quiz config:',
JSON.stringify(savedConfig, null, 2)
)
console.log('[RoomMemoryQuizProvider] savedConfig.playMode value:', savedConfig?.playMode)
if (!savedConfig) {
console.log('[RoomMemoryQuizProvider] No saved config for memory-quiz, using initialState')
console.log(
'[RoomMemoryQuizProvider] No saved config for memory-quiz, using initialState with playMode:',
initialState.playMode
)
return initialState
}
@@ -234,12 +247,20 @@ export function RoomMemoryQuizProvider({ children }: { children: ReactNode }) {
selectedDifficulty: savedConfig.selectedDifficulty ?? initialState.selectedDifficulty,
playMode: savedConfig.playMode ?? initialState.playMode,
}
console.log('[RoomMemoryQuizProvider] Merged state:', {
selectedCount: merged.selectedCount,
displayTime: merged.displayTime,
selectedDifficulty: merged.selectedDifficulty,
playMode: merged.playMode,
})
console.log(
'[RoomMemoryQuizProvider] Merged state:',
JSON.stringify(
{
selectedCount: merged.selectedCount,
displayTime: merged.displayTime,
selectedDifficulty: merged.selectedDifficulty,
playMode: merged.playMode,
},
null,
2
)
)
console.log('[RoomMemoryQuizProvider] Final merged.playMode:', merged.playMode)
return merged
}, [roomData?.gameConfig])

View File

@@ -405,8 +405,14 @@ export class MemoryQuizGameValidator
selectedCount: number
displayTime: number
selectedDifficulty: DifficultyLevel
playMode?: 'cooperative' | 'competitive'
}): SorobanQuizState {
return {
console.log(
'[MemoryQuizValidator] getInitialState called with config:',
JSON.stringify(config, null, 2)
)
const initialState: SorobanQuizState = {
cards: [],
quizCards: [],
correctAnswers: [],
@@ -422,7 +428,7 @@ export class MemoryQuizGameValidator
activePlayers: [],
playerMetadata: {},
playerScores: {},
playMode: 'cooperative',
playMode: config.playMode || 'cooperative',
numberFoundBy: {},
// UI state
gamePhase: 'setup',
@@ -433,6 +439,9 @@ export class MemoryQuizGameValidator
testingMode: false,
showOnScreenKeyboard: false,
}
console.log('[MemoryQuizValidator] getInitialState returning playMode:', initialState.playMode)
return initialState
}
}

View File

@@ -84,17 +84,45 @@ export function initializeSocketServer(httpServer: HTTPServer) {
// Different games have different initial configs
let initialState: any
if (room.gameName === 'matching') {
// Access nested gameConfig: { matching: { gameType, difficulty, turnTimer } }
const matchingConfig = (room.gameConfig as any)?.matching || {}
initialState = validator.getInitialState({
difficulty: (room.gameConfig as any)?.difficulty || 6,
gameType: (room.gameConfig as any)?.gameType || 'abacus-numeral',
turnTimer: (room.gameConfig as any)?.turnTimer || 30,
difficulty: matchingConfig.difficulty || 6,
gameType: matchingConfig.gameType || 'abacus-numeral',
turnTimer: matchingConfig.turnTimer || 30,
})
} else if (room.gameName === 'memory-quiz') {
initialState = validator.getInitialState({
selectedCount: (room.gameConfig as any)?.selectedCount || 5,
displayTime: (room.gameConfig as any)?.displayTime || 2.0,
selectedDifficulty: (room.gameConfig as any)?.selectedDifficulty || 'easy',
})
// Access nested gameConfig: { 'memory-quiz': { selectedCount, displayTime, selectedDifficulty, playMode } }
const memoryQuizConfig = (room.gameConfig as any)?.['memory-quiz'] || {}
console.log(
'[join-arcade-session] memory-quiz - Full room.gameConfig:',
JSON.stringify(room.gameConfig, null, 2)
)
console.log(
'[join-arcade-session] memory-quiz - Extracted memoryQuizConfig:',
JSON.stringify(memoryQuizConfig, null, 2)
)
console.log(
'[join-arcade-session] memory-quiz - playMode from config:',
memoryQuizConfig.playMode
)
const configToPass = {
selectedCount: memoryQuizConfig.selectedCount || 5,
displayTime: memoryQuizConfig.displayTime || 2.0,
selectedDifficulty: memoryQuizConfig.selectedDifficulty || 'easy',
playMode: memoryQuizConfig.playMode || 'cooperative',
}
console.log(
'[join-arcade-session] memory-quiz - Config being passed to getInitialState:',
JSON.stringify(configToPass, null, 2)
)
initialState = validator.getInitialState(configToPass)
console.log(
'[join-arcade-session] memory-quiz - initialState.playMode after getInitialState:',
initialState.playMode
)
} else {
// Fallback for other games
initialState = validator.getInitialState(room.gameConfig || {})
@@ -124,7 +152,17 @@ export function initializeSocketServer(httpServer: HTTPServer) {
roomId,
version: session.version,
sessionUserId: session.userId,
gameName: session.currentGame,
})
// Log playMode specifically for memory-quiz
if (session.currentGame === 'memory-quiz') {
console.log(
'[join-arcade-session] memory-quiz session - gameState.playMode:',
(session.gameState as any).playMode
)
}
socket.emit('session-state', {
gameState: session.gameState,
currentGame: session.currentGame,

View File

@@ -1,6 +1,6 @@
{
"name": "soroban-monorepo",
"version": "3.17.8",
"version": "3.17.11",
"private": true,
"description": "Beautiful Soroban Flashcard Generator - Monorepo",
"workspaces": [