107 lines
3.5 KiB
Python
107 lines
3.5 KiB
Python
"""High score and game statistics management."""
|
|
import json
|
|
from pathlib import Path
|
|
from typing import Optional, Dict, List
|
|
from dataclasses import dataclass, asdict
|
|
from datetime import datetime
|
|
|
|
|
|
@dataclass
|
|
class GameRecord:
|
|
"""A single game record."""
|
|
player_name: str
|
|
score: int
|
|
length: int
|
|
speed: int
|
|
timestamp: str
|
|
|
|
@classmethod
|
|
def from_dict(cls, data: dict) -> 'GameRecord':
|
|
return cls(**data)
|
|
|
|
def to_dict(self) -> dict:
|
|
return asdict(self)
|
|
|
|
|
|
class HighScoreManager:
|
|
"""Manages high scores and game statistics by player."""
|
|
|
|
def __init__(self):
|
|
self.scores_file = Path.home() / '.snake_scores.json'
|
|
self.game_records: List[GameRecord] = []
|
|
self.load_scores()
|
|
|
|
def load_scores(self) -> None:
|
|
"""Load scores from file."""
|
|
if self.scores_file.exists():
|
|
try:
|
|
with open(self.scores_file, 'r') as f:
|
|
data = json.load(f)
|
|
self.game_records = [GameRecord.from_dict(r) for r in data.get('records', [])]
|
|
except (json.JSONDecodeError, IOError):
|
|
self.game_records = []
|
|
else:
|
|
self.game_records = []
|
|
|
|
def save_game(self, player_name: str, score: int, length: int, speed: int) -> None:
|
|
"""Save a completed game."""
|
|
record = GameRecord(
|
|
player_name=player_name,
|
|
score=score,
|
|
length=length,
|
|
speed=speed,
|
|
timestamp=datetime.now().isoformat()
|
|
)
|
|
self.game_records.append(record)
|
|
self._save_to_file()
|
|
|
|
def _save_to_file(self) -> None:
|
|
"""Save scores to file."""
|
|
try:
|
|
with open(self.scores_file, 'w') as f:
|
|
json.dump({
|
|
'records': [r.to_dict() for r in self.game_records]
|
|
}, f, indent=2)
|
|
except IOError as e:
|
|
print(f"Warning: Could not save scores: {e}")
|
|
|
|
def get_player_stats(self, player_name: str) -> dict:
|
|
"""Get game statistics for a specific player."""
|
|
player_records = [r for r in self.game_records if r.player_name == player_name]
|
|
|
|
if not player_records:
|
|
return {
|
|
'total_games': 0,
|
|
'high_score': 0,
|
|
'average_score': 0,
|
|
'total_length': 0,
|
|
}
|
|
|
|
scores = [r.score for r in player_records]
|
|
return {
|
|
'total_games': len(player_records),
|
|
'high_score': max(scores),
|
|
'average_score': sum(scores) // len(scores),
|
|
'total_length': sum(r.length for r in player_records),
|
|
}
|
|
|
|
def get_all_stats(self) -> Dict[str, dict]:
|
|
"""Get statistics for all players."""
|
|
player_names = set(r.player_name for r in self.game_records)
|
|
return {name: self.get_player_stats(name) for name in player_names}
|
|
|
|
def get_leaderboard(self, limit: int = 10) -> List[tuple]:
|
|
"""Get top scores across all players."""
|
|
# Group by player and get their best score
|
|
player_best_scores = {}
|
|
for record in self.game_records:
|
|
if record.player_name not in player_best_scores:
|
|
player_best_scores[record.player_name] = record.score
|
|
else:
|
|
player_best_scores[record.player_name] = max(player_best_scores[record.player_name], record.score)
|
|
|
|
# Sort by score descending
|
|
sorted_scores = sorted(player_best_scores.items(), key=lambda x: x[1], reverse=True)
|
|
return sorted_scores[:limit]
|
|
|