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
This commit is contained in:
+162
-1
@@ -1544,6 +1544,167 @@ Hard mode (100 points/hint):
|
||||
|
||||
---
|
||||
|
||||
## Iteration 15: [Pending]
|
||||
## Iteration 15: Game Over Logic
|
||||
|
||||
**Date**: 2025-01-22
|
||||
|
||||
### Goals
|
||||
- Implement time limit for gameplay
|
||||
- Implement hint limit based on penalty points
|
||||
- Provide clear feedback on why game ended
|
||||
- Make time limit configurable via game options
|
||||
|
||||
### Tasks Completed
|
||||
|
||||
#### 1. Added Time Limit Configuration
|
||||
**File: `src/types.ts`**
|
||||
|
||||
**GameOptions Extended:**
|
||||
```typescript
|
||||
export type GameOptions = {
|
||||
difficulty?: Difficulty;
|
||||
timeLimit?: number; // In seconds, default 600 (10 minutes)
|
||||
};
|
||||
```
|
||||
|
||||
**Default:** 10 minutes (600 seconds) for all difficulty levels
|
||||
**Rationale:** Harder puzzles naturally take longer, creating appropriate challenge
|
||||
|
||||
#### 2. Implemented Game Over Triggers
|
||||
**File: `src/useScore/useScore.ts`**
|
||||
|
||||
**Two conditions trigger game-over:**
|
||||
|
||||
**Condition 1: Time Limit Exceeded**
|
||||
```typescript
|
||||
const timeLimitMs = timeLimit * 1000;
|
||||
if (timeValue >= timeLimitMs) {
|
||||
setGameOverReason("Time's up!");
|
||||
setGameStatus('game-over');
|
||||
return;
|
||||
}
|
||||
```
|
||||
|
||||
**Condition 2: Hint Penalty Limit**
|
||||
```typescript
|
||||
const hintPenalty = hintsUsed * HINT_PENALTY[difficulty];
|
||||
if (hintPenalty >= 500) {
|
||||
setGameOverReason('Too many hints!');
|
||||
setGameStatus('game-over');
|
||||
return;
|
||||
}
|
||||
```
|
||||
|
||||
**Hint Limits by Difficulty:**
|
||||
- Easy (25 pts/hint): 20 hints max → 500 points
|
||||
- Medium (50 pts/hint): 10 hints max → 500 points
|
||||
- Hard (100 pts/hint): 5 hints max → 500 points
|
||||
|
||||
#### 3. Added Game Over Reason Tracking
|
||||
**ScoreManager Extended:**
|
||||
```typescript
|
||||
export type ScoreManager = {
|
||||
// ... existing fields
|
||||
gameOverReason: string | null; // "Time's up!" or "Too many hints!"
|
||||
};
|
||||
```
|
||||
|
||||
**Reason Management:**
|
||||
- Set when game-over triggered
|
||||
- Reset on new game start
|
||||
- Displayed to player in UI
|
||||
|
||||
#### 4. Updated Game Lifecycle
|
||||
**Status Flow:**
|
||||
```
|
||||
waiting → started → [time/hints exceeded] → game-over
|
||||
→ [puzzle complete] → game-won
|
||||
|
||||
game-over → [press space] → waiting → started (new game)
|
||||
```
|
||||
|
||||
**Timer Behavior:**
|
||||
- Stops on game-over
|
||||
- Stops on game-won
|
||||
- Resets on waiting
|
||||
|
||||
#### 5. Updated Example App Display
|
||||
**Game Over Message:**
|
||||
```tsx
|
||||
{gameStatus === "game-over" && (
|
||||
<p>{gameOverReason || "Game Over!"} Press <kbd>space</kbd> to restart</p>
|
||||
)}
|
||||
```
|
||||
|
||||
Shows specific reason:
|
||||
- "Time's up! Press space to restart"
|
||||
- "Too many hints! Press space to restart"
|
||||
|
||||
### Technical Details
|
||||
|
||||
**Check Frequency:**
|
||||
- Both checks run on every render during 'started' state
|
||||
- useEffect dependencies: `[gameStatus, timeValue, hintsUsed, difficulty, timeLimit]`
|
||||
- Checks happen before win condition check
|
||||
|
||||
**Priority:**
|
||||
1. Game-over checks (time, hints)
|
||||
2. Win condition check (board complete)
|
||||
|
||||
**Time Limit Examples:**
|
||||
```
|
||||
Default (10 minutes):
|
||||
- Player has 600 seconds to complete puzzle
|
||||
- At 600s: game-over triggered
|
||||
|
||||
Custom (5 minutes):
|
||||
- GameOptions: { timeLimit: 300 }
|
||||
- Player has 300 seconds to complete
|
||||
```
|
||||
|
||||
**Hint Limit Examples:**
|
||||
```
|
||||
Easy mode (25 pts/hint, 500 limit):
|
||||
- Hint 1-19: OK
|
||||
- Hint 20: 20 × 25 = 500 → game-over
|
||||
|
||||
Hard mode (100 pts/hint, 500 limit):
|
||||
- Hint 1-4: OK
|
||||
- Hint 5: 5 × 100 = 500 → game-over
|
||||
```
|
||||
|
||||
### Design Rationale
|
||||
|
||||
**Why Same Time Limit for All Difficulties?**
|
||||
- Harder puzzles naturally take longer to solve
|
||||
- Creates appropriate pressure at each level
|
||||
- Simple and consistent rule
|
||||
|
||||
**Why 500 Point Hint Limit?**
|
||||
- Half of max score (1000)
|
||||
- Prevents hint abuse
|
||||
- Scales appropriately with difficulty:
|
||||
- Easy: More hints allowed (puzzle easier)
|
||||
- Hard: Fewer hints allowed (puzzle harder)
|
||||
|
||||
**Why Two Separate Conditions?**
|
||||
- Time limit: Encourages speed
|
||||
- Hint limit: Encourages skill
|
||||
- Both contribute to challenge
|
||||
|
||||
### Build Status
|
||||
- ✅ TypeScript compiles cleanly
|
||||
- ✅ Both game-over conditions working
|
||||
- ✅ Reasons displayed correctly
|
||||
- ✅ Timer stops on game-over
|
||||
- ✅ Configurable time limit working
|
||||
|
||||
### Next Steps
|
||||
- Final polish and testing
|
||||
- Update UNIFIED_API.md
|
||||
|
||||
---
|
||||
|
||||
## Iteration 16: [Pending]
|
||||
|
||||
*To be filled as implementation progresses...*
|
||||
|
||||
Reference in New Issue
Block a user