// Game logic for morse code practice import { textToMorse, compareMorse, normalizeMorse } from "./morse.ts"; export type GameMode = 'letters' | 'numbers' | 'words' | 'phrases'; export interface GameConfig { mode: GameMode; rounds: number; timePerRound: number; // seconds } export interface RoundResult { challenge: string; expectedMorse: string; userInput: string; correct: boolean; timeSpent: number; // milliseconds } export interface GameSession { config: GameConfig; currentRound: number; results: RoundResult[]; currentStreak: number; bestStreak: number; } // Word banks for different difficulty levels const LETTERS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'.split(''); const NUMBERS = '0123456789'.split(''); const WORDS = [ 'HELLO', 'WORLD', 'CODE', 'TIME', 'GAME', 'TEST', 'PLAY', 'FAST', 'SLOW', 'HELP', 'GOOD', 'BEST', 'NICE', 'COOL', 'MORSE', 'SIGNAL', 'RADIO', 'SEND', 'MESSAGE', 'QUICK', 'LEARN', 'PRACTICE', 'SKILL', 'MASTER', 'EXPERT', ]; const PHRASES = [ 'HELLO WORLD', 'GOOD MORNING', 'HOW ARE YOU', 'THANK YOU', 'SEE YOU SOON', 'HAVE A NICE DAY', 'MORSE CODE', 'QUICK BROWN FOX', 'THE END', 'WELL DONE', ]; /** * Get a random challenge based on game mode */ export function getChallenge(mode: GameMode): string { switch (mode) { case 'letters': return LETTERS[Math.floor(Math.random() * LETTERS.length)]; case 'numbers': return NUMBERS[Math.floor(Math.random() * NUMBERS.length)]; case 'words': return WORDS[Math.floor(Math.random() * WORDS.length)]; case 'phrases': return PHRASES[Math.floor(Math.random() * PHRASES.length)]; } } /** * Create a new game session */ export function createGameSession(config: GameConfig): GameSession { return { config, currentRound: 0, results: [], currentStreak: 0, bestStreak: 0, }; } /** * Process a round result */ export function processRound( session: GameSession, challenge: string, userInput: string, timeSpent: number ): RoundResult { const expectedMorse = textToMorse(challenge); const normalizedInput = normalizeMorse(userInput); const correct = compareMorse(expectedMorse, normalizedInput); const result: RoundResult = { challenge, expectedMorse, userInput: normalizedInput, correct, timeSpent, }; session.results.push(result); session.currentRound++; // Update streak if (correct) { session.currentStreak++; session.bestStreak = Math.max(session.bestStreak, session.currentStreak); } else { session.currentStreak = 0; } return result; } /** * Check if game is complete */ export function isGameComplete(session: GameSession): boolean { return session.currentRound >= session.config.rounds; } /** * Get game summary statistics */ export function getGameSummary(session: GameSession) { const totalRounds = session.results.length; const correct = session.results.filter(r => r.correct).length; const incorrect = totalRounds - correct; const accuracy = totalRounds > 0 ? (correct / totalRounds) * 100 : 0; const averageTime = totalRounds > 0 ? session.results.reduce((sum, r) => sum + r.timeSpent, 0) / totalRounds : 0; return { totalRounds, correct, incorrect, accuracy, averageTime, streak: session.bestStreak, }; } /** * Get difficulty description for a mode */ export function getDifficultyDescription(mode: GameMode): string { switch (mode) { case 'letters': return 'Single letters A-Z'; case 'numbers': return 'Single digits 0-9'; case 'words': return 'Common 4-6 letter words'; case 'phrases': return 'Short phrases'; } }