first commit
This commit is contained in:
276
main.ts
Normal file
276
main.ts
Normal file
@@ -0,0 +1,276 @@
|
||||
#!/usr/bin/env -S deno run --allow-read --allow-write --allow-env
|
||||
|
||||
import { Command } from "@cliffy/command";
|
||||
import { colors } from "@cliffy/ansi/colors";
|
||||
import {
|
||||
clearScreen,
|
||||
printBanner,
|
||||
showMainMenu,
|
||||
selectGameMode,
|
||||
configureGame,
|
||||
playRound,
|
||||
showGameResults,
|
||||
showStats,
|
||||
showReference,
|
||||
confirmAction,
|
||||
} from "./ui.ts";
|
||||
import { createGameSession, isGameComplete } from "./game.ts";
|
||||
import { loadStats, updateStats, resetStats, getAccuracy } from "./stats.ts";
|
||||
import type { GameResult } from "./stats.ts";
|
||||
|
||||
/**
|
||||
* Main application class
|
||||
*/
|
||||
class MorseGame {
|
||||
/**
|
||||
* Run the interactive menu
|
||||
*/
|
||||
async runInteractive(): Promise<void> {
|
||||
while (true) {
|
||||
clearScreen();
|
||||
printBanner();
|
||||
|
||||
const action = await showMainMenu();
|
||||
|
||||
switch (action) {
|
||||
case "play":
|
||||
await this.playGame();
|
||||
break;
|
||||
case "stats":
|
||||
await this.viewStats();
|
||||
break;
|
||||
case "reference":
|
||||
await this.showReference();
|
||||
break;
|
||||
case "reset":
|
||||
await this.resetStats();
|
||||
break;
|
||||
case "exit":
|
||||
console.log(colors.cyan("\nThanks for playing! 73 (Best regards in morse)\n"));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Play a game
|
||||
*/
|
||||
async playGame(): Promise<void> {
|
||||
const mode = await selectGameMode();
|
||||
const config = await configureGame(mode);
|
||||
|
||||
const session = createGameSession({
|
||||
mode,
|
||||
rounds: config.rounds,
|
||||
timePerRound: config.timePerRound,
|
||||
});
|
||||
|
||||
// Play all rounds
|
||||
for (let i = 0; i < config.rounds; i++) {
|
||||
await playRound(session, i + 1);
|
||||
|
||||
if (isGameComplete(session)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Show results
|
||||
showGameResults(session);
|
||||
|
||||
// Update stats
|
||||
const summary = session.results.reduce(
|
||||
(acc, result) => ({
|
||||
correct: acc.correct + (result.correct ? 1 : 0),
|
||||
incorrect: acc.incorrect + (result.correct ? 0 : 1),
|
||||
totalTime: acc.totalTime + result.timeSpent,
|
||||
}),
|
||||
{ correct: 0, incorrect: 0, totalTime: 0 }
|
||||
);
|
||||
|
||||
const gameResult: GameResult = {
|
||||
mode,
|
||||
rounds: session.results.length,
|
||||
correct: summary.correct,
|
||||
incorrect: summary.incorrect,
|
||||
averageTime: summary.totalTime / session.results.length / 1000,
|
||||
streak: session.bestStreak,
|
||||
};
|
||||
|
||||
await updateStats(gameResult);
|
||||
|
||||
// Wait for user to continue
|
||||
console.log(colors.gray("\nPress Enter to continue..."));
|
||||
await new Promise((resolve) => {
|
||||
const buf = new Uint8Array(1);
|
||||
Deno.stdin.setRaw(true);
|
||||
Deno.stdin.read(buf).then(() => {
|
||||
Deno.stdin.setRaw(false);
|
||||
resolve(undefined);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* View statistics
|
||||
*/
|
||||
async viewStats(): Promise<void> {
|
||||
const stats = await loadStats();
|
||||
showStats(stats);
|
||||
|
||||
// Wait for user to continue
|
||||
console.log(colors.gray("Press Enter to continue..."));
|
||||
await new Promise((resolve) => {
|
||||
const buf = new Uint8Array(1);
|
||||
Deno.stdin.setRaw(true);
|
||||
Deno.stdin.read(buf).then(() => {
|
||||
Deno.stdin.setRaw(false);
|
||||
resolve(undefined);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Show reference
|
||||
*/
|
||||
async showReference(): Promise<void> {
|
||||
showReference();
|
||||
|
||||
// Wait for user to continue
|
||||
console.log(colors.gray("Press Enter to continue..."));
|
||||
await new Promise((resolve) => {
|
||||
const buf = new Uint8Array(1);
|
||||
Deno.stdin.setRaw(true);
|
||||
Deno.stdin.read(buf).then(() => {
|
||||
Deno.stdin.setRaw(false);
|
||||
resolve(undefined);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset statistics
|
||||
*/
|
||||
async resetStats(): Promise<void> {
|
||||
const confirmed = await confirmAction(
|
||||
"Are you sure you want to reset all statistics? This cannot be undone."
|
||||
);
|
||||
|
||||
if (confirmed) {
|
||||
await resetStats();
|
||||
console.log(colors.green("\n✓ Statistics have been reset.\n"));
|
||||
await new Promise((resolve) => setTimeout(resolve, 1500));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Quick play with CLI arguments
|
||||
*/
|
||||
async quickPlay(mode: string, rounds: number, time: number): Promise<void> {
|
||||
const validModes = ["letters", "numbers", "words", "phrases"];
|
||||
if (!validModes.includes(mode)) {
|
||||
console.error(colors.red(`Invalid mode: ${mode}`));
|
||||
console.log(`Valid modes: ${validModes.join(", ")}`);
|
||||
Deno.exit(1);
|
||||
}
|
||||
|
||||
clearScreen();
|
||||
printBanner();
|
||||
|
||||
const session = createGameSession({
|
||||
mode: mode as any,
|
||||
rounds,
|
||||
timePerRound: time,
|
||||
});
|
||||
|
||||
// Play all rounds
|
||||
for (let i = 0; i < rounds; i++) {
|
||||
await playRound(session, i + 1);
|
||||
}
|
||||
|
||||
// Show results
|
||||
showGameResults(session);
|
||||
|
||||
// Update stats
|
||||
const summary = session.results.reduce(
|
||||
(acc, result) => ({
|
||||
correct: acc.correct + (result.correct ? 1 : 0),
|
||||
incorrect: acc.incorrect + (result.correct ? 0 : 1),
|
||||
totalTime: acc.totalTime + result.timeSpent,
|
||||
}),
|
||||
{ correct: 0, incorrect: 0, totalTime: 0 }
|
||||
);
|
||||
|
||||
const gameResult: GameResult = {
|
||||
mode: mode as any,
|
||||
rounds: session.results.length,
|
||||
correct: summary.correct,
|
||||
incorrect: summary.incorrect,
|
||||
averageTime: summary.totalTime / session.results.length / 1000,
|
||||
streak: session.bestStreak,
|
||||
};
|
||||
|
||||
await updateStats(gameResult);
|
||||
}
|
||||
|
||||
/**
|
||||
* Show quick stats
|
||||
*/
|
||||
async showQuickStats(): Promise<void> {
|
||||
const stats = await loadStats();
|
||||
|
||||
if (stats.totalGames === 0) {
|
||||
console.log(colors.yellow("No games played yet!"));
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(colors.bold.cyan("\n📈 QUICK STATS\n"));
|
||||
console.log(`Games Played: ${colors.bold(stats.totalGames.toString())}`);
|
||||
console.log(`Total Rounds: ${colors.bold(stats.totalRounds.toString())}`);
|
||||
console.log(`Accuracy: ${colors.bold(getAccuracy(stats).toFixed(1) + "%")}`);
|
||||
console.log(`Best Streak: ${colors.bold(stats.bestStreak.toString())}`);
|
||||
console.log();
|
||||
}
|
||||
}
|
||||
|
||||
// CLI Setup
|
||||
const app = new MorseGame();
|
||||
|
||||
await new Command()
|
||||
.name("morse-game")
|
||||
.version("1.0.0")
|
||||
.description("A terminal-based morse code practice game")
|
||||
.action(async () => {
|
||||
// Default action: run interactive mode
|
||||
await app.runInteractive();
|
||||
})
|
||||
.command("play", "Start a quick game with specified settings")
|
||||
.option("-m, --mode <mode:string>", "Game mode (letters, numbers, words, phrases)", {
|
||||
default: "letters",
|
||||
})
|
||||
.option("-r, --rounds <rounds:number>", "Number of rounds", { default: 10 })
|
||||
.option("-t, --time <seconds:number>", "Seconds per round", { default: 30 })
|
||||
.action(async (options) => {
|
||||
await app.quickPlay(options.mode, options.rounds, options.time);
|
||||
})
|
||||
.command("stats", "Display your statistics")
|
||||
.action(async () => {
|
||||
await app.showQuickStats();
|
||||
})
|
||||
.command("reset", "Reset all statistics")
|
||||
.action(async () => {
|
||||
const confirmed = await confirmAction(
|
||||
"Are you sure you want to reset all statistics? This cannot be undone."
|
||||
);
|
||||
|
||||
if (confirmed) {
|
||||
await resetStats();
|
||||
console.log(colors.green("\n✓ Statistics have been reset.\n"));
|
||||
} else {
|
||||
console.log(colors.yellow("\nCancelled.\n"));
|
||||
}
|
||||
})
|
||||
.command("reference", "Show morse code reference")
|
||||
.action(() => {
|
||||
showReference();
|
||||
})
|
||||
.parse(Deno.args);
|
||||
Reference in New Issue
Block a user