Files
morse-trainer/stats.ts

159 lines
3.7 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;
alphanumeric: ModeStats;
full: ModeStats;
words: ModeStats;
phrases: ModeStats;
};
lastPlayed?: string;
}
export interface ModeStats {
gamesPlayed: number;
correctAnswers: number;
incorrectAnswers: number;
averageAccuracy: number;
}
export interface GameResult {
mode: "letters" | "alphanumeric" | "full" | "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,
},
alphanumeric: {
gamesPlayed: 0,
correctAnswers: 0,
incorrectAnswers: 0,
averageAccuracy: 0,
},
full: {
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 {
return join(Deno.cwd(), "data", "stats.json");
}
/**
* Load stats from JSON file
*/
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 JSON file
*/
export async function saveStats(stats: GameStats): Promise<void> {
try {
const statsPath = getStatsPath();
const dataDir = join(Deno.cwd(), "data");
await ensureDir(dataDir);
await Deno.writeTextFile(statsPath, JSON.stringify(stats, null, 2));
} catch (error) {
throw new Error(
`Failed to save statistics: ${error instanceof Error ? error.message : "Unknown error"}`
);
}
}
/**
* 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;
}