feat: changed stats storage location, refactored code
This commit is contained in:
110
ui.ts
110
ui.ts
@@ -1,22 +1,15 @@
|
||||
// UI utilities and game interface
|
||||
|
||||
import { colors } from "@cliffy/ansi/colors";
|
||||
import { Confirm } from "@cliffy/prompt/confirm";
|
||||
import { Input } from "@cliffy/prompt/input";
|
||||
import { Select } from "@cliffy/prompt/select";
|
||||
import { Confirm } from "@cliffy/prompt/confirm";
|
||||
import { Table } from "@cliffy/table";
|
||||
import { textToMorse } from "./morse.ts";
|
||||
import type { GameMode, GameSession, RoundResult } from "./game.ts";
|
||||
import { getChallenge, getGameSummary, processRound } from "./game.ts";
|
||||
import { morseToText, textToMorse } from "./morse.ts";
|
||||
import type { GameStats } from "./stats.ts";
|
||||
import { getAccuracy } from "./stats.ts";
|
||||
import type { GameSession, RoundResult, GameMode } from "./game.ts";
|
||||
import {
|
||||
createGameSession,
|
||||
getChallenge,
|
||||
processRound,
|
||||
isGameComplete,
|
||||
getGameSummary,
|
||||
getDifficultyDescription,
|
||||
} from "./game.ts";
|
||||
|
||||
/**
|
||||
* Clear the terminal screen
|
||||
@@ -29,12 +22,14 @@ export function clearScreen(): void {
|
||||
* Print a banner/header
|
||||
*/
|
||||
export function printBanner(): void {
|
||||
console.log(colors.bold.cyan(`
|
||||
console.log(
|
||||
colors.bold.cyan(`
|
||||
╔═══════════════════════════════════════╗
|
||||
║ MORSE CODE PRACTICE GAME ║
|
||||
║ .... . .-.. .-.. --- ║
|
||||
╚═══════════════════════════════════════╝
|
||||
`));
|
||||
`)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -59,35 +54,37 @@ export async function showMainMenu(): Promise<string> {
|
||||
* Select game mode
|
||||
*/
|
||||
export async function selectGameMode(): Promise<GameMode> {
|
||||
const mode = await Select.prompt<GameMode>({
|
||||
const mode = await Select.prompt({
|
||||
message: "Select difficulty mode:",
|
||||
options: [
|
||||
{
|
||||
name: `${colors.green("Easy")} - Letters (A-Z)`,
|
||||
value: "letters" as GameMode
|
||||
value: "letters",
|
||||
},
|
||||
{
|
||||
name: `${colors.yellow("Medium")} - Numbers (0-9)`,
|
||||
value: "numbers" as GameMode
|
||||
value: "numbers",
|
||||
},
|
||||
{
|
||||
name: `${colors.magenta("Hard")} - Words`,
|
||||
value: "words" as GameMode
|
||||
value: "words",
|
||||
},
|
||||
{
|
||||
name: `${colors.red("Expert")} - Phrases`,
|
||||
value: "phrases" as GameMode
|
||||
value: "phrases",
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
return mode;
|
||||
return mode as GameMode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure game settings
|
||||
*/
|
||||
export async function configureGame(mode: GameMode): Promise<{ rounds: number; timePerRound: number }> {
|
||||
export async function configureGame(
|
||||
_mode: GameMode
|
||||
): Promise<{ rounds: number; timePerRound: number }> {
|
||||
const roundsInput = await Input.prompt({
|
||||
message: "How many rounds? (5-50)",
|
||||
default: "10",
|
||||
@@ -130,11 +127,21 @@ export async function playRound(
|
||||
|
||||
clearScreen();
|
||||
printBanner();
|
||||
console.log(colors.bold(`\nRound ${roundNumber} of ${session.config.rounds}`));
|
||||
console.log(colors.gray(`Time limit: ${session.config.timePerRound}s | Current streak: ${session.currentStreak}\n`));
|
||||
console.log(
|
||||
colors.bold(`\nRound ${roundNumber} of ${session.config.rounds}`)
|
||||
);
|
||||
console.log(
|
||||
colors.gray(
|
||||
`Time limit: ${session.config.timePerRound}s | Current streak: ${session.currentStreak}\n`
|
||||
)
|
||||
);
|
||||
|
||||
console.log(colors.bold.white(`Translate to Morse code: ${colors.yellow(challenge)}\n`));
|
||||
console.log(colors.dim("Tip: Use dots (.) and dashes (-), separate letters with spaces"));
|
||||
console.log(
|
||||
colors.bold.white(`Translate to Morse code: ${colors.yellow(challenge)}\n`)
|
||||
);
|
||||
console.log(
|
||||
colors.dim("Tip: Use dots (.) and dashes (-), separate letters with spaces")
|
||||
);
|
||||
console.log(colors.dim(" Use / for word spaces\n"));
|
||||
|
||||
// Create a promise that rejects after the time limit
|
||||
@@ -173,6 +180,18 @@ export async function playRound(
|
||||
console.log(colors.bold.red("✗ Incorrect"));
|
||||
console.log(colors.gray(`Expected: ${result.expectedMorse}`));
|
||||
console.log(colors.gray(`You entered: ${result.userInput || "(nothing)"}`));
|
||||
|
||||
// Show what the user's morse code translates to
|
||||
if (result.userInput) {
|
||||
try {
|
||||
const decoded = morseToText(result.userInput);
|
||||
if (decoded && decoded !== result.userInput) {
|
||||
console.log(colors.yellow(`Your morse translates to: ${decoded}`));
|
||||
}
|
||||
} catch {
|
||||
// If decoding fails, just skip showing it
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Wait for user to continue
|
||||
@@ -210,15 +229,14 @@ export function showGameResults(session: GameSession): void {
|
||||
// Show detailed results
|
||||
console.log(colors.bold("\n📊 Round Details:\n"));
|
||||
|
||||
const detailsTable = new Table()
|
||||
.header([
|
||||
colors.bold("#"),
|
||||
colors.bold("Challenge"),
|
||||
colors.bold("Expected"),
|
||||
colors.bold("Your Answer"),
|
||||
colors.bold("Result"),
|
||||
colors.bold("Time"),
|
||||
]);
|
||||
const detailsTable = new Table().header([
|
||||
colors.bold("#"),
|
||||
colors.bold("Challenge"),
|
||||
colors.bold("Expected"),
|
||||
colors.bold("Your Answer"),
|
||||
colors.bold("Result"),
|
||||
colors.bold("Time"),
|
||||
]);
|
||||
|
||||
session.results.forEach((result, index) => {
|
||||
detailsTable.push([
|
||||
@@ -244,7 +262,9 @@ export function showStats(stats: GameStats): void {
|
||||
console.log(colors.bold.cyan("\n📈 YOUR STATISTICS\n"));
|
||||
|
||||
if (stats.totalGames === 0) {
|
||||
console.log(colors.yellow("No games played yet! Start playing to see your stats.\n"));
|
||||
console.log(
|
||||
colors.yellow("No games played yet! Start playing to see your stats.\n")
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -259,7 +279,12 @@ export function showStats(stats: GameStats): void {
|
||||
["Overall Accuracy", `${getAccuracy(stats).toFixed(1)}%`],
|
||||
["Average Time/Round", `${stats.averageTimePerRound.toFixed(1)}s`],
|
||||
["Best Streak", colors.yellow(stats.bestStreak.toString())],
|
||||
["Last Played", stats.lastPlayed ? new Date(stats.lastPlayed).toLocaleDateString() : "Never"],
|
||||
[
|
||||
"Last Played",
|
||||
stats.lastPlayed
|
||||
? new Date(stats.lastPlayed).toLocaleDateString()
|
||||
: "Never",
|
||||
],
|
||||
])
|
||||
.border(true)
|
||||
.padding(1);
|
||||
@@ -269,14 +294,13 @@ export function showStats(stats: GameStats): void {
|
||||
// Mode-specific stats
|
||||
console.log(colors.bold("\n📊 Stats by Mode:\n"));
|
||||
|
||||
const modeTable = new Table()
|
||||
.header([
|
||||
colors.bold("Mode"),
|
||||
colors.bold("Games"),
|
||||
colors.bold("Correct"),
|
||||
colors.bold("Incorrect"),
|
||||
colors.bold("Accuracy"),
|
||||
]);
|
||||
const modeTable = new Table().header([
|
||||
colors.bold("Mode"),
|
||||
colors.bold("Games"),
|
||||
colors.bold("Correct"),
|
||||
colors.bold("Incorrect"),
|
||||
colors.bold("Accuracy"),
|
||||
]);
|
||||
|
||||
Object.entries(stats.modeStats).forEach(([mode, modeStats]) => {
|
||||
if (modeStats.gamesPlayed > 0) {
|
||||
|
||||
Reference in New Issue
Block a user