Commit Graph
46 Commits
Author SHA1 Message Date
pastilhas 3e79d33af2 docs: add UNIFIED_API v2.0 to vim-sudoku 2025-10-22 22:31:32 +01:00
pastilhas ff497bc393 docs: add gif demo image reference back to README 2025-10-22 22:14:20 +01:00
pastilhas 4236f53ef8 feat: add configurable removedCells option and comprehensive documentation
- Add removedCells to GameOptions for custom puzzle sizes
- removedCells overrides difficulty default when provided
- Maintain backward compatibility with all defaults
- Complete IMPLEMENTATION.md with Iteration 16 and summary
- Completely rewrite README.md with comprehensive documentation:
  - Complete API reference (GameOptions, GameManager, CursorManager, ScoreManager)
  - Game states and state machine flow
  - Full VIM controls reference (movement, editing, hints, game control)
  - Cell state rules and visual feedback
  - Detailed scoring formula with examples
  - Game-over conditions (time and hint limits)
  - Hint system priority and penalties
  - Configuration examples for different play styles
  - Platform hook integration guide
- Mark implementation as COMPLETE and PRODUCTION READY
2025-10-22 21:58:59 +01:00
pastilhas f68098657b feat: implement game-over logic with time and hint limits
- Add configurable timeLimit to GameOptions (default 600 seconds / 10 minutes)
- Time limit applies to all difficulty levels (harder puzzles = more time pressure)
- Implement game-over triggers:
  1. Time limit exceeded: timeValue >= timeLimit → 'Time's up!'
  2. Hint penalty limit: hintsUsed * penalty >= 500 → 'Too many hints!'
- Hint limits by difficulty:
  - Easy: 20 hints max (20 × 25 = 500 points)
  - Medium: 10 hints max (10 × 50 = 500 points)
  - Hard: 5 hints max (5 × 100 = 500 points)
- Add gameOverReason to ScoreManager to display specific failure reason
- Timer stops on game-over
- Update example app to show game-over reason
- Both conditions checked during gameplay before win condition
2025-10-22 21:48:33 +01:00
pastilhas 97c31ab6cc feat: implement hint system (Shift+H) with scoring penalties
- Add getHint() method to BoardManager with two-tier priority:
  1. Find incorrect user-entered cells → flash red
  2. Find empty cells → show correct digit green temporarily
- Add Shift+H key handler in useCursor (normal mode only)
- Visual feedback:
  - Red flash (hint-flash-error): incorrect cells, 1s duration, red bg + border
  - Green flash (hint-flash-correct): correct digit shown, 1s duration, green bg + border
  - Distinct from invalid-move (no background, 500ms)
- Score penalties based on difficulty:
  - Easy: 25 points per hint
  - Medium: 50 points per hint
  - Hard: 100 points per hint
- Track hints used in scoreboard display
- Correct digit appears temporarily (1s) then disappears (player still does the work)
- Update example app to show hints counter and Shift+H instruction
2025-10-22 21:24:06 +01:00
pastilhas cfdf8100fb refactor: simplify scoring to remove efficiency metrics
- Remove efficiency calculation (not applicable to sudoku gameplay)
- Simplify to time + keystrokes only scoring
- New formula: score = min(1000, (1000 - time_penalty - keystroke_penalty) * difficulty_multiplier)
  - time_penalty = seconds / 10
  - keystroke_penalty = totalKeys / 2
  - difficulty multipliers: easy 1.0x, medium 1.5x, hard 2.0x
- Update difficulty levels to realistic values:
  - Easy: 25 cells removed (was 5)
  - Medium: 40 cells removed (was 50)
  - Hard: 50 cells removed (was 60)
- Update scoreboard to show only Time, Keystrokes, and Final Score (X / 1000)
- Remove editKeystrokes, optimalKeystrokes, efficiency from ScoreManager type
2025-10-22 21:03:56 +01:00
pastilhas 8db4455dc5 feat: implement scoring system and win condition (Phase 50)
- Add useTimer hook for elapsed time tracking
- Add useScore hook with comprehensive metrics:
  - Total keystrokes (all keys)
  - Edit keystrokes (only editing keys)
  - Optimal keystrokes (empty cells × 3)
  - Efficiency percentage (optimal/edit × 100)
  - Final score (70% efficiency + 30% time-based)
- Win condition detects board completion only in normal mode
- Track all keystrokes with timestamps in useGame
- Update example app with full scoreboard display
- Expose boardRef in BoardManager for state checking
- Adjust easy mode to 5 removed cells for testing
2025-10-22 20:42:50 +01:00
pastilhas 710ec4b83b feat: implement erase functionality (Phase 40.3.5)
- Add x, d, Delete in normal mode to erase user-entered cells
- Add Backspace in edit mode to erase and stay in edit mode
- Add Backspace in normal mode as invalid move (red flash)
- All erase commands respect given cell protection
- Empty cells flash red on erase attempt in normal mode
2025-10-22 20:15:00 +01:00
pastilhas bd3616b472 feat: implement r and c commands for single-digit edit
- Add singleEdit state to track single vs multi-edit mode
- r and c only work on user-entered cells (flash red otherwise)
- i only works on empty cells (flash red on given or user-entered)
- r/c auto-exit to normal mode after one digit entry
- i stays in edit mode until Escape (multi-edit)
2025-10-22 20:04:14 +01:00
pastilhas 340008ae11 fix: stale closure causing wrong cell to flash on edit mode entry
- Add position to useEffect dependency array in useCursor
- Fixes issue where old position was captured in handleKeyDown closure
- Edit mode entry now correctly validates current cell position
2025-10-22 19:54:28 +01:00
pastilhas bc8eeaf400 feat: implement digit entry in edit mode (Phase 40.3.3)
- Add setCell method to useBoard to update board state
- Handle digit keys 1-9 in edit mode in useCursor
- Move boardRendered counter to useBoard for proper cursor re-rendering
- Fix golden glow disappearing after digit entry
- Digits display with .user-entered class via renderer
2025-10-22 19:49:12 +01:00
pastilhas 6a4b72075c docs: add NEXT_PROMPT.md for continuing in new session
Comprehensive prompt for Phase 40.3.3 (digit entry):
- Context setup instructions
- Complete summary of Phases 40.1, 40.2, 40.3
- Current architecture overview
- Detailed implementation strategy for digit entry
- Testing checklist
- Agent rules and code conventions
- Quick reference for current state

This document allows seamless continuation in a new chat session
with full context and clear next steps.
2025-10-22 19:34:35 +01:00
pastilhas 3ce3f92d81 docs: document Phase 40.3 - Edit Mode Implementation
Documented complete edit mode state machine:
- Phase 40.3.1: Mode state (normal/edit) with i and Escape
- Phase 40.3.2: Edit rules (given cells block, red flash feedback)
- Visual indicators using existing CSS classes
- Mode behavior and transitions

All edit mode functionality tested and working:
✅ State machine transitions
✅ Given cell validation with red flash
✅ Visual feedback (blue/yellow/red)
✅ Vim motions blocked in edit mode

Phase 40.3 complete. Ready for digit entry (40.3.3).
2025-10-22 19:30:56 +01:00
pastilhas c5e98dafb8 feat: add edit mode rules and use existing CSS classes
Rules Implemented:
1. Can only enter edit mode on empty cells or user-entered cells
2. Pressing 'i' on a given cell (from algo) triggers red flash
3. Edit mode blocked on cells with 'given' class

Visual Changes:
- Use existing 'active' class for normal mode cursor (blue glow)
- Use existing 'editing' class for edit mode (yellow/amber glow)
- Removed custom cursor/cursor-edit classes
- Matches original CSS design from src_old

Cell Detection:
- Check if cell has 'given' class before entering edit mode
- Given cells: From puzzle generation (immutable)
- Empty or user-entered cells: Can be edited

Flow:
1. Navigate to given cell → Press 'i' → Red flash (blocked)
2. Navigate to empty cell → Press 'i' → Yellow editing glow
3. Navigate to user-entered cell → Press 'i' → Yellow editing glow
4. Press Escape → Return to blue active glow

Phase 40.3.2 complete.
2025-10-22 19:27:47 +01:00
pastilhas 0ce1d7fe65 feat: implement edit mode state machine (i and Escape)
Added mode state machine with visual feedback:

Mode State:
- CursorMode type: 'normal' | 'edit'
- Tracked in useCursor with useState
- Starts in 'normal' mode

Key Bindings:
- Press 'i' in normal mode → Enter edit mode
- Press 'Escape' in edit mode → Return to normal mode
- All vim motions (hjkl, anchors, etc.) only work in normal mode
- Edit mode blocks all movement keys

Visual Feedback:
- Normal mode: Blue outline (rgb(59, 130, 246))
- Edit mode: Green pulsing outline (rgb(34, 197, 94))
- Smooth pulse animation (1.5s ease-in-out infinite)
- cursor-edit CSS class applied in edit mode

Flow:
1. Game starts in normal mode (blue cursor)
2. Press 'i' → Green pulsing cursor (edit mode)
3. Press 'Escape' → Blue cursor (normal mode)

No digit entry yet - just state machine and visual indicators.
Phase 40.3.1 complete.
2025-10-22 19:22:15 +01:00
pastilhas 8ac008aeac docs: document Phase 40.2 - Complete Vim Motion System
Documented complete vim motion implementation:
- Anchor moves (0, ^, $, gg, G)
- Count prefixes (5j, 10l, etc.)
- Repeat motion (.)
- 0 caveat solution
- gg two-key sequence fix

All motion features tested and working flawlessly.
Phase 40.2 complete. Ready for Phase 40.3 (cell editing).
2025-10-22 19:18:58 +01:00
pastilhas c187ff455e fix: gg now requires two g presses (not just one)
Added pendingGRef to track when waiting for second 'g':
- First g: Set pendingGRef = true, return null (no action)
- Second g: Check pendingGRef, if true → 'top' anchor
- Any other key after first g: Clear pendingGRef, return null

Now:
- g alone → Nothing (waiting for second g)
- gg → Jump to top of column
- G → Jump to bottom of column

Matches vim behavior for gg command.
2025-10-22 19:17:14 +01:00
pastilhas ef25d3873b feat: implement vim anchor moves and motion system
Added complete vim motion system with anchor moves:

New Files:
- src/useCursor/useVimMotions.ts: Vim motion processor
  - Handles number prefixes (counts)
  - Special 0 handling: anchor when alone, count when prefixed
  - Tracks last motion for repeat (.)

Anchor Moves:
- 0 or ^ → Move to start of row (col 0)
- $ → Move to end of row (col 8)
- gg → Move to top of column (row 0)
- G → Move to bottom of column (row 8)

Count Support:
- 5j → Move down 5 cells
- 10l → Move right 10 cells (bounded to 8)
- Works with all directional moves (h/j/k/l)

Repeat Motion:
- . → Repeat last motion with same count

Key Logic (0 handling):
- If 0 pressed with empty count buffer → anchor to start
- If 0 pressed with existing count → add to count (10j, 20k)
- Prevents conflict between anchor and counted moves

Updated:
- useCursor.ts: Integrated useVimMotions processor
- types.ts: Added Motion and VimMotionSystem types
- CursorManager: Added anchor and repeat methods

All moves respect 9x9 bounds with red flash on invalid.
2025-10-22 19:14:21 +01:00
pastilhas 38b9b4b694 docs: add Agent Rules section to IMPLEMENTATION.md
Added key principles and constraints:
- React 19 optimization (no useCallback/useMemo)
- Code conventions (no comments, type-first)
- Architecture patterns (hook composition, refs)
- Documentation standards

These rules guide all implementation decisions and ensure consistency.
2025-10-22 19:08:54 +01:00
pastilhas 1409dcf012 docs: document Issue 3 fix and Phase 40.1 completion
Documented:
- Issue 3: Red flash on invalid movement (500ms animation)
- Cleanup: Removed all debug console.logs
- Summary: All 3 issues resolved, production-ready

Phase 40.1 complete:
✅ Basic cursor with hjkl movement
✅ Visual cursor highlight (blue outline)
✅ Shows immediately on game start
✅ No border conflicts with grid edges
✅ Red flash feedback on invalid moves
✅ Clean, production-ready code

Ready for next phase or sidequest.
2025-10-22 19:04:10 +01:00
pastilhas 345b273669 feat: add red flash on invalid movement (Issue 3)
When user attempts to move out of bounds, show visual feedback:

CSS:
- Added .invalid-move class with red outline and background
- Animation: invalid-flash (500ms ease-out)
- Starts with bright red glow, fades out
- Red outline (3px solid) matches cursor style

Logic:
- flashInvalidMove() function in useCursor
- Triggers when move would go out of bounds (< 0 or > 8)
- Adds invalid-move class to current cell
- Removes class after 500ms
- Passes containerRef to useCursor for DOM access

Cleanup:
- Removed all console.log debugging statements
- Clean code ready for production

UX Flow:
- Press h at left edge → Red flash at current position
- Press j at bottom edge → Red flash at current position
- Press k at top edge → Red flash at current position
- Press l at right edge → Red flash at current position

Issue 3 complete!
2025-10-22 19:03:05 +01:00
pastilhas 154a2e5cc3 docs: document Issue 2 fix (cursor border conflicts) 2025-10-22 19:01:53 +01:00
pastilhas 49a6ab5eef fix: cursor border conflicts with grid edges (Issue 2)
Problem: Cursor's border was conflicting with 3x3 box separators
- Cells already have borders for grid structure
- Cursor border overlapped/clipped at edges

Solution: Use outline instead of border
- outline: 3px solid (sits above borders)
- outline-offset: -3px (draws inside cell)
- z-index: 10 + position: relative (layers above grid)

Benefits:
- Outline doesn't affect layout
- Doesn't conflict with existing borders
- Clean appearance at all grid edges
- Consistent thickness throughout
2025-10-22 19:00:32 +01:00
pastilhas 695c356e91 docs: document Phase 40.1 implementation and Issue 1 fix 2025-10-22 18:59:47 +01:00
pastilhas b8ff946114 fix: cursor now shows immediately on game start
Added boardRendered counter to track when board DOM is rebuilt:
- Increments after each renderBoard() call
- useCursorRender depends on boardRendered
- Forces cursor re-application after board rebuilds DOM

Flow:
1. Game starts
2. renderBoard() rebuilds DOM
3. boardRendered increments
4. useCursorRender triggers
5. Cursor class applied to (0,0)

This ensures cursor is visible immediately when pressing Space.
2025-10-22 18:57:57 +01:00
pastilhas 6d12a01f21 fix: cursor not showing on game start
Issue: When game starts, renderBoard() rebuilds DOM which removes cursor class
Solution: Add gameStatus as dependency to useCursorRender
- Now re-applies cursor after board renders
- Only applies cursor when gameStatus === 'started'

This ensures cursor is visible immediately when game starts at position (0,0).
2025-10-22 18:56:44 +01:00
pastilhas b635c21042 fix: cursor not updating - use useState instead of useRef
The cursor position wasn't updating visually because:
- useRef doesn't trigger re-renders
- useCursorRender wasn't being called on position changes

Solution:
- Changed position from useRef to useState
- Now position changes trigger re-renders
- useCursorRender receives updated position
- Cursor highlight updates properly

Added temporary console.logs for debugging:
- Cursor position changes
- Key press events
- Movement attempts and bounds checking
- Cell selection and class application

This will help verify cursor is working in browser.
Console logs to be removed once verified working.
2025-10-22 18:52:01 +01:00
pastilhas be41ccee14 feat: implement Phase 40.1 - basic cursor with hjkl movement
Add cursor navigation with hjkl keys (vim-style):

New Module Structure:
- src/useCursor/types.ts: Coord and CursorManager types
- src/useCursor/useCursor.ts: Cursor movement logic
- src/useCursor/useCursorRender.ts: Visual cursor highlight
- src/useCursor/index.ts: Public exports

Cursor Implementation:
- Position tracking with useRef (row/col from 0-8)
- Basic movement: moveLeft, moveRight, moveUp, moveDown
- Bounds checking (prevents moving outside 9x9 grid)
- Keyboard listener for h/j/k/l keys
- Only active when gameStatus === 'started'

Cursor Rendering:
- useCursorRender hook tracks cursor position
- Adds 'cursor' CSS class to current cell
- Removes from previous cell on movement
- Blue highlight with border (rgba(59, 130, 246, 0.3))

Integration:
- src/useGame.ts: Integrate useCursor and useCursorRender
- src/types.ts: Export CursorManager and Coord types
- src/useBoard/types.ts: NEW - BoardManager type
- src/game.css: Add cursor highlight styles

Game Flow:
- Press Space → Game starts → Cursor appears at (0,0)
- Press h/j/k/l → Cursor moves (bounded to grid)
- Visual feedback with blue highlight
- Press Q → Return to waiting state

Phase 40.1 complete. Next: Add number prefixes (5j) and other vim motions.
2025-10-22 18:49:24 +01:00
pastilhas 4e14d54122 feat: implement Phase 30 - useGameStatus hook
Extract game status logic into dedicated hook following vim-maze pattern:

New Module Structure:
- src/useGameStatus/types.ts: GameStatus and GameStatusManager types
- src/useGameStatus/useGameStatus.ts: Hook implementation
- src/useGameStatus/index.ts: Public exports

Hook Implementation:
- State machine with 5 states (waiting/started/paused/game-over/game-won)
- Methods: startGame, togglePause, quitGame, setGameStatus
- Clean state transitions with useState
- Simpler than vim-maze (no intermediate 'has-key' state)

Updated Core Files:
- src/useGame.ts: Use useGameStatus() instead of inline useState
  - Compose gameStatusManager into GameManager
  - Cleaner separation of concerns

- src/types.ts: Import and re-export GameStatus types
  - GameManager extends GameStatusManager (intersection type)
  - Single source of truth for status types

- src/index.ts: Export useGameStatus hook publicly

Benefits:
- Reusable state machine
- Easier to test in isolation
- Follows unified API architecture
- Matches vim-maze patterns

State Transitions:
waiting → startGame() → started ↔ togglePause() ↔ paused
started → (win/lose) → game-won/game-over
* → quitGame() → waiting

Phase 30 complete. Next: Phase 40 (useCursor composition).
2025-10-22 18:45:56 +01:00
pastilhas 6f4a1accc4 feat: add difficulty selector to example app
Add interactive difficulty selection when game is idle:

Example App Changes:
- example/src/App.tsx: Add difficulty state and radio group
  - Import useState and Difficulty type
  - Radio buttons for Easy/Medium/Hard
  - Only visible when gameStatus === 'waiting'
  - Updates difficulty state on selection
  - Passes selected difficulty to useGame

Core Changes:
- src/useGame.ts: Respect difficulty changes
  - Add difficulty to useEffect dependencies
  - Regenerates board when difficulty changes

UX Flow:
1. User sees difficulty selector on idle screen
2. Selects difficulty (easy/medium/hard)
3. Presses Space to start
4. Puzzle generated with selected difficulty
5. On quit, returns to selector with last selection

Cell Counts:
- Easy: 40 empty cells (~41 givens)
- Medium: 50 empty cells (~31 givens)
- Hard: 60 empty cells (~21 givens)
2025-10-22 18:42:35 +01:00
pastilhas 07f07376be feat: implement Phase 21 - replace with pattern-based generation
Replace Robatron's complex algorithm with simpler, always-valid approach:

Core Changes:
- src/useBoard/SudokuGenerator.ts: Complete rewrite
  - Pattern-based generation (base pattern + structural shuffles)
  - Generate complete solution first, then remove cells
  - Validates all boards before returning (defensive)
  - Class-based API: generate(), getPuzzle(), getSolution()
  - Configurable difficulty: CELLS_TO_REMOVE = {easy: 40, medium: 50, hard: 60}
  - ~130 lines vs 372 lines (much simpler)

- src/useBoard/SudokuRenderer.ts: NEW - Clean separation
  - render(board, initialBoard): Displays current state
  - renderEmpty(): Shows empty 9x9 grid
  - Distinguishes 'given' vs 'user-entered' cells with CSS classes

- src/useBoard/useBoard.ts: Updated for class architecture
  - Refs: generatorRef, rendererRef, boardRef, solutionRef
  - Uses class instances for generation and rendering
  - Clean separation of concerns

- src/useBoard/SudokuGenerator.test.ts: Unit tests
  - Tests valid board generation (100x)
  - Tests correct cell removal counts per difficulty
  - Tests puzzle/solution consistency

Algorithm Benefits:
1. Always valid - starts with mathematically valid board
2. Always solvable - we keep the solution reference
3. Fast - no backtracking, just pattern + shuffle
4. Simple - easy to understand and maintain
5. Predictable - difficulty = cell count, not complexity

Pattern-Based Approach:
- Base pattern ensures valid structure
- Shuffle rows/cols within bands/stacks (preserves 3x3 boxes)
- Shuffle band/stack order
- Shuffle digit mapping (1-9)
- Result: Valid, random, unique board every time

Phase 21 complete. Next: Phase 30 (useGameStatus hook).
2025-10-22 18:39:55 +01:00
pastilhas a6c4c5302f feat: implement Phase 20 - puzzle generation and rendering
Port sudoku generation algorithm and integrate with game flow:

Core Changes:
- src/useBoard/SudokuGenerator.ts: Ported from src_old/lib/Sudoku.ts
  - Full puzzle generation algorithm (robatron/sudoku.js)
  - Supports all difficulty levels (easy through inhuman)
  - Returns 81-character string representation

- src/useBoard/useBoard.ts: Enhanced with puzzle state
  - Added boardRef and initialBoardRef for state management
  - Added generateBoard(difficulty) method
  - Updated renderBoard() to display givens from board string
  - Cells show digits for givens, empty for '.' characters

- src/useGame.ts: Integrated game flow
  - Reads difficulty from GameOptions (defaults to 'easy')
  - Generates puzzle on startGame (gameStatus → 'started')
  - Re-renders board on status changes
  - quitGame returns to empty grid

Game Flow:
- Waiting: Empty 9x9 grid
- Press Space → startGame() → puzzle generated (~62 givens on easy)
- Press Q → quitGame() → empty grid

Testing platformHook integration:
- startGame trigger working via useKeyBindings
- quitGame trigger working via useKeyBindings
- Board state properly cleared/populated on status changes

Phase 20 complete. Next: Phase 30 (useGameStatus hook).
2025-10-22 18:24:08 +01:00
pastilhas b7d740386e feat: implement Phase 10 - minimal skeleton with empty grid
Create clean src/ structure with minimal viable implementation:

Core Files:
- src/types.ts: Basic type definitions (Difficulty, GameStatus, GameManager)
- src/index.ts: Main package exports
- src/game.css: Copied from src_old for styling
- src/useGame.ts: Orchestrator with mocked GameManager API
- src/useBoard/useBoard.ts: Renders empty 9x9 sudoku grid

Implementation Details:
- useBoard renders 81 cells with sudoku-grid/sudoku-cell classes
- Each cell has data-r and data-c attributes for identification
- useGame provides full GameManager interface to satisfy example app
- Real implementations: gameStatus, keyLog tracking, game controls
- Mocked implementations: cursor ({}), scoreManager (zeros/null)
- Supports optional platformHook parameter

Build Status:
- TypeScript compiles cleanly with no errors
- Example app builds successfully
- Ready for browser testing with empty grid display

Phase 10 complete. Next: Phase 20 (full useBoard with puzzle generation).
2025-10-22 18:18:06 +01:00
pastilhas 9292b83dd0 feat: prepare vim-sudoku for unified API implementation
- Archive previous implementation attempt (src -> src_failed)
- Create comprehensive IMPLEMENTATION_PLAN.md with phased approach
- Create IMPLEMENTATION.md to track implementation progress
- Update example app to match vim-maze patterns:
  - Simplify UI to desktop-only (no mobile support)
  - Add score metrics display (time, keystrokes, final score)
  - Update instructions for sudoku-specific keys (hjkl, i for edit)
  - Remove unnecessary mobile hooks

Phase 5 complete. Ready to begin Phase 10 (Setup & Core Types).
2025-10-22 18:08:33 +01:00
pastilhas d0a51d7079 starting new structure 2025-10-19 17:59:12 +01:00
pastilhas 4c6bd0d4d4 ready for publish 2025-10-15 23:10:12 +01:00
pastilhas 7e305e027f 0.10.0 2025-10-15 23:06:29 +01:00
pastilhas f4b781b2de ready for publish 2025-10-15 23:06:24 +01:00
pastilhas 2e51b11a59 ready for publish 2025-10-15 23:06:01 +01:00
pastilhas 73ce666f53 GameOverlays 2025-10-15 21:21:45 +01:00
pastilhas 800d9d083f working on useScore 2025-10-15 19:02:22 +01:00
pastilhas 2dd317e0dc working on useScore 2025-10-15 18:28:55 +01:00
pastilhas 5f3e3706fa working on useScore 2025-10-15 18:03:18 +01:00
pastilhas 5b9b44aef6 working on useGame 2025-10-15 17:56:41 +01:00
pastilhas da8ca2c289 working on useGame 2025-10-15 17:47:41 +01:00
pastilhas 8acc16fc1c working on useGame 2025-10-15 17:29:53 +01:00