diff --git a/apps/web/.claude/settings.local.json b/apps/web/.claude/settings.local.json index f15bb8f7..46f26e77 100644 --- a/apps/web/.claude/settings.local.json +++ b/apps/web/.claude/settings.local.json @@ -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": [] diff --git a/apps/web/src/arcade-games/know-your-world/Validator.ts b/apps/web/src/arcade-games/know-your-world/Validator.ts index d0a725a9..afd45ef0 100644 --- a/apps/web/src/arcade-games/know-your-world/Validator.ts +++ b/apps/web/src/arcade-games/know-your-world/Validator.ts @@ -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 diff --git a/apps/web/src/arcade-games/know-your-world/components/DrillDownMapSelector.tsx b/apps/web/src/arcade-games/know-your-world/components/DrillDownMapSelector.tsx index 3ef3de72..388abbc7 100644 --- a/apps/web/src/arcade-games/know-your-world/components/DrillDownMapSelector.tsx +++ b/apps/web/src/arcade-games/know-your-world/components/DrillDownMapSelector.tsx @@ -853,11 +853,7 @@ export function DrillDownMapSelector({ {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({ })} )} - ) } diff --git a/apps/web/src/arcade-games/know-your-world/components/PlayingPhase.test.tsx b/apps/web/src/arcade-games/know-your-world/components/PlayingPhase.test.tsx index 34fdb113..ab11472e 100644 --- a/apps/web/src/arcade-games/know-your-world/components/PlayingPhase.test.tsx +++ b/apps/web/src/arcade-games/know-your-world/components/PlayingPhase.test.tsx @@ -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() - 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, diff --git a/apps/web/src/arcade-games/know-your-world/components/PlayingPhase.tsx b/apps/web/src/arcade-games/know-your-world/components/PlayingPhase.tsx index 5ad9a380..4e1c1ea3 100644 --- a/apps/web/src/arcade-games/know-your-world/components/PlayingPhase.tsx +++ b/apps/web/src/arcade-games/know-your-world/components/PlayingPhase.tsx @@ -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 ( diff --git a/apps/web/src/arcade-games/know-your-world/maps.ts b/apps/web/src/arcade-games/know-your-world/maps.ts index 3f53b1b6..6c7f83ff 100644 --- a/apps/web/src/arcade-games/know-your-world/maps.ts +++ b/apps/web/src/arcade-games/know-your-world/maps.ts @@ -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 = { +export const REGION_SIZE_CONFIG: Record< + RegionSize, + { label: string; emoji: string; description: string } +> = { huge: { label: 'Major', emoji: '🌍',