diff --git a/mathGame.MichaelHelgesen/Controllers/GameController.cs b/mathGame.MichaelHelgesen/Controllers/GameController.cs new file mode 100644 index 00000000..95865583 --- /dev/null +++ b/mathGame.MichaelHelgesen/Controllers/GameController.cs @@ -0,0 +1,34 @@ +namespace mathGame.MichaelHelgesen.Controllers; +using mathGame.MichaelHelgesen.Enums; +using mathGame.MichaelHelgesen.Models; +using Spectre.Console; + + +class GameController() +{ + internal static Archive archive = new(); + + internal static void RunGame(GameType gameType) + { + Console.Clear(); + AnsiConsole.Prompt( + new TextPrompt($"[grey]Press [bold]Enter[/] when ready. Your game is beeing timed.[/]") + .AllowEmpty() + ); + Console.Clear(); + + var gameRound = Game.PlayRound(gameType); + archive.ArchiveGameRound(gameRound); + + Console.Clear(); + + archive.DisplayLastGame(); + + AnsiConsole.Prompt( + new TextPrompt($"[grey]Press [bold]Enter[/] to return to main menu.[/]") + .AllowEmpty() + ); + MenuController.RenderMainMenu(); + } +} + diff --git a/mathGame.MichaelHelgesen/Controllers/MenuController.cs b/mathGame.MichaelHelgesen/Controllers/MenuController.cs new file mode 100644 index 00000000..4cc5167c --- /dev/null +++ b/mathGame.MichaelHelgesen/Controllers/MenuController.cs @@ -0,0 +1,151 @@ +namespace mathGame.MichaelHelgesen.Controllers; + +using mathGame.MichaelHelgesen.Enums; +using mathGame.MichaelHelgesen.Models; +using Spectre.Console; + +class MenuController +{ + internal static void RenderMainMenu() + { + Console.Clear(); + + AnsiConsole.MarkupLine("[bold blue]Welcome[/] to [green]the Math Game[/]!"); + AnsiConsole.MarkupLine($"[bold blue]Current difficulty:[/] [green]{Game.DifficultySetting}[/].\n"); + + var mainMenuChoice = DisplayMainMenu(); + + switch (mainMenuChoice) + { + case GameChoice.Play: + DisplayGameMenu(); + break; + case GameChoice.Results: + DisplayResults(mainMenuChoice); + break; + case GameChoice.Difficulty: + DisplayDifficultyMenu(); + break; + } + } + + private static void DisplayDifficultyMenu() + { + Console.Clear(); + var DifficultyChoices = Enum.GetValues(); + + var difficultyChoice = AnsiConsole.Prompt( + new SelectionPrompt() + .Title("Choose [green]difficulty[/] level:") + .UseConverter(item => item switch + { + Difficulty.Back => "[yellow]<- Back to Main Menu[/]", + _ => GenerateDifficultyMenu(item) + }) + .AddChoices(DifficultyChoices)); + + if (difficultyChoice == Difficulty.Back) + { + RenderMainMenu(); + return; + } + Game.DifficultySetting = difficultyChoice; + RenderMainMenu(); + } + + private static void DisplayResults(GameChoice mainMenuChoice) + { + Console.Clear(); + AnsiConsole.MarkupLine($"[yellow]{mainMenuChoice}[/]:\n"); + GameController.archive.DisplayArchive(); + AnsiConsole.Prompt( + new TextPrompt($"[grey]Press [bold]Enter[/] to return to main menu.[/]") + .AllowEmpty() + ); + RenderMainMenu(); + } + private static void DisplayGameMenu() + { + Console.Clear(); + var GameChoices = Enum.GetValues(); + + var gameChoice = AnsiConsole.Prompt( + new SelectionPrompt() + .Title("Choose a [green]game mode[/] from the meny to continue.") + .UseConverter(item => item switch + { + GameType.Back => "[yellow]<- Back to Main Menu[/]", + _ => GenerateGameMenu(item) + }) + .AddChoices(GameChoices)); + + if (gameChoice == GameType.Back) + { + RenderMainMenu(); + return; + } + GameController.RunGame(gameChoice); + } + + private static GameChoice DisplayMainMenu() + { + var menuChoices = GenerateMainMenuChoices(); + + var menuChoice = AnsiConsole.Prompt( + new SelectionPrompt() + .Title("Please choose [green]an option[/] from the meny below") + .UseConverter(item => GenerateMainMenuItem(item)) + .AddChoices(menuChoices)); + return menuChoice; + } + + private static List GenerateMainMenuChoices() + { + var choices = Enum.GetValues().ToList(); + if (GameController.archive.ArchivedGames.Count < 1) choices.Remove(GameChoice.Results); + return choices; + } + + private static string GenerateMainMenuItem(GameChoice item) + { + return item switch + { + GameChoice.Difficulty => "🪜 Choose difficulty", + GameChoice.Play => "▶️ Play a round of The Math Game", + GameChoice.Results => "📊 Show results of previous games", + GameChoice.Quit => "❌ Quit the game", + _ => item.ToString() + }; + } + + private static string GenerateGameMenu(GameType item) + { + return item switch + { + GameType.Addition => "➕ Add numbers (addition)", + GameType.Subtraction => "➖ Subtract numbers (subtraction)", + GameType.Division => "➗ Divide numbers (division)", + GameType.Multiplication => "✖️ Multiply numbers (multiplication)", + GameType.Random => "🔄 Random (random operators)", + _ => item.ToString() + }; + } + + private static string GenerateDifficultyMenu(Difficulty item) + { + bool isSelected = item == Game.DifficultySetting; + + string text = item switch + { + Difficulty.Easy => "Easy: up to 10", + Difficulty.Normal => "Normal: up to 100", + Difficulty.Hard => "Difficult: up to 1000", + _ => item.ToString() + }; + if (isSelected) + { + return $"[bold green]{text} (active)[/]"; + } + return $"[grey]{text}[/]"; + } +} \ No newline at end of file diff --git a/mathGame.MichaelHelgesen/Enums/Enums.cs b/mathGame.MichaelHelgesen/Enums/Enums.cs new file mode 100644 index 00000000..a28252bb --- /dev/null +++ b/mathGame.MichaelHelgesen/Enums/Enums.cs @@ -0,0 +1,27 @@ +namespace mathGame.MichaelHelgesen.Enums; + +enum GameType +{ + Addition, + Multiplication, + Division, + Subtraction, + Random, + Back +} + +enum Difficulty +{ + Easy, + Normal, + Hard, + Back +} + +enum GameChoice +{ + Play, + Results, + Difficulty, + Quit +} \ No newline at end of file diff --git a/mathGame.MichaelHelgesen/Models/Archive.cs b/mathGame.MichaelHelgesen/Models/Archive.cs new file mode 100644 index 00000000..b3081d91 --- /dev/null +++ b/mathGame.MichaelHelgesen/Models/Archive.cs @@ -0,0 +1,50 @@ +namespace mathGame.MichaelHelgesen.Models; +using Spectre.Console; + +class Archive +{ + internal List ArchivedGames = new(); + + internal void ArchiveGameRound(Game.GameData game) + { + ArchivedGames.Add(game); + } + + internal void DisplayLastGame() + { + if (ArchivedGames.Count == 0) return; + + DisplayArchive([ArchivedGames[^1]]); + } + + internal void DisplayArchive() + { + DisplayArchive(ArchivedGames); + } + + void DisplayArchive(List games) + { + foreach (var gameRound in games) + { + AnsiConsole.MarkupLine($"[bold blue]Game #{gameRound.GameNumber} - {gameRound.GameType}[/]"); + AnsiConsole.MarkupLine($"[dim]Time used:[/] [yellow]{gameRound.TimeUsed:mm\\:ss}[/]"); + AnsiConsole.MarkupLine($"[dim]Total points:[/] [bold underline]{gameRound.Points()}[/]\n"); + + for (int i = 0; i < gameRound.GameTurns.Count; i++) + { + var turn = gameRound.GameTurns[i]; + bool isCorrect = turn.PlayerAnswer.IsCorrect; + + string statusColor = isCorrect ? "green" : "red"; + string statusIcon = isCorrect ? "[✓]" : "[✗]"; + + AnsiConsole.MarkupLine( + $" [dim]Round {i + 1}:[/] {turn.MathProblem.AsString} = [bold]{turn.MathProblem.CorrectAnswer}[/]" + + $"\tYour answer: [{statusColor}]{turn.PlayerAnswer.Answer} [{statusIcon}][/]" + ); + } + + AnsiConsole.WriteLine(); + } + } +} \ No newline at end of file diff --git a/mathGame.MichaelHelgesen/Models/Game.cs b/mathGame.MichaelHelgesen/Models/Game.cs new file mode 100644 index 00000000..714b390e --- /dev/null +++ b/mathGame.MichaelHelgesen/Models/Game.cs @@ -0,0 +1,70 @@ +namespace mathGame.MichaelHelgesen.Models; + +using mathGame.MichaelHelgesen.Enums; +using Spectre.Console; +using System.Diagnostics; + +class Game +{ + static readonly int numberOfTurns = 5; + + internal static Difficulty DifficultySetting { get; set; } = Difficulty.Easy; + + internal static GameData PlayRound(GameType gameType) + { + long startTimestamp = Stopwatch.GetTimestamp(); + + List turns = []; + + PlayTurns(gameType, turns); + + TimeSpan timeUsed = Stopwatch.GetElapsedTime(startTimestamp); + + GameData playedGame = new GameData(gameType, turns, timeUsed); + + return playedGame; + } + internal class GameTurn(MathProblem mathProblem, PlayerAnswer playerAnswer) + { + internal MathProblem MathProblem { get; } = mathProblem; + internal PlayerAnswer PlayerAnswer { get; } = playerAnswer; + } + internal class GameData(GameType gameType, List turns, TimeSpan timeUsed) + { + static int _nextGameNumber = 1; + internal int GameNumber { get; } = _nextGameNumber++; + internal TimeSpan TimeUsed { get; } = timeUsed; + internal GameType GameType { get; } = gameType; + internal List GameTurns { get; } = turns; + + + internal int Points() + { + int points = 0; + foreach (var turn in GameTurns) + { + if (turn.PlayerAnswer.IsCorrect) + points++; + } + return points; + } + } + + static void PlayTurns(GameType gameType, List turns) + { + for (int i = 0; i < numberOfTurns; i++) + { + Console.Clear(); + + MathProblem mathProblem = new MathProblem(gameType); + + AnsiConsole.MarkupLine($"[bold blue]Question {i + 1} of 5[/]"); + + int playerAnswer = AnsiConsole.Ask($"What is [green]{mathProblem.AsString}[/]?"); + PlayerAnswer answer = new PlayerAnswer(mathProblem.CorrectAnswer, playerAnswer); + GameTurn gameTurn = new GameTurn(mathProblem, answer); + turns.Add(gameTurn); + + } + } +} \ No newline at end of file diff --git a/mathGame.MichaelHelgesen/Models/MathProblem.cs b/mathGame.MichaelHelgesen/Models/MathProblem.cs new file mode 100644 index 00000000..68278c2d --- /dev/null +++ b/mathGame.MichaelHelgesen/Models/MathProblem.cs @@ -0,0 +1,65 @@ +using mathGame.MichaelHelgesen.Enums; + +namespace mathGame.MichaelHelgesen.Models; + +class MathProblem +{ + int FirstNumber { get; set; } + int SecondNumber { get; set; } + string Operator { get; } + internal int CorrectAnswer { get; set; } + + internal MathProblem(GameType gameType) + { + + if (gameType == GameType.Random) gameType = GenerateRandomOperator(); + + GenerateNumbers(); + + switch (gameType) + { + case GameType.Addition: + Operator = "+"; + CorrectAnswer = FirstNumber + SecondNumber; + break; + case GameType.Subtraction: + Operator = "-"; + CorrectAnswer = FirstNumber - SecondNumber; + break; + case GameType.Multiplication: + Operator = "*"; + CorrectAnswer = FirstNumber * SecondNumber; + break; + default: + FirstNumber *= SecondNumber; + Operator = "/"; + CorrectAnswer = FirstNumber / SecondNumber; + break; + } + } + + void GenerateNumbers() + { + + var maxNumber = Game.DifficultySetting switch + { + Difficulty.Hard => 1000, + Difficulty.Easy => 10, + _ => 100, + }; + + FirstNumber = Random.Shared.Next(0, maxNumber); + SecondNumber = Random.Shared.Next(1, maxNumber); + } + + GameType GenerateRandomOperator() + { + + var values = Enum.GetValues() + .Where(m => m != GameType.Random) + .ToArray(); + return values[Random.Shared.Next(values.Length)]; + } + + internal string AsString => $"{FirstNumber} {Operator} {SecondNumber}"; +} \ No newline at end of file diff --git a/mathGame.MichaelHelgesen/Models/PlayerAnswer.cs b/mathGame.MichaelHelgesen/Models/PlayerAnswer.cs new file mode 100644 index 00000000..b2c24c2c --- /dev/null +++ b/mathGame.MichaelHelgesen/Models/PlayerAnswer.cs @@ -0,0 +1,8 @@ +namespace mathGame.MichaelHelgesen.Models; + +class PlayerAnswer(int correctMathAnswer, int playerAnswer) +{ + internal int Answer { get; } = playerAnswer; + int MathAnswer { get; } = correctMathAnswer; + internal bool IsCorrect => Answer == MathAnswer; +} \ No newline at end of file diff --git a/mathGame.MichaelHelgesen/Program.cs b/mathGame.MichaelHelgesen/Program.cs new file mode 100644 index 00000000..decc99ac --- /dev/null +++ b/mathGame.MichaelHelgesen/Program.cs @@ -0,0 +1,2 @@ +using mathGame.MichaelHelgesen.Controllers; +MenuController.RenderMainMenu(); \ No newline at end of file diff --git a/mathGame.MichaelHelgesen/math_game.csproj b/mathGame.MichaelHelgesen/math_game.csproj new file mode 100644 index 00000000..2d744420 --- /dev/null +++ b/mathGame.MichaelHelgesen/math_game.csproj @@ -0,0 +1,14 @@ + + + + Exe + net8.0 + enable + enable + + + + + + +