- Provide detailed before/after examples for all sections - Document new gameOver pattern with flexible function-based conditions - Explain migration path for games updating to this pattern - Highlight benefits: pure data collection, separation of concerns, flexibility - Guide for updating root UNIFIED_API.md to reflect vim-maze v2.3 changes
269 lines
6.6 KiB
Markdown
269 lines
6.6 KiB
Markdown
# UNIFIED_API.md Update Recommendations
|
||
|
||
This document outlines the changes needed in `/vimazing/games/UNIFIED_API.md` to reflect the scoring refactor implemented in vim-maze v2.3.0.
|
||
|
||
## Summary of Changes
|
||
|
||
Vim-maze has implemented a new pattern for score management:
|
||
- **ScoreManager** is now a pure data collector (no score calculation)
|
||
- **Score calculation** moved to consumer (via utility function or custom logic)
|
||
- **Game-over conditions** are fully flexible and consumer-controlled
|
||
|
||
## Required Updates
|
||
|
||
### 1. Example Entry Point (Line ~100)
|
||
|
||
**OLD:**
|
||
```typescript
|
||
// vim-maze
|
||
useGame({ rows: 24, cols: 32, timeLimit: 600 })
|
||
```
|
||
|
||
**NEW:**
|
||
```typescript
|
||
// vim-maze (with custom game-over condition)
|
||
useGame({
|
||
rows: 24,
|
||
cols: 32,
|
||
gameOver: (sm) => sm.timeValue >= 600000 // 10 minutes in milliseconds
|
||
})
|
||
```
|
||
|
||
### 2. Score Manager Type (Line ~189-201)
|
||
|
||
**OLD:**
|
||
```typescript
|
||
type ScoreManager = {
|
||
// Required Timing
|
||
timeValue: number; // Current time in milliseconds
|
||
|
||
// Required Metrics
|
||
totalKeystrokes: number; // Total keys pressed
|
||
|
||
// Required Result
|
||
finalScore: number | null; // 0-1000 (null until game-won)
|
||
|
||
// Optional: Game-specific metrics
|
||
// [additional fields as needed]
|
||
};
|
||
```
|
||
|
||
**NEW:**
|
||
```typescript
|
||
type ScoreManager = {
|
||
// Required Timing
|
||
timeValue: number; // Current time in milliseconds
|
||
|
||
// Required Metrics
|
||
totalKeystrokes: number; // Total keys pressed
|
||
|
||
// Optional: Game-specific metrics
|
||
// [additional fields as needed]
|
||
// NOTE: finalScore removed - calculation moved to consumer
|
||
};
|
||
```
|
||
|
||
**Add note:**
|
||
```
|
||
**Important:** ScoreManager is now a pure data collector.
|
||
Score calculation has been moved to the consumer.
|
||
Games should provide a utility function like `calculateScore()`
|
||
or instructions for score calculation in their documentation.
|
||
```
|
||
|
||
### 3. GameOptions Examples (Line ~222-230)
|
||
|
||
**OLD:**
|
||
```typescript
|
||
// vim-maze
|
||
type GameOptions = {
|
||
rows: number;
|
||
cols: number;
|
||
timeLimit?: number;
|
||
};
|
||
```
|
||
|
||
**NEW:**
|
||
```typescript
|
||
// vim-maze
|
||
type GameOptions = {
|
||
rows: number;
|
||
cols: number;
|
||
gameOver?: boolean | ((scoreManager: ScoreManager) => boolean);
|
||
};
|
||
```
|
||
|
||
### 4. Scoring Implementation Section (Line ~257-265)
|
||
|
||
**OLD:**
|
||
```typescript
|
||
**vim-maze approach:**
|
||
```typescript
|
||
// Size-based: time + keystrokes × maze size
|
||
const baseScore = 1000 - timePenalty - keystrokePenalty;
|
||
const sizeMultiplier = max(1.0, (rows × cols) / 500);
|
||
const finalScore = min(1000, baseScore × sizeMultiplier);
|
||
```
|
||
```
|
||
|
||
**NEW:**
|
||
```typescript
|
||
**vim-maze approach (v2.3+):**
|
||
```typescript
|
||
// Pure data collection in ScoreManager
|
||
const scoreManager = useScore({ /* dependencies */ });
|
||
// scoreManager returns: { timeValue, keystrokes, optimalSteps, efficiency, ... }
|
||
|
||
// Score calculation moved to consumer
|
||
import { calculateMazeScore } from '@vimazing/vim-maze';
|
||
const finalScore = calculateMazeScore(scoreManager, mazeSize);
|
||
// Base: 1000 - (time/10) - (keystrokes/2)
|
||
// Then: multiply by size factor = max(1.0, (rows × cols) / 500)
|
||
// Final: clamp to 0-1000
|
||
```
|
||
```
|
||
|
||
### 5. Scoring Requirements Section (Line ~203-208)
|
||
|
||
**OLD:**
|
||
```
|
||
**Scoring Requirements:**
|
||
- Score range: 0-1000 (capped)
|
||
- Score calculated on game-won
|
||
- Based on time and keystrokes (minimum)
|
||
- Can include game-specific penalties/bonuses
|
||
```
|
||
|
||
**NEW:**
|
||
```
|
||
**Scoring Requirements:**
|
||
- Score range: 0-1000 (capped)
|
||
- Based on time and keystrokes (minimum)
|
||
- Can include game-specific penalties/bonuses
|
||
- **NEW:** Score calculation moved to consumer via utility function
|
||
- Games should provide `calculateScore()` or similar utility
|
||
- Games should document their scoring formula clearly
|
||
```
|
||
|
||
### 6. Game-Over Conditions Pattern (New Section)
|
||
|
||
Add a new subsection after "Scoring Requirements":
|
||
|
||
```markdown
|
||
### Game-Over Conditions
|
||
|
||
Starting with vim-maze v2.3+, games have full control over game-over conditions:
|
||
|
||
```typescript
|
||
type GameOptions = {
|
||
// ...game-specific options...
|
||
gameOver?: boolean | ((scoreManager: ScoreManager) => boolean);
|
||
};
|
||
```
|
||
|
||
**Examples:**
|
||
```typescript
|
||
// Static game-over
|
||
useGame({ rows: 24, cols: 32, gameOver: true })
|
||
|
||
// Time-based
|
||
useGame({
|
||
rows: 24,
|
||
cols: 32,
|
||
gameOver: (sm) => sm.timeValue >= 600000 // 10 minutes
|
||
})
|
||
|
||
// Efficiency-based
|
||
useGame({
|
||
rows: 24,
|
||
cols: 32,
|
||
gameOver: (sm) => sm.efficiency > 150 // >150% keystrokes
|
||
})
|
||
|
||
// Combined conditions
|
||
useGame({
|
||
rows: 24,
|
||
cols: 32,
|
||
gameOver: (sm) => sm.efficiency > 150 || sm.timeValue >= 600000
|
||
})
|
||
```
|
||
|
||
**Benefits:**
|
||
- No hardcoded game-over logic in libraries
|
||
- Consumers define win/loss conditions
|
||
- Flexible for tournaments, variations, custom rules
|
||
```
|
||
|
||
### 7. Hook Specifications - useScore Section (Line ~424+)
|
||
|
||
Find and update the useScore section:
|
||
|
||
**OLD:**
|
||
```typescript
|
||
export function useScore(/* dependencies */) {
|
||
// ...
|
||
const [timeValue, setTimeValue] = useState(0);
|
||
const [finalScore, setFinalScore] = useState<number | null>(null);
|
||
|
||
// Calculate score on game-won
|
||
useEffect(() => {
|
||
if (gameStatus === 'game-won') {
|
||
const score = calculateScore(timeValue, totalKeystrokes);
|
||
setFinalScore(Math.min(1000, Math.max(0, score)));
|
||
}
|
||
}, [gameStatus]);
|
||
|
||
return {
|
||
timeValue,
|
||
totalKeystrokes,
|
||
finalScore,
|
||
// Additional metrics
|
||
};
|
||
}
|
||
```
|
||
|
||
**NEW:**
|
||
```typescript
|
||
export function useScore(/* dependencies */) {
|
||
// Returns raw metrics only - no calculation
|
||
// Score calculation moved to consumer
|
||
|
||
return {
|
||
timeValue,
|
||
totalKeystrokes,
|
||
// Additional game-specific metrics
|
||
// finalScore removed - consumer responsibility
|
||
};
|
||
}
|
||
```
|
||
|
||
Add note:
|
||
```
|
||
**Change in v2.3+:** ScoreManager is now a pure data collector.
|
||
All score calculation has been moved to the consumer.
|
||
This provides flexibility and allows games to:
|
||
- Provide utility functions for standard scoring
|
||
- Allow consumers to implement custom scoring
|
||
- Keep the hook focused on metrics collection
|
||
```
|
||
|
||
## Migration Guide for Games
|
||
|
||
For games updating to this pattern:
|
||
|
||
1. Remove `finalScore` from `ScoreManager` type
|
||
2. Remove score calculation logic from `useScore` hook
|
||
3. Export a `calculateScore()` utility function
|
||
4. Update `gameOver` option to accept function receiving `scoreManager`
|
||
5. Remove `timeLimit` logic from `useScore` (delegate to consumer)
|
||
6. Document the new scoring approach in gameInfo
|
||
|
||
## Benefits of This Pattern
|
||
|
||
✅ **Pure Data Collection** - ScoreManager has no side effects
|
||
✅ **Separation of Concerns** - Scoring logic is independent
|
||
✅ **Flexibility** - Consumers control both scoring and game-over
|
||
✅ **Testability** - Scoring functions can be tested in isolation
|
||
✅ **Reusability** - Metrics available for any custom logic
|
||
✅ **Clarity** - Clear boundaries between collection and calculation
|