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 exec:*)",
|
||||
"Bash(node --input-type=module -e:*)",
|
||||
"Bash(npm test:*)"
|
||||
"Bash(npm test:*)",
|
||||
"Bash(npx tsx:*)",
|
||||
"Bash(tsc:*)"
|
||||
],
|
||||
"deny": [],
|
||||
"ask": []
|
||||
|
|
|
|||
|
|
@ -553,7 +553,8 @@ export class KnowYourWorldValidator
|
|||
// Determine re-ask position based on assistance level
|
||||
// Guided/Helpful: re-ask soon (after 2-3 regions) to reinforce learning
|
||||
// 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
|
||||
|
||||
// Build new regions queue: take next regions, then insert given-up region at appropriate position
|
||||
|
|
|
|||
|
|
@ -853,11 +853,7 @@ export function DrillDownMapSelector({
|
|||
<span
|
||||
className={css({
|
||||
fontWeight: '500',
|
||||
color: isChecked
|
||||
? 'white'
|
||||
: isDark
|
||||
? 'gray.200'
|
||||
: 'gray.700',
|
||||
color: isChecked ? 'white' : isDark ? 'gray.200' : 'gray.700',
|
||||
})}
|
||||
>
|
||||
{config.label}
|
||||
|
|
@ -908,8 +904,7 @@ export function DrillDownMapSelector({
|
|||
{peers.map((peer) => {
|
||||
// Check if this is a planet (joke at world level)
|
||||
const isPlanet = 'isPlanet' in peer && peer.isPlanet
|
||||
const planetData =
|
||||
'planetData' in peer ? (peer.planetData as PlanetData | null) : null
|
||||
const planetData = 'planetData' in peer ? (peer.planetData as PlanetData | null) : null
|
||||
|
||||
// Calculate viewBox for this peer's continent (only for non-planets)
|
||||
const peerContinentId = peer.path[0]
|
||||
|
|
@ -1105,7 +1100,6 @@ export function DrillDownMapSelector({
|
|||
})}
|
||||
</div>
|
||||
)}
|
||||
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,7 +9,8 @@ vi.mock('../Provider', () => ({
|
|||
state: {
|
||||
selectedMap: 'world' as const,
|
||||
selectedContinent: 'all',
|
||||
difficulty: 'easy',
|
||||
includeSizes: ['huge', 'large', 'medium'],
|
||||
assistanceLevel: 'helpful',
|
||||
regionsFound: ['france', 'germany'],
|
||||
currentPrompt: 'spain',
|
||||
gameMode: 'cooperative' as const,
|
||||
|
|
@ -20,7 +21,7 @@ vi.mock('../Provider', () => ({
|
|||
}))
|
||||
|
||||
vi.mock('../maps', () => ({
|
||||
getFilteredMapDataSync: () =>
|
||||
getFilteredMapDataBySizesSync: () =>
|
||||
({
|
||||
id: 'world',
|
||||
name: 'World Map',
|
||||
|
|
@ -107,7 +108,8 @@ describe('PlayingPhase', () => {
|
|||
state: {
|
||||
selectedMap: 'world' as const,
|
||||
selectedContinent: 'all',
|
||||
difficulty: 'easy',
|
||||
includeSizes: ['huge', 'large', 'medium'],
|
||||
assistanceLevel: 'helpful',
|
||||
regionsFound: ['france', 'germany'],
|
||||
currentPrompt: null,
|
||||
gameMode: 'cooperative' as const,
|
||||
|
|
@ -142,7 +144,8 @@ describe('PlayingPhase', () => {
|
|||
state: {
|
||||
selectedMap: 'world' as const,
|
||||
selectedContinent: 'all',
|
||||
difficulty: 'easy',
|
||||
includeSizes: ['huge', 'large', 'medium'],
|
||||
assistanceLevel: 'helpful',
|
||||
regionsFound: [],
|
||||
currentPrompt: 'spain',
|
||||
gameMode: 'cooperative' as const,
|
||||
|
|
@ -159,8 +162,8 @@ describe('PlayingPhase', () => {
|
|||
expect(mockClickRegion).toHaveBeenCalledWith('spain', 'Spain')
|
||||
})
|
||||
|
||||
it('uses correct map data from getFilteredMapDataSync', () => {
|
||||
const mockGetFilteredMapDataSync = vi.fn().mockReturnValue({
|
||||
it('uses correct map data from getFilteredMapDataBySizesSync', () => {
|
||||
const mockGetFilteredMapDataBySizesSync = vi.fn().mockReturnValue({
|
||||
id: 'usa',
|
||||
name: 'USA Map',
|
||||
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 />)
|
||||
|
||||
expect(mockGetFilteredMapDataSync).toHaveBeenCalledWith('world', 'all', 'easy')
|
||||
expect(mockGetFilteredMapDataBySizesSync).toHaveBeenCalledWith('world', 'all', [
|
||||
'huge',
|
||||
'large',
|
||||
'medium',
|
||||
])
|
||||
})
|
||||
})
|
||||
|
||||
|
|
@ -184,7 +192,8 @@ describe('PlayingPhase - Different Scenarios', () => {
|
|||
state: {
|
||||
selectedMap: 'world' as const,
|
||||
selectedContinent: 'all',
|
||||
difficulty: 'easy',
|
||||
includeSizes: ['huge', 'large', 'medium'],
|
||||
assistanceLevel: 'helpful',
|
||||
regionsFound: [],
|
||||
currentPrompt: 'spain',
|
||||
gameMode: 'cooperative' as const,
|
||||
|
|
@ -203,7 +212,8 @@ describe('PlayingPhase - Different Scenarios', () => {
|
|||
state: {
|
||||
selectedMap: 'world' as const,
|
||||
selectedContinent: 'all',
|
||||
difficulty: 'easy',
|
||||
includeSizes: ['huge', 'large', 'medium'],
|
||||
assistanceLevel: 'helpful',
|
||||
regionsFound: ['spain', 'italy', 'portugal'],
|
||||
currentPrompt: null,
|
||||
gameMode: 'cooperative' as const,
|
||||
|
|
@ -217,12 +227,13 @@ describe('PlayingPhase - Different Scenarios', () => {
|
|||
expect(screen.getByText('Progress: 3/3')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('renders with hard difficulty', () => {
|
||||
it('renders with no assistance mode', () => {
|
||||
vi.mocked(vi.importActual('../Provider')).useKnowYourWorld = () => ({
|
||||
state: {
|
||||
selectedMap: 'world' as const,
|
||||
selectedContinent: 'all',
|
||||
difficulty: 'hard',
|
||||
includeSizes: ['huge', 'large', 'medium', 'small', 'tiny'],
|
||||
assistanceLevel: 'none',
|
||||
regionsFound: [],
|
||||
currentPrompt: 'luxembourg',
|
||||
gameMode: 'race' as const,
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import { useCallback, useMemo } from 'react'
|
|||
import { css } from '@styled/css'
|
||||
import { Panel, PanelGroup, PanelResizeHandle } from 'react-resizable-panels'
|
||||
import { useKnowYourWorld } from '../Provider'
|
||||
import { getFilteredMapDataSync } from '../maps'
|
||||
import { getFilteredMapDataBySizesSync } from '../maps'
|
||||
import { MapRenderer } from './MapRenderer'
|
||||
import { GameInfoPanel } from './GameInfoPanel'
|
||||
import { useViewerId } from '@/lib/arcade/game-sdk'
|
||||
|
|
@ -39,10 +39,10 @@ export function PlayingPhase() {
|
|||
[localPlayerId, viewerId, sendCursorUpdate]
|
||||
)
|
||||
|
||||
const mapData = getFilteredMapDataSync(
|
||||
const mapData = getFilteredMapDataBySizesSync(
|
||||
state.selectedMap,
|
||||
state.selectedContinent,
|
||||
state.difficulty
|
||||
state.includeSizes
|
||||
)
|
||||
const totalRegions = mapData.regions.length
|
||||
const foundCount = state.regionsFound.length
|
||||
|
|
@ -55,15 +55,22 @@ export function PlayingPhase() {
|
|||
const currentRegionName = currentRegion?.name ?? 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) {
|
||||
console.warn('[PlayingPhase] Prompt not in filtered regions - server/client filter mismatch:', {
|
||||
const errorInfo = {
|
||||
currentPrompt: state.currentPrompt,
|
||||
difficulty: state.difficulty,
|
||||
includeSizes: state.includeSizes,
|
||||
selectedMap: state.selectedMap,
|
||||
selectedContinent: state.selectedContinent,
|
||||
clientFilteredCount: mapData.regions.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 (
|
||||
|
|
|
|||
|
|
@ -226,7 +226,10 @@ export const ALL_REGION_SIZES: RegionSize[] = ['huge', 'large', 'medium', 'small
|
|||
/**
|
||||
* 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: {
|
||||
label: 'Major',
|
||||
emoji: '🌍',
|
||||
|
|
|
|||
Loading…
Reference in New Issue