first commit
This commit is contained in:
126
stats.ts
Normal file
126
stats.ts
Normal file
@@ -0,0 +1,126 @@
|
||||
// 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;
|
||||
}
|
||||
Reference in New Issue
Block a user