diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000..2efc0762 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "dotnet.defaultSolution": "MathGame.sln" +} \ No newline at end of file diff --git a/MathGame.sln b/MathGame.sln new file mode 100644 index 00000000..6bc6d048 --- /dev/null +++ b/MathGame.sln @@ -0,0 +1,34 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.0.31903.59 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MathGame.vitorvelozo", "MathGame.vitorvelozo\MathGame.vitorvelozo.csproj", "{285CA9B0-CBE7-49C5-840F-AA8F7EAD0CD8}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {285CA9B0-CBE7-49C5-840F-AA8F7EAD0CD8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {285CA9B0-CBE7-49C5-840F-AA8F7EAD0CD8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {285CA9B0-CBE7-49C5-840F-AA8F7EAD0CD8}.Debug|x64.ActiveCfg = Debug|Any CPU + {285CA9B0-CBE7-49C5-840F-AA8F7EAD0CD8}.Debug|x64.Build.0 = Debug|Any CPU + {285CA9B0-CBE7-49C5-840F-AA8F7EAD0CD8}.Debug|x86.ActiveCfg = Debug|Any CPU + {285CA9B0-CBE7-49C5-840F-AA8F7EAD0CD8}.Debug|x86.Build.0 = Debug|Any CPU + {285CA9B0-CBE7-49C5-840F-AA8F7EAD0CD8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {285CA9B0-CBE7-49C5-840F-AA8F7EAD0CD8}.Release|Any CPU.Build.0 = Release|Any CPU + {285CA9B0-CBE7-49C5-840F-AA8F7EAD0CD8}.Release|x64.ActiveCfg = Release|Any CPU + {285CA9B0-CBE7-49C5-840F-AA8F7EAD0CD8}.Release|x64.Build.0 = Release|Any CPU + {285CA9B0-CBE7-49C5-840F-AA8F7EAD0CD8}.Release|x86.ActiveCfg = Release|Any CPU + {285CA9B0-CBE7-49C5-840F-AA8F7EAD0CD8}.Release|x86.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/MathGame.vitorvelozo/MathGame.vitorvelozo.csproj b/MathGame.vitorvelozo/MathGame.vitorvelozo.csproj new file mode 100644 index 00000000..452a0af1 --- /dev/null +++ b/MathGame.vitorvelozo/MathGame.vitorvelozo.csproj @@ -0,0 +1,14 @@ + + + + Exe + net10.0 + enable + enable + + + + + + + diff --git a/MathGame.vitorvelozo/Program.cs b/MathGame.vitorvelozo/Program.cs new file mode 100644 index 00000000..5faa2a04 --- /dev/null +++ b/MathGame.vitorvelozo/Program.cs @@ -0,0 +1,49 @@ +using CodeReviews.Console.MathGame.Controllers; +using CodeReviews.Console.MathGame.Models; +using CodeReviews.Console.MathGame.Views; + +Game RunGame() +{ + GameDifficulty difficulty = MainMenu.GetDifficulty(); + bool randomMode = MainMenu.GetRandomMode(); + + Game game = new Game(difficulty, randomMode: randomMode); + + for (int i = 0; i < game.GameLength; i++) + { + QuestionGenerator randomGenerator; + if (!randomMode) + { + randomGenerator = new QuestionGenerator(difficulty, QuestionMenu.GetOperation() + ); + } else + { + randomGenerator = new QuestionGenerator(difficulty); + } + Question question = randomGenerator.GenerateQuestion(); + game.Questions.Add(question); + + question.StartTime = TimeOnly.FromDateTime(DateTime.Now); + question.ProvidedAnswer = QuestionMenu.GetQuestionAnswer(question); + question.FinishTime = TimeOnly.FromDateTime(DateTime.Now); + game.PlayerScore += QuestionEvaluator.Evaluate(question); + } + return game; +} + +void Main() +{ + MainMenu.DisplayWelcome(); + MathSet mathSet = new MathSet(); + + bool keepPlaying; + do + { + mathSet.AddGame(RunGame()); + keepPlaying = GameMenu.Display(mathSet); + } while (keepPlaying); + + ResultScreen.Display(mathSet); +} + +Main(); diff --git a/MathGame.vitorvelozo/controllers/Evaluation.cs b/MathGame.vitorvelozo/controllers/Evaluation.cs new file mode 100644 index 00000000..1bd1b474 --- /dev/null +++ b/MathGame.vitorvelozo/controllers/Evaluation.cs @@ -0,0 +1,41 @@ +using CodeReviews.Console.MathGame.Models; + +namespace CodeReviews.Console.MathGame.Controllers; + +public static class QuestionEvaluator +{ + private static int CalculateResult(int firstNumber, int secondNumber, MathOperation operation) + { + int result; + switch (operation) + { + case MathOperation.Addition: + result = firstNumber + secondNumber; + break; + case MathOperation.Subtraction: + result = firstNumber - secondNumber; + break; + case MathOperation.Multiplication: + result = firstNumber * secondNumber; + break; + case MathOperation.Division: + result = firstNumber / secondNumber; + break; + default: + throw new ArgumentOutOfRangeException(nameof(operation), operation, null); + } + return result; + } + + public static int Evaluate(Question question) + { + question.Result = CalculateResult(question.FirstNumber, question.SecondNumber, question.Operation); + int answer; + if (int.TryParse(question.ProvidedAnswer, out answer)) + { + question.IsAnswerCorrect = answer == question.Result; + return 1; + } + return 0; + } +} diff --git a/MathGame.vitorvelozo/controllers/QuestionGeneration.cs b/MathGame.vitorvelozo/controllers/QuestionGeneration.cs new file mode 100644 index 00000000..609c01cb --- /dev/null +++ b/MathGame.vitorvelozo/controllers/QuestionGeneration.cs @@ -0,0 +1,78 @@ +using CodeReviews.Console.MathGame.Models; + +namespace CodeReviews.Console.MathGame.Controllers; + +public class QuestionGenerator +{ + public int FirstNumber { get; init; } + public int SecondNumber { get; set; } + public GameDifficulty GameDifficulty {get; set;} + public MathOperation Operation { get; init; } + private int GeneratedNumberCeiling + { + get + { + if (GameDifficulty == GameDifficulty.Easy) + { + return 11; + } + else if (GameDifficulty == GameDifficulty.Normal) + { + return 51; + } + return 101; + } + } + + private static readonly Array OperationsList = Enum.GetValues(typeof(MathOperation)); + + public QuestionGenerator(GameDifficulty difficulty) + { + int randomIndex = Random.Shared.Next(OperationsList.Length); + Operation = (MathOperation)OperationsList.GetValue(randomIndex)!; + + GameDifficulty = difficulty; + FirstNumber = Random.Shared.Next(1, GeneratedNumberCeiling); + SecondNumber = GenerateSecondNumber(); + } + + public QuestionGenerator(GameDifficulty difficulty, MathOperation operation) + { + Operation = operation; + + GameDifficulty = difficulty; + FirstNumber = Random.Shared.Next(1, GeneratedNumberCeiling); + SecondNumber = GenerateSecondNumber(); + } + + private int[] GetIntDivisors() + { + int[] divisors = Enumerable.Range(1, FirstNumber) + .Where(d => FirstNumber % d == 0) + .ToArray(); + return divisors; + } + + private int GenerateSecondNumber() + { + switch (Operation) + { + case MathOperation.Subtraction: + SecondNumber = Random.Shared.Next(1, FirstNumber); + break; + case MathOperation.Division: + int[] divisors = GetIntDivisors(); + SecondNumber = divisors[Random.Shared.Next(divisors.Length)]; + break; + default: + SecondNumber = Random.Shared.Next(1, GeneratedNumberCeiling); + break; + } + return SecondNumber; + } + + public Question GenerateQuestion() + { + return new Question(FirstNumber, SecondNumber, Operation); + } +} diff --git a/MathGame.vitorvelozo/models/MathGame.cs b/MathGame.vitorvelozo/models/MathGame.cs new file mode 100644 index 00000000..c02ddfc4 --- /dev/null +++ b/MathGame.vitorvelozo/models/MathGame.cs @@ -0,0 +1,24 @@ +namespace CodeReviews.Console.MathGame.Models; + +public enum GameDifficulty : int +{ + Easy = 0, + Normal = 1, + Hard = 2, +} +public class Game(GameDifficulty difficulty = GameDifficulty.Normal, int length = 5, bool randomMode = true) +{ + public List Questions { get; set; } = []; + public int PlayerScore { get; set; } = 0; + public int GameLength { get; } = length; + public GameDifficulty Difficulty { get; } = difficulty; + public bool IsRandomMode { get; } = randomMode; + public TimeSpan Duration + { + get + { + long totalTicks = Questions.Sum(question => question.Duration.Ticks); + return TimeSpan.FromTicks(totalTicks); + } + } +} diff --git a/MathGame.vitorvelozo/models/MathQuestion.cs b/MathGame.vitorvelozo/models/MathQuestion.cs new file mode 100644 index 00000000..6ce5ad47 --- /dev/null +++ b/MathGame.vitorvelozo/models/MathQuestion.cs @@ -0,0 +1,36 @@ +namespace CodeReviews.Console.MathGame.Models; + +public enum MathOperation : ushort +{ + Multiplication = '*', + Division = '/', + Addition = '+', + Subtraction = '-', +} + +public class Question +{ + public int FirstNumber { get; } + public int SecondNumber { get; } + public MathOperation Operation { get; } + public int Result { get; set; } + public string? ProvidedAnswer { get; set; } + public bool IsAnswerCorrect { get; set; } = false; + public TimeOnly StartTime {get; set; } + public TimeOnly FinishTime {get; set; } + public TimeSpan Duration + { + get + { + return FinishTime - StartTime; + } + } + + + public Question(int firstNumber, int secondNumber, MathOperation operation) + { + FirstNumber = firstNumber; + SecondNumber = secondNumber; + Operation = operation; + } +} diff --git a/MathGame.vitorvelozo/models/MathSet.cs b/MathGame.vitorvelozo/models/MathSet.cs new file mode 100644 index 00000000..b6f6cbfd --- /dev/null +++ b/MathGame.vitorvelozo/models/MathSet.cs @@ -0,0 +1,36 @@ +namespace CodeReviews.Console.MathGame.Models; + +public class MathSet +{ + public List Games { get; set; } = []; + + public int totalPoints + { + get + { + return Games.Sum(game => game.PlayerScore); + } + } + + public int totalQuestions + { + get + { + return Games.Sum(game => game.GameLength); + } + } + + public TimeSpan Duration + { + get + { + long totalTicks = Games.Sum(game => game.Duration.Ticks); + return TimeSpan.FromTicks(totalTicks); + } + } + + public void AddGame(Game game) + { + Games.Add(game); + } +} diff --git a/MathGame.vitorvelozo/views/GameScreen.cs b/MathGame.vitorvelozo/views/GameScreen.cs new file mode 100644 index 00000000..0a619894 --- /dev/null +++ b/MathGame.vitorvelozo/views/GameScreen.cs @@ -0,0 +1,43 @@ +using CodeReviews.Console.MathGame.Models; +using Spectre.Console; + +namespace CodeReviews.Console.MathGame.Views; + +public static class GameMenu +{ + private static MenuChoices GetOperation() + { + return AnsiConsole.Prompt( + new SelectionPrompt() + .Title("Select an operation") + .AddChoices(Enum.GetValues()) + ); + } + + public static bool Display(MathSet mathSet) + { + bool keepPlaying = false; + MenuChoices operation; + do + { + operation = GetOperation(); + switch (operation) + { + case MenuChoices.NewGame: + keepPlaying = true; + break; + case MenuChoices.ShowHistory: + ResultScreen.Display(mathSet); + break; + } + } while (operation == MenuChoices.ShowHistory); + return keepPlaying; + } + + private enum MenuChoices + { + NewGame, + ShowHistory, + ExitProgram, + } +} \ No newline at end of file diff --git a/MathGame.vitorvelozo/views/QuestionScreen.cs b/MathGame.vitorvelozo/views/QuestionScreen.cs new file mode 100644 index 00000000..9d071d26 --- /dev/null +++ b/MathGame.vitorvelozo/views/QuestionScreen.cs @@ -0,0 +1,33 @@ + +using CodeReviews.Console.MathGame.Models; +using Spectre.Console; + +namespace CodeReviews.Console.MathGame.Views; + +public static class QuestionMenu +{ + public static MathOperation GetOperation() + { + return AnsiConsole.Prompt( + new SelectionPrompt() + .Title("Select an operation") + .AddChoices(Enum.GetValues()) + ); + } + + public static string? GetQuestionAnswer(Question question) + { + AnsiConsole.Markup($"{question.FirstNumber} {GetOperationSymbol(question.Operation)} {question.SecondNumber} = "); + return System.Console.ReadLine(); + } + + private static char GetOperationSymbol(MathOperation operation) => + operation switch + { + MathOperation.Addition => '+', + MathOperation.Subtraction => '-', + MathOperation.Multiplication => '*', + MathOperation.Division => '/', + _ => '?' + }; +} diff --git a/MathGame.vitorvelozo/views/ResultScreen.cs b/MathGame.vitorvelozo/views/ResultScreen.cs new file mode 100644 index 00000000..d9b0997d --- /dev/null +++ b/MathGame.vitorvelozo/views/ResultScreen.cs @@ -0,0 +1,38 @@ +using CodeReviews.Console.MathGame.Models; +using Spectre.Console; + +namespace CodeReviews.Console.MathGame.Views; + +public static class ResultScreen +{ + public static void Display(MathSet mathSet) + { + System.Console.Clear(); + for (int i = 0; i < mathSet.Games.Count; i++) + { + Game game = mathSet.Games[i]; + Table table = new Table() + .Border(TableBorder.Rounded) + .Title($"Game {i + 1} ({Markup.Escape(game.Difficulty.ToString())}) | Score: {game.PlayerScore}/{game.GameLength}") + .AddColumn("Question") + .AddColumn("Answer") + .AddColumn("Result"); + + foreach (Question question in game.Questions) + { + string result = question.IsAnswerCorrect ? "[green]✔ Correct[/]" : "[red]✘ Incorrect[/]"; + table.AddRow( + Markup.Escape(question.Result.ToString()), + Markup.Escape(question.ProvidedAnswer.ToString()), + result); + } + + AnsiConsole.Write(table); + } + + string durationFormatted = mathSet.Duration.ToString(@"mm'm 'ss's'"); + AnsiConsole.Write(new Panel($"[bold]{mathSet.totalPoints}/{mathSet.totalQuestions}[/] in {durationFormatted}") + .Header("SET RESULT", Justify.Center) + .Border(BoxBorder.Double)); + } +} diff --git a/MathGame.vitorvelozo/views/StartScreen.cs b/MathGame.vitorvelozo/views/StartScreen.cs new file mode 100644 index 00000000..f24dc7e8 --- /dev/null +++ b/MathGame.vitorvelozo/views/StartScreen.cs @@ -0,0 +1,28 @@ +using CodeReviews.Console.MathGame.Models; +using Spectre.Console; + +namespace CodeReviews.Console.MathGame.Views; + +public static class MainMenu +{ + public static void DisplayWelcome() + { + AnsiConsole.MarkupLine("Hello! Welcome to the [cyan]math game[/]!"); + } + + public static GameDifficulty GetDifficulty() + { + return AnsiConsole.Prompt( + new SelectionPrompt() + .Title("Select a [red]difficulty[/]") + .AddChoices(Enum.GetValues()) + ); + } + + public static bool GetRandomMode() + { + bool randomMode = AnsiConsole.Confirm("Do you prefer to [red]manually select[/] operations?"); + System.Console.Clear(); + return !randomMode; + } +}