first commit

This commit is contained in:
2025-11-21 13:03:06 +01:00
commit 043812606d
8 changed files with 1206 additions and 0 deletions

158
game.ts Normal file
View File

@@ -0,0 +1,158 @@
// 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';
}
}