docs: add UNIFIED_API update recommendations
- 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
This commit is contained in:
@@ -0,0 +1,268 @@
|
||||
# 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
|
||||
Reference in New Issue
Block a user