// User statistics tracking and persistence import { join } from "@std/path"; import { ensureDir } from "@std/fs"; export interface GameStats { totalGames: number; totalRounds: number; correctAnswers: number; incorrectAnswers: number; averageTimePerRound: number; bestStreak: number; modeStats: { letters: ModeStats; numbers: ModeStats; words: ModeStats; phrases: ModeStats; }; lastPlayed?: string; } export interface ModeStats { gamesPlayed: number; correctAnswers: number; incorrectAnswers: number; averageAccuracy: number; } export interface GameResult { mode: 'letters' | 'numbers' | 'words' | 'phrases'; rounds: number; correct: number; incorrect: number; averageTime: number; streak: number; } const DEFAULT_STATS: GameStats = { totalGames: 0, totalRounds: 0, correctAnswers: 0, incorrectAnswers: 0, averageTimePerRound: 0, bestStreak: 0, modeStats: { letters: { gamesPlayed: 0, correctAnswers: 0, incorrectAnswers: 0, averageAccuracy: 0 }, numbers: { gamesPlayed: 0, correctAnswers: 0, incorrectAnswers: 0, averageAccuracy: 0 }, words: { gamesPlayed: 0, correctAnswers: 0, incorrectAnswers: 0, averageAccuracy: 0 }, phrases: { gamesPlayed: 0, correctAnswers: 0, incorrectAnswers: 0, averageAccuracy: 0 }, }, }; function getStatsPath(): string { const homeDir = Deno.env.get("HOME") || Deno.env.get("USERPROFILE") || "."; return join(homeDir, ".morse-game", "stats.json"); } /** * Load stats from disk */ export async function loadStats(): Promise { try { const statsPath = getStatsPath(); const content = await Deno.readTextFile(statsPath); return JSON.parse(content); } catch { return { ...DEFAULT_STATS }; } } /** * Save stats to disk */ export async function saveStats(stats: GameStats): Promise { const statsPath = getStatsPath(); const dir = join(Deno.env.get("HOME") || Deno.env.get("USERPROFILE") || ".", ".morse-game"); await ensureDir(dir); await Deno.writeTextFile(statsPath, JSON.stringify(stats, null, 2)); } /** * Update stats with a new game result */ export async function updateStats(result: GameResult): Promise { const stats = await loadStats(); // Update overall stats stats.totalGames++; stats.totalRounds += result.rounds; stats.correctAnswers += result.correct; stats.incorrectAnswers += result.incorrect; stats.averageTimePerRound = (stats.averageTimePerRound * (stats.totalRounds - result.rounds) + result.averageTime * result.rounds) / stats.totalRounds; stats.bestStreak = Math.max(stats.bestStreak, result.streak); stats.lastPlayed = new Date().toISOString(); // Update mode-specific stats const modeStats = stats.modeStats[result.mode]; modeStats.gamesPlayed++; modeStats.correctAnswers += result.correct; modeStats.incorrectAnswers += result.incorrect; const totalAnswers = modeStats.correctAnswers + modeStats.incorrectAnswers; modeStats.averageAccuracy = totalAnswers > 0 ? (modeStats.correctAnswers / totalAnswers) * 100 : 0; await saveStats(stats); return stats; } /** * Reset all stats */ export async function resetStats(): Promise { await saveStats({ ...DEFAULT_STATS }); } /** * Get formatted accuracy percentage */ export function getAccuracy(stats: GameStats): number { const total = stats.correctAnswers + stats.incorrectAnswers; return total > 0 ? (stats.correctAnswers / total) * 100 : 0; }