From 8511998d0cde710563d502b650d6678082165ba6 Mon Sep 17 00:00:00 2001 From: Thomas Hallock Date: Wed, 19 Nov 2025 08:21:27 -0600 Subject: [PATCH] docs: add comprehensive precision controls documentation for Know Your World MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add detailed documentation for the advanced precision control system that makes sub-pixel regions (Gibraltar 0.08px, Jersey 0.82px) clickable: - PRECISION_CONTROLS.md: Complete technical deep-dive - Automatic cursor dampening (3-25% speed based on region size) - Auto super-zoom (up to 60x) after 500ms hover on sub-pixel regions - Quick-escape mechanism (>50px/frame cancels all precision features) - Crosshair accuracy fix ensuring dampened cursor matches hover highlighting - Configuration constants, debug logging, performance considerations - know-your-world/README.md: Game overview and feature documentation - Game modes, maps, difficulty levels, study mode - Visual features, technical architecture, component structure - Development guide, troubleshooting, configuration - Updated documentation graph: - Main README → Arcade Games → Know Your World → Precision Controls - All documentation now reachable from root README 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- README.md | 17 + apps/web/src/arcade-games/README.md | 19 ++ .../know-your-world/PRECISION_CONTROLS.md | 291 ++++++++++++++++++ .../arcade-games/know-your-world/README.md | 219 +++++++++++++ 4 files changed, 546 insertions(+) create mode 100644 apps/web/src/arcade-games/know-your-world/PRECISION_CONTROLS.md create mode 100644 apps/web/src/arcade-games/know-your-world/README.md diff --git a/README.md b/README.md index 3c4079e6..be37bd05 100644 --- a/README.md +++ b/README.md @@ -804,6 +804,23 @@ MIT License - see LICENSE file for details. ## 📚 Additional Documentation +### Arcade Game System + +**Location**: [`apps/web/src/arcade-games/`](./apps/web/src/arcade-games/) +**Overview**: [`apps/web/src/arcade-games/README.md`](./apps/web/src/arcade-games/README.md) + +Modular, plugin-based architecture for building multiplayer arcade games with real-time synchronization. + +**Available Games**: +- **[Know Your World](./apps/web/src/arcade-games/know-your-world/README.md)** - Geography quiz with precision controls for tiny regions ([Precision Controls Docs](./apps/web/src/arcade-games/know-your-world/PRECISION_CONTROLS.md)) +- Number Guesser - Hot/cold guessing game +- Memory Quiz - Pattern matching challenge +- Matching Pairs - Memory card game + +**Key Documentation**: +- **[Game SDK](./apps/web/src/arcade-games/README.md)** - How to create new arcade games +- **[Precision Controls](./apps/web/src/arcade-games/know-your-world/PRECISION_CONTROLS.md)** - Advanced cursor dampening and super zoom system + ### Worksheet Generator **Location**: [`apps/web/src/app/create/worksheets/`](./apps/web/src/app/create/worksheets/) diff --git a/apps/web/src/arcade-games/README.md b/apps/web/src/arcade-games/README.md index 0eaae853..40d9f88c 100644 --- a/apps/web/src/arcade-games/README.md +++ b/apps/web/src/arcade-games/README.md @@ -15,6 +15,25 @@ A modular, plugin-based architecture for building multiplayer arcade games with --- +## Available Games + +### Know Your World + +**Location**: [`know-your-world/`](./know-your-world/) +**Documentation**: [`know-your-world/README.md`](./know-your-world/README.md) + +A geography quiz game where players identify countries, states, and territories on unlabeled maps. + +**Key Features**: +- **[Precision Controls](./know-your-world/PRECISION_CONTROLS.md)** - Automatic cursor dampening and super zoom for tiny regions (Gibraltar 0.08px) +- **3 Game Modes**: Cooperative, Race, Turn-Based +- **Multiple Maps**: World (256 countries), USA States (51 states) +- **Adaptive Difficulty**: Per-map difficulty tiers with smart filtering +- **Study Mode**: Optional memorization period before gameplay +- **Visual Features**: Excluded region visualization, adaptive zoom magnifier, smart label positioning + +--- + ## Overview ### Goals diff --git a/apps/web/src/arcade-games/know-your-world/PRECISION_CONTROLS.md b/apps/web/src/arcade-games/know-your-world/PRECISION_CONTROLS.md new file mode 100644 index 00000000..78917921 --- /dev/null +++ b/apps/web/src/arcade-games/know-your-world/PRECISION_CONTROLS.md @@ -0,0 +1,291 @@ +# Precision Controls for Tiny Map Regions + +## Overview + +The Know Your World map includes sophisticated precision controls that automatically activate when hovering over extremely small regions, making it possible to accurately select sub-pixel regions like Gibraltar (0.08px) and Jersey (0.82px) on screen. + +## Features + +### 1. Automatic Cursor Dampening (Precision Mode) + +**Purpose**: Slow down cursor movement when over tiny regions for more precise control + +**Activation**: Automatically detects when hovering over small regions (< 15px) + +**Behavior**: +- **Sub-pixel regions (< 1px)**: 3% cursor speed (ultra precision) + - Example: Gibraltar (0.08px width) +- **Tiny regions (1-5px)**: 10% cursor speed (high precision) + - Example: Jersey (0.82px width) +- **Small regions (5-15px)**: 25% cursor speed (moderate precision) + - Example: Rhode Island (~11px) + +**Visual feedback**: Cursor changes to crosshair when precision mode is active + +**Implementation**: `MapRenderer.tsx` lines 166-187, 668-712 + +```typescript +// Adaptive dampening based on smallest region size +const getDampeningFactor = (size: number): number => { + if (size < 1) return 0.03 // Ultra precision + if (size < 5) return 0.1 // High precision + return 0.25 // Moderate precision +} + +// Apply dampening by interpolating cursor position +const deltaX = cursorX - lastCursorRef.current.x +const deltaY = cursorY - lastCursorRef.current.y +finalCursorX = lastCursorRef.current.x + deltaX * dampeningFactor +finalCursorY = lastCursorRef.current.y + deltaY * dampeningFactor +``` + +### 2. Auto Super-Zoom on Hover + +**Purpose**: Dramatically increase magnification for sub-pixel regions after a brief hover + +**Activation**: +- Automatically triggers after **500ms** of hovering over sub-pixel regions (< 1px) +- Only activates when magnifier is already showing + +**Behavior**: +- Normal adaptive zoom: 8-24x +- Super zoom: up to **60x magnification** (2.5x multiplier) +- Applies to regions smaller than 1 screen pixel + +**Visual feedback**: +- Magnifier border changes from **blue** to **gold** +- Gold glow shadow around magnifier +- Zoom level indicator shows current magnification + +**Implementation**: `MapRenderer.tsx` lines 853-873, 1282-1292 + +```typescript +const HOVER_DELAY_MS = 500 +const SUPER_ZOOM_MULTIPLIER = 2.5 + +// Start timer when hovering over sub-pixel regions +if (detectedSmallestSize < 1 && shouldShow && !superZoomActive) { + hoverTimerRef.current = setTimeout(() => { + setSuperZoomActive(true) + }, HOVER_DELAY_MS) +} + +// Apply super zoom multiplier to adaptive zoom +if (superZoomActive) { + adaptiveZoom = Math.min(60, adaptiveZoom * SUPER_ZOOM_MULTIPLIER) +} +``` + +### 3. Quick-Escape Mechanism + +**Purpose**: Allow users to instantly cancel precision mode and super zoom with fast mouse movement + +**Activation**: Moving mouse faster than **50 pixels per frame** + +**Behavior**: +- Immediately cancels cursor dampening +- Immediately cancels super zoom +- Clears hover timer if active +- Restores normal cursor speed + +**Visual feedback**: +- Cursor returns to normal pointer +- Magnifier border returns to blue (if still showing) +- Super zoom deactivates + +**Implementation**: `MapRenderer.tsx` lines 641-665 + +```typescript +const QUICK_MOVE_THRESHOLD = 50 // pixels per frame + +// Calculate velocity +const deltaX = cursorX - lastCursorRef.current.x +const deltaY = cursorY - lastCursorRef.current.y +const distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY) +velocity = distance + +// Quick escape on fast movement +if (velocity > QUICK_MOVE_THRESHOLD) { + setPrecisionMode(false) + setSuperZoomActive(false) + clearTimeout(hoverTimerRef.current) +} +``` + +### 4. Crosshair Accuracy + +**Purpose**: Ensure crosshairs in magnifier accurately show which region will be selected + +**Challenge**: When cursor dampening is active, native mouse events fire at real mouse position, but crosshairs show dampened position - creating a mismatch + +**Solution**: Manual region detection using dampened cursor position + +**Implementation**: `MapRenderer.tsx` lines 743-842 + +```typescript +// Convert dampened cursor to client coordinates +const finalClientX = containerRect.left + finalCursorX +const finalClientY = containerRect.top + finalCursorY + +// Check which region is under dampened cursor +const cursorInRegion = + finalClientX >= regionLeft && + finalClientX <= regionRight && + finalClientY >= regionTop && + finalClientY <= regionBottom + +// Find closest region by distance to center +if (cursorInRegion) { + const distanceToCenter = Math.sqrt( + Math.pow(finalClientX - regionCenterX, 2) + + Math.pow(finalClientY - regionCenterY, 2) + ) + + if (distanceToCenter < smallestDistanceToCenter) { + regionUnderCursor = region.id + } +} + +// Set hover state manually (bypass native events) +setHoveredRegion(regionUnderCursor) +``` + +**Native hover events disabled**: When precision mode is active, native `onMouseEnter`/`onMouseLeave` are bypassed: + +```typescript +onMouseEnter={() => !precisionMode && setHoveredRegion(region.id)} +onMouseLeave={() => !precisionMode && setHoveredRegion(null)} +``` + +## User Experience Flow + +### Example: Selecting Gibraltar (0.08px) + +1. **Hover over Iberian Peninsula area** + - Magnifier appears showing 8-12x zoom + - Cursor speed normal + +2. **Move cursor near Gibraltar** + - **Precision mode activates**: Cursor becomes crosshair + - **Cursor dampening**: Movement slows to 3% speed + - User has fine control to position cursor + +3. **Hold cursor over Gibraltar for 500ms** + - **Super zoom activates**: Magnifier zooms to 40-60x + - **Magnifier border**: Changes from blue to gold + - Gibraltar now clearly visible and selectable + +4. **Click on Gibraltar** or **"shake" mouse to escape** + - Quick movement (>50px/frame) instantly cancels precision mode + - Cursor returns to normal speed + - Magnifier returns to blue border (or hides if moved away) + +## Debug Logging + +Extensive console logging helps troubleshoot precision controls: + +```javascript +// Precision mode activation +console.log('[Precision Mode] ✅ DAMPENING ACTIVE:', { + smallestRegionSize: '0.82px', + dampeningFactor: '10%', + actual: { x: 500, y: 300 }, + dampened: { x: 485, y: 298 } +}) + +// Super zoom activation +console.log('[Super Zoom] 🔍 ACTIVATING super zoom!') + +// Quick escape +console.log('[Quick Escape] 💨 Fast movement detected (75px) - canceling') + +// Hover detection +console.log('[Hover Detection] Region under dampened cursor:', { + region: 'je', + regionName: 'Jersey', + dampenedPos: { x: 485, y: 298 }, + distanceToCenter: '2.5px' +}) +``` + +## Configuration Constants + +All tuning parameters are defined at the top of `MapRenderer.tsx`: + +```typescript +// Hover delay before super zoom activates +const HOVER_DELAY_MS = 500 + +// Velocity threshold for quick-escape (pixels per frame) +const QUICK_MOVE_THRESHOLD = 50 + +// Super zoom multiplier (applied to adaptive zoom) +const SUPER_ZOOM_MULTIPLIER = 2.5 + +// Adaptive dampening factors +const getDampeningFactor = (size: number): number => { + if (size < 1) return 0.03 // Ultra precision: 3% speed + if (size < 5) return 0.1 // High precision: 10% speed + return 0.25 // Moderate precision: 25% speed +} + +// Maximum zoom levels +const MAX_ZOOM_NORMAL = 24 // Normal adaptive zoom cap +const MAX_ZOOM_SUPER = 60 // Super zoom cap +``` + +## Technical Details + +### Coordinate Systems + +The implementation handles three coordinate systems: + +1. **Container coordinates**: Relative to map container div (includes padding) +2. **Client coordinates**: Absolute screen position +3. **SVG coordinates**: ViewBox coordinate space for rendering + +Cursor dampening happens in container coordinates, then converts to client coordinates for region detection. + +### Performance Considerations + +- **No performance impact when inactive**: All precision features only run when hovering over map +- **Efficient region detection**: Uses `getBoundingClientRect()` which is hardware-accelerated +- **Minimal state updates**: Only updates when cursor moves or regions change +- **Spring animations**: Uses `@react-spring/web` for smooth magnifier transitions + +### Browser Compatibility + +Works in all modern browsers that support: +- CSS transforms +- getBoundingClientRect() +- React 18 hooks +- @react-spring/web + +Tested on: +- Chrome/Edge (Chromium) +- Firefox +- Safari + +## Future Improvements + +Potential enhancements: + +1. **Configurable sensitivity**: Let users adjust dampening factors in settings +2. **Haptic feedback**: Vibration on mobile when precision mode activates +3. **Audio cues**: Subtle sound when super zoom activates +4. **Visual trail**: Show cursor path in dampened mode +5. **Gamepad support**: Use analog stick for dampened cursor control +6. **Keyboard navigation**: Arrow keys for pixel-perfect positioning + +## Related Files + +- `MapRenderer.tsx` - Main implementation +- `PlayingPhase.tsx` - Passes props to MapRenderer +- `maps.ts` - Map data and region metadata +- `types.ts` - TypeScript interfaces + +## See Also + +- [Map Rendering Architecture](./MAP_RENDERING.md) (if exists) +- [Game Configuration](./GAME_CONFIG.md) (if exists) +- [Difficulty System](./DIFFICULTY.md) (if exists) diff --git a/apps/web/src/arcade-games/know-your-world/README.md b/apps/web/src/arcade-games/know-your-world/README.md new file mode 100644 index 00000000..b3e2ce3e --- /dev/null +++ b/apps/web/src/arcade-games/know-your-world/README.md @@ -0,0 +1,219 @@ +# Know Your World + +A geography quiz game where players identify countries, states, and territories on unlabeled maps. + +## Features + +### Game Modes + +- **Cooperative**: Work together as a team to find all regions +- **Race**: Compete to click regions first +- **Turn-Based**: Take turns finding locations + +### Maps + +- **World Map**: 256 countries and territories + - Filter by continent: Africa, Asia, Europe, North America, South America, Oceania, Antarctica +- **USA States**: 51 states (50 states + DC) + +### Difficulty Levels + +Configurable per map with customizable difficulty tiers: + +- **World Map**: Easy (capitals only), Medium, Hard (all countries), Expert (including tiny territories) +- **USA States**: Easy (large states), Hard (all states including small ones) + +### Study Mode + +Optional study period before gameplay: +- Skip (no study time) +- 30 seconds (quick review) +- 1 minute (study time) +- 2 minutes (deep study) + +During study, all regions are labeled so players can memorize locations. + +## Precision Controls for Tiny Regions + +One of the most innovative features is the **automatic precision control system** that makes it possible to click on extremely small regions like Gibraltar (0.08px) and Jersey (0.82px). + +### Features: + +- **Adaptive cursor dampening**: Automatically slows cursor movement (3-25% speed) when over tiny regions +- **Auto super-zoom**: Zooms up to 60x after hovering 500ms over sub-pixel regions +- **Quick-escape**: Fast mouse movement instantly cancels precision features +- **Crosshair accuracy**: Magnifier crosshairs accurately show which region will be selected + +**📖 See [PRECISION_CONTROLS.md](./PRECISION_CONTROLS.md) for complete documentation** + +## Visual Features + +### Excluded Region Visualization + +Regions filtered out by difficulty settings are pre-labeled on the map in gray, showing which regions are not included in the current game. + +### Enhanced Contrast + +- Unfound regions: 70% opacity (increased from 30% for better visibility) +- Found regions: 100% opacity with player avatar pattern +- Excluded regions: Gray fill with label + +### Adaptive Zoom Magnifier + +Shows a magnified view (8-60x) when hovering over crowded or tiny regions: +- Automatically calculates optimal zoom based on region density and size +- Smooth spring animations for position and opacity +- Crosshairs show exact cursor position +- Dashed box on main map shows magnified area + +### Smart Label Positioning + +Uses D3 force simulation to position region labels without overlaps: +- Regular regions: Labels at center +- Small regions: Labels with arrow pointers +- Washington DC: Special positioning to avoid blocking other states +- Collision detection prevents label overlaps + +## Technical Architecture + +### Component Structure + +``` +know-your-world/ +├── components/ +│ ├── GameComponent.tsx # Main game container +│ ├── SetupPhase.tsx # Game configuration screen +│ ├── StudyPhase.tsx # Optional study mode +│ ├── PlayingPhase.tsx # Active gameplay +│ ├── ResultsPhase.tsx # Game completion screen +│ ├── MapRenderer.tsx # SVG map rendering + precision controls +│ ├── ContinentSelector.tsx # Interactive world map continent filter +│ └── MapRenderer.stories.tsx # Storybook stories +├── Provider.tsx # React Context provider +├── Validator.ts # Server-side game logic +├── index.ts # Game definition export +├── types.ts # TypeScript interfaces +├── maps.ts # Map data (World, USA) +├── continents.ts # Continent definitions +├── mapColors.ts # Color utilities +└── README.md # This file +``` + +### State Management + +Uses React Context (`KnowYourWorldProvider`) for client-side state: +- Game phase (setup, study, playing, results) +- Selected map and continent +- Game mode and difficulty +- Current prompt and regions found +- Guess history and player metadata + +### Server Validation + +All game moves are validated server-side in `Validator.ts`: +- Verifies region IDs are valid +- Checks if region was already found +- Tracks which player found each region +- Handles turn order (turn-based mode) +- Generates random prompts + +### Map Data + +Maps are sourced from `@svg-maps/world` and `@svg-maps/usa` with enhancements: +- Pre-calculated bounding boxes for each region +- Center coordinates for labels +- Continent metadata (world map only) +- Difficulty tier assignments + +## Configuration + +Game configuration is persisted in arcade room settings: + +```typescript +interface KnowYourWorldConfig { + selectedMap: 'world' | 'usa' + gameMode: 'cooperative' | 'race' | 'turn-based' + difficulty: string // Difficulty level ID (varies per map) + studyDuration: 0 | 30 | 60 | 120 // seconds + selectedContinent: 'all' | ContinentId // world map only +} +``` + +## Development + +### Adding New Maps + +1. Install or create SVG map data package +2. Add map data to `maps.ts`: + ```typescript + export const MAP_DATA = { + 'new-map': { + name: 'New Map', + regions: [...], + viewBox: '0 0 1000 1000', + // ... + } + } + ``` +3. Add difficulty configuration +4. Update type definitions + +### Customizing Difficulty + +Edit `maps.ts` to add custom difficulty tiers: + +```typescript +difficultyConfig: { + levels: [ + { id: 'easy', label: 'Easy', emoji: '😊', description: 'Large regions only' }, + { id: 'hard', label: 'Hard', emoji: '🤔', description: 'All regions' }, + ], + tiers: { + easy: { minWidth: 15, minHeight: 15, minArea: 200 }, + hard: { minWidth: 0, minHeight: 0, minArea: 0 }, + } +} +``` + +### Testing + +Use Storybook to test map rendering: + +```bash +npm run storybook +``` + +Navigate to "Arcade Games / Know Your World / Map Renderer" + +## Troubleshooting + +### Tiny regions not clickable + +- Check browser zoom level (100% recommended) +- Ensure precision controls are working (look for crosshair cursor) +- Check console for debug logs + +### Labels overlapping + +- Adjust force simulation parameters in `MapRenderer.tsx`: + - `centeringStrength`: How strongly labels pull toward region centers + - `collisionPadding`: Minimum space between labels + - `simulationIterations`: More iterations = better layout + +### Performance issues + +- Reduce map size (filter by continent) +- Lower difficulty (fewer regions) +- Disable label arrows in force tuning parameters + +## Related Documentation + +- [Precision Controls](./PRECISION_CONTROLS.md) - Cursor dampening and super zoom +- [Game SDK](../../lib/arcade/README.md) - How arcade games are structured +- [Arcade System](../../app/arcade/README.md) - Overall arcade architecture + +## Credits + +- Map data: [@svg-maps/world](https://www.npmjs.com/package/@svg-maps/world), [@svg-maps/usa](https://www.npmjs.com/package/@svg-maps/usa) +- Label positioning: [D3 Force](https://d3js.org/d3-force) +- Animations: [React Spring](https://www.react-spring.dev/)