fix(know-your-world): fix server/client filter mismatch for USA map
PlayingPhase was using deprecated getFilteredMapDataSync with state.difficulty (which was undefined) instead of the new getFilteredMapDataBySizesSync with state.includeSizes. This caused a mismatch where the server generated 50 regions but the client filtered to 35, resulting in prompts showing "..." when the current region wasn't in the client's filtered list. Changes: - Update PlayingPhase to use getFilteredMapDataBySizesSync with includeSizes - Add error throwing (not just warning) when prompt not found in filtered regions - Update test mocks to use new function and state structure 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
a05c4ca5bf
commit
98e74bae3a
|
|
@ -77,7 +77,9 @@
|
||||||
"Bash(docker logs:*)",
|
"Bash(docker logs:*)",
|
||||||
"Bash(docker exec:*)",
|
"Bash(docker exec:*)",
|
||||||
"Bash(node --input-type=module -e:*)",
|
"Bash(node --input-type=module -e:*)",
|
||||||
"Bash(npm test:*)"
|
"Bash(npm test:*)",
|
||||||
|
"Bash(npx tsx:*)",
|
||||||
|
"Bash(tsc:*)"
|
||||||
],
|
],
|
||||||
"deny": [],
|
"deny": [],
|
||||||
"ask": []
|
"ask": []
|
||||||
|
|
|
||||||
|
|
@ -553,7 +553,8 @@ export class KnowYourWorldValidator
|
||||||
// Determine re-ask position based on assistance level
|
// Determine re-ask position based on assistance level
|
||||||
// Guided/Helpful: re-ask soon (after 2-3 regions) to reinforce learning
|
// Guided/Helpful: re-ask soon (after 2-3 regions) to reinforce learning
|
||||||
// Standard/None: re-ask at the end
|
// Standard/None: re-ask at the end
|
||||||
const isHighAssistance = state.assistanceLevel === 'guided' || state.assistanceLevel === 'helpful'
|
const isHighAssistance =
|
||||||
|
state.assistanceLevel === 'guided' || state.assistanceLevel === 'helpful'
|
||||||
const reaskDelay = isHighAssistance ? 3 : state.regionsToFind.length
|
const reaskDelay = isHighAssistance ? 3 : state.regionsToFind.length
|
||||||
|
|
||||||
// Build new regions queue: take next regions, then insert given-up region at appropriate position
|
// Build new regions queue: take next regions, then insert given-up region at appropriate position
|
||||||
|
|
|
||||||
|
|
@ -853,11 +853,7 @@ export function DrillDownMapSelector({
|
||||||
<span
|
<span
|
||||||
className={css({
|
className={css({
|
||||||
fontWeight: '500',
|
fontWeight: '500',
|
||||||
color: isChecked
|
color: isChecked ? 'white' : isDark ? 'gray.200' : 'gray.700',
|
||||||
? 'white'
|
|
||||||
: isDark
|
|
||||||
? 'gray.200'
|
|
||||||
: 'gray.700',
|
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
{config.label}
|
{config.label}
|
||||||
|
|
@ -908,8 +904,7 @@ export function DrillDownMapSelector({
|
||||||
{peers.map((peer) => {
|
{peers.map((peer) => {
|
||||||
// Check if this is a planet (joke at world level)
|
// Check if this is a planet (joke at world level)
|
||||||
const isPlanet = 'isPlanet' in peer && peer.isPlanet
|
const isPlanet = 'isPlanet' in peer && peer.isPlanet
|
||||||
const planetData =
|
const planetData = 'planetData' in peer ? (peer.planetData as PlanetData | null) : null
|
||||||
'planetData' in peer ? (peer.planetData as PlanetData | null) : null
|
|
||||||
|
|
||||||
// Calculate viewBox for this peer's continent (only for non-planets)
|
// Calculate viewBox for this peer's continent (only for non-planets)
|
||||||
const peerContinentId = peer.path[0]
|
const peerContinentId = peer.path[0]
|
||||||
|
|
@ -1105,7 +1100,6 @@ export function DrillDownMapSelector({
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,8 @@ vi.mock('../Provider', () => ({
|
||||||
state: {
|
state: {
|
||||||
selectedMap: 'world' as const,
|
selectedMap: 'world' as const,
|
||||||
selectedContinent: 'all',
|
selectedContinent: 'all',
|
||||||
difficulty: 'easy',
|
includeSizes: ['huge', 'large', 'medium'],
|
||||||
|
assistanceLevel: 'helpful',
|
||||||
regionsFound: ['france', 'germany'],
|
regionsFound: ['france', 'germany'],
|
||||||
currentPrompt: 'spain',
|
currentPrompt: 'spain',
|
||||||
gameMode: 'cooperative' as const,
|
gameMode: 'cooperative' as const,
|
||||||
|
|
@ -20,7 +21,7 @@ vi.mock('../Provider', () => ({
|
||||||
}))
|
}))
|
||||||
|
|
||||||
vi.mock('../maps', () => ({
|
vi.mock('../maps', () => ({
|
||||||
getFilteredMapDataSync: () =>
|
getFilteredMapDataBySizesSync: () =>
|
||||||
({
|
({
|
||||||
id: 'world',
|
id: 'world',
|
||||||
name: 'World Map',
|
name: 'World Map',
|
||||||
|
|
@ -107,7 +108,8 @@ describe('PlayingPhase', () => {
|
||||||
state: {
|
state: {
|
||||||
selectedMap: 'world' as const,
|
selectedMap: 'world' as const,
|
||||||
selectedContinent: 'all',
|
selectedContinent: 'all',
|
||||||
difficulty: 'easy',
|
includeSizes: ['huge', 'large', 'medium'],
|
||||||
|
assistanceLevel: 'helpful',
|
||||||
regionsFound: ['france', 'germany'],
|
regionsFound: ['france', 'germany'],
|
||||||
currentPrompt: null,
|
currentPrompt: null,
|
||||||
gameMode: 'cooperative' as const,
|
gameMode: 'cooperative' as const,
|
||||||
|
|
@ -142,7 +144,8 @@ describe('PlayingPhase', () => {
|
||||||
state: {
|
state: {
|
||||||
selectedMap: 'world' as const,
|
selectedMap: 'world' as const,
|
||||||
selectedContinent: 'all',
|
selectedContinent: 'all',
|
||||||
difficulty: 'easy',
|
includeSizes: ['huge', 'large', 'medium'],
|
||||||
|
assistanceLevel: 'helpful',
|
||||||
regionsFound: [],
|
regionsFound: [],
|
||||||
currentPrompt: 'spain',
|
currentPrompt: 'spain',
|
||||||
gameMode: 'cooperative' as const,
|
gameMode: 'cooperative' as const,
|
||||||
|
|
@ -159,8 +162,8 @@ describe('PlayingPhase', () => {
|
||||||
expect(mockClickRegion).toHaveBeenCalledWith('spain', 'Spain')
|
expect(mockClickRegion).toHaveBeenCalledWith('spain', 'Spain')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('uses correct map data from getFilteredMapDataSync', () => {
|
it('uses correct map data from getFilteredMapDataBySizesSync', () => {
|
||||||
const mockGetFilteredMapDataSync = vi.fn().mockReturnValue({
|
const mockGetFilteredMapDataBySizesSync = vi.fn().mockReturnValue({
|
||||||
id: 'usa',
|
id: 'usa',
|
||||||
name: 'USA Map',
|
name: 'USA Map',
|
||||||
viewBox: '0 0 2000 1000',
|
viewBox: '0 0 2000 1000',
|
||||||
|
|
@ -170,11 +173,16 @@ describe('PlayingPhase', () => {
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
|
|
||||||
vi.mocked(vi.importActual('../maps')).getFilteredMapDataSync = mockGetFilteredMapDataSync
|
vi.mocked(vi.importActual('../maps')).getFilteredMapDataBySizesSync =
|
||||||
|
mockGetFilteredMapDataBySizesSync
|
||||||
|
|
||||||
render(<PlayingPhase />)
|
render(<PlayingPhase />)
|
||||||
|
|
||||||
expect(mockGetFilteredMapDataSync).toHaveBeenCalledWith('world', 'all', 'easy')
|
expect(mockGetFilteredMapDataBySizesSync).toHaveBeenCalledWith('world', 'all', [
|
||||||
|
'huge',
|
||||||
|
'large',
|
||||||
|
'medium',
|
||||||
|
])
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
@ -184,7 +192,8 @@ describe('PlayingPhase - Different Scenarios', () => {
|
||||||
state: {
|
state: {
|
||||||
selectedMap: 'world' as const,
|
selectedMap: 'world' as const,
|
||||||
selectedContinent: 'all',
|
selectedContinent: 'all',
|
||||||
difficulty: 'easy',
|
includeSizes: ['huge', 'large', 'medium'],
|
||||||
|
assistanceLevel: 'helpful',
|
||||||
regionsFound: [],
|
regionsFound: [],
|
||||||
currentPrompt: 'spain',
|
currentPrompt: 'spain',
|
||||||
gameMode: 'cooperative' as const,
|
gameMode: 'cooperative' as const,
|
||||||
|
|
@ -203,7 +212,8 @@ describe('PlayingPhase - Different Scenarios', () => {
|
||||||
state: {
|
state: {
|
||||||
selectedMap: 'world' as const,
|
selectedMap: 'world' as const,
|
||||||
selectedContinent: 'all',
|
selectedContinent: 'all',
|
||||||
difficulty: 'easy',
|
includeSizes: ['huge', 'large', 'medium'],
|
||||||
|
assistanceLevel: 'helpful',
|
||||||
regionsFound: ['spain', 'italy', 'portugal'],
|
regionsFound: ['spain', 'italy', 'portugal'],
|
||||||
currentPrompt: null,
|
currentPrompt: null,
|
||||||
gameMode: 'cooperative' as const,
|
gameMode: 'cooperative' as const,
|
||||||
|
|
@ -217,12 +227,13 @@ describe('PlayingPhase - Different Scenarios', () => {
|
||||||
expect(screen.getByText('Progress: 3/3')).toBeInTheDocument()
|
expect(screen.getByText('Progress: 3/3')).toBeInTheDocument()
|
||||||
})
|
})
|
||||||
|
|
||||||
it('renders with hard difficulty', () => {
|
it('renders with no assistance mode', () => {
|
||||||
vi.mocked(vi.importActual('../Provider')).useKnowYourWorld = () => ({
|
vi.mocked(vi.importActual('../Provider')).useKnowYourWorld = () => ({
|
||||||
state: {
|
state: {
|
||||||
selectedMap: 'world' as const,
|
selectedMap: 'world' as const,
|
||||||
selectedContinent: 'all',
|
selectedContinent: 'all',
|
||||||
difficulty: 'hard',
|
includeSizes: ['huge', 'large', 'medium', 'small', 'tiny'],
|
||||||
|
assistanceLevel: 'none',
|
||||||
regionsFound: [],
|
regionsFound: [],
|
||||||
currentPrompt: 'luxembourg',
|
currentPrompt: 'luxembourg',
|
||||||
gameMode: 'race' as const,
|
gameMode: 'race' as const,
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ import { useCallback, useMemo } from 'react'
|
||||||
import { css } from '@styled/css'
|
import { css } from '@styled/css'
|
||||||
import { Panel, PanelGroup, PanelResizeHandle } from 'react-resizable-panels'
|
import { Panel, PanelGroup, PanelResizeHandle } from 'react-resizable-panels'
|
||||||
import { useKnowYourWorld } from '../Provider'
|
import { useKnowYourWorld } from '../Provider'
|
||||||
import { getFilteredMapDataSync } from '../maps'
|
import { getFilteredMapDataBySizesSync } from '../maps'
|
||||||
import { MapRenderer } from './MapRenderer'
|
import { MapRenderer } from './MapRenderer'
|
||||||
import { GameInfoPanel } from './GameInfoPanel'
|
import { GameInfoPanel } from './GameInfoPanel'
|
||||||
import { useViewerId } from '@/lib/arcade/game-sdk'
|
import { useViewerId } from '@/lib/arcade/game-sdk'
|
||||||
|
|
@ -39,10 +39,10 @@ export function PlayingPhase() {
|
||||||
[localPlayerId, viewerId, sendCursorUpdate]
|
[localPlayerId, viewerId, sendCursorUpdate]
|
||||||
)
|
)
|
||||||
|
|
||||||
const mapData = getFilteredMapDataSync(
|
const mapData = getFilteredMapDataBySizesSync(
|
||||||
state.selectedMap,
|
state.selectedMap,
|
||||||
state.selectedContinent,
|
state.selectedContinent,
|
||||||
state.difficulty
|
state.includeSizes
|
||||||
)
|
)
|
||||||
const totalRegions = mapData.regions.length
|
const totalRegions = mapData.regions.length
|
||||||
const foundCount = state.regionsFound.length
|
const foundCount = state.regionsFound.length
|
||||||
|
|
@ -55,15 +55,22 @@ export function PlayingPhase() {
|
||||||
const currentRegionName = currentRegion?.name ?? null
|
const currentRegionName = currentRegion?.name ?? null
|
||||||
const currentRegionId = currentRegion?.id ?? null
|
const currentRegionId = currentRegion?.id ?? null
|
||||||
|
|
||||||
// Debug warning if prompt not found in filtered regions (indicates server/client filter mismatch)
|
// Error if prompt not found in filtered regions (indicates server/client filter mismatch)
|
||||||
if (state.currentPrompt && !currentRegion) {
|
if (state.currentPrompt && !currentRegion) {
|
||||||
console.warn('[PlayingPhase] Prompt not in filtered regions - server/client filter mismatch:', {
|
const errorInfo = {
|
||||||
currentPrompt: state.currentPrompt,
|
currentPrompt: state.currentPrompt,
|
||||||
difficulty: state.difficulty,
|
includeSizes: state.includeSizes,
|
||||||
|
selectedMap: state.selectedMap,
|
||||||
selectedContinent: state.selectedContinent,
|
selectedContinent: state.selectedContinent,
|
||||||
clientFilteredCount: mapData.regions.length,
|
clientFilteredCount: mapData.regions.length,
|
||||||
serverRegionsToFindCount: state.regionsToFind.length,
|
serverRegionsToFindCount: state.regionsToFind.length,
|
||||||
})
|
clientRegionIds: mapData.regions.map((r) => r.id).slice(0, 10), // First 10 for debugging
|
||||||
|
}
|
||||||
|
console.error('[PlayingPhase] CRITICAL: Prompt not in filtered regions!', errorInfo)
|
||||||
|
throw new Error(
|
||||||
|
`Server/client filter mismatch: prompt "${state.currentPrompt}" not found in client's ${mapData.regions.length} filtered regions. ` +
|
||||||
|
`Server has ${state.regionsToFind.length} regions. includeSizes=${JSON.stringify(state.includeSizes)}`
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
||||||
|
|
@ -226,7 +226,10 @@ export const ALL_REGION_SIZES: RegionSize[] = ['huge', 'large', 'medium', 'small
|
||||||
/**
|
/**
|
||||||
* Display configuration for each region size
|
* Display configuration for each region size
|
||||||
*/
|
*/
|
||||||
export const REGION_SIZE_CONFIG: Record<RegionSize, { label: string; emoji: string; description: string }> = {
|
export const REGION_SIZE_CONFIG: Record<
|
||||||
|
RegionSize,
|
||||||
|
{ label: string; emoji: string; description: string }
|
||||||
|
> = {
|
||||||
huge: {
|
huge: {
|
||||||
label: 'Major',
|
label: 'Major',
|
||||||
emoji: '🌍',
|
emoji: '🌍',
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue