Files
morse-trainer/stats.ts
Irinel Sarbu 3f98712766 first commit
2025-11-21 13:03:06 +01:00

127 lines
3.4 KiB
TypeScript

// 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<GameStats> {
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<void> {
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<GameStats> {
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<void> {
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;
}