diff --git a/MathGame.ivangar/MathGame.ivangar.slnx b/MathGame.ivangar/MathGame.ivangar.slnx new file mode 100644 index 00000000..51a2997e --- /dev/null +++ b/MathGame.ivangar/MathGame.ivangar.slnx @@ -0,0 +1,3 @@ + + + diff --git a/MathGame.ivangar/MathGame.ivangar/Enums/MainMenuOptions.cs b/MathGame.ivangar/MathGame.ivangar/Enums/MainMenuOptions.cs new file mode 100644 index 00000000..91e4df18 --- /dev/null +++ b/MathGame.ivangar/MathGame.ivangar/Enums/MainMenuOptions.cs @@ -0,0 +1,9 @@ +namespace MathGame.ivangar.Enums +{ + public enum MainMenuItems + { + Play = 1, + Exit = 2, + History = 3 + } +} diff --git a/MathGame.ivangar/MathGame.ivangar/Game.cs b/MathGame.ivangar/MathGame.ivangar/Game.cs new file mode 100644 index 00000000..be207333 --- /dev/null +++ b/MathGame.ivangar/MathGame.ivangar/Game.cs @@ -0,0 +1,154 @@ +using MathGame.ivangar.Validators; + +namespace MathGame.ivangar +{ + public class Game : IGame + { + private static readonly char[] _operators = ['+', '-', '*', '/']; + + private readonly int _maxNumberOfQuestions; + private readonly Random _random; + + // State fields + private int _currentQuestionNumber; + private int _score; + private int _result; + private int _op1, _op2; + + public int UserAnswer { get; set; } + + private readonly List _gameHistory = new(); + + public Game() + { + _random = new Random(); + _maxNumberOfQuestions = _random.Next(5, 11); + _currentQuestionNumber = 1; + _score = 0; + } + + public void Play() + { + bool continueGame = true; + char operation = Menu.StartGamePrompt(_maxNumberOfQuestions, _currentQuestionNumber); + GameValidator validator = new GameValidator(); + + while (continueGame) + { + while (!Array.Exists(_operators, o => o == operation)) + { + operation = Menu.PrintGameOptions(invalid: true); + } + + PerformMathOperation(operation); + validator.ValidateAnswer(_result, UserAnswer, ref _score); + + _currentQuestionNumber++; + + if (_currentQuestionNumber > _maxNumberOfQuestions) + break; + + continueGame = validator.ValidateContinueGame(Menu.ContinueGamePrompt()); + + if (continueGame) + operation = Menu.PrintGameOptions(false, _currentQuestionNumber); + } + + FinishGame(); + } + + public void PerformMathOperation(char operation) + { + _op1 = operation == '/' ? _random.Next(1, 101) : _random.Next(0, 101); + _op2 = operation == '/' ? GetDivisor() : _random.Next(0, 101); + CalculateResult(operation); + + Console.Write($"\n\nWhat is the result of:\n{_op1} {operation} {_op2} = "); + string? answer = Console.ReadLine(); + + int parsedAnswer; + while (!int.TryParse(answer, out parsedAnswer)) + { + Console.WriteLine("Invalid input. Please enter a valid number: "); + answer = Console.ReadLine(); + } + + UserAnswer = parsedAnswer; + + _gameHistory.Add(new MathOperation + { + OperandA = _op1, + OperandB = _op2, + Operation = operation, + UserAnswer = UserAnswer, + ScoreMark = UserAnswer == _result ? "Correct" : "Incorrect" + } + ); + } + + public void PrintGameHistory() + { + Console.WriteLine("\nGame History:\n"); + + foreach (var (index, operation) in _gameHistory.Select((o, i) => (i, o))) + { + Console.WriteLine($"{index + 1}. {operation}"); + } + + Console.WriteLine("\n\n"); + } + + public void PrintScore() + { + var numberOfQuestionsAnswered = _currentQuestionNumber - 1; + decimal finalScore = Math.Round((decimal)_score * 100 / numberOfQuestionsAnswered, MidpointRounding.AwayFromZero); + + Console.WriteLine($"You answered correctly {_score}/{numberOfQuestionsAnswered} questions."); + Console.WriteLine($"Your score is: {finalScore}%"); + } + + public void FinishGame() + { + Console.WriteLine("\n\nGame Over!\n"); + PrintGameHistory(); + PrintScore(); + } + + #region Private Methods + private void CalculateResult(char operation) + { + _result = operation switch + { + '+' => _op1 + _op2, + '-' => _op1 - _op2, + '*' => _op1 * _op2, + '/' => _op1 / _op2, + _ => throw new InvalidOperationException("Invalid operation") + }; + } + + /*Get a list of potential divisors (without remainders) and return randomly any divisor */ + private int GetDivisor() + { + if (IsPrimeNumber(_op1)) + return 1; + + var divisors = Enumerable + .Range(1, _op1) + .Where(x => _op1 % x == 0) + .ToList(); + + return divisors[_random.Next(0, divisors.Count)]; + } + + private static bool IsPrimeNumber(int number) + { + var primes = Enumerable.Range(2, 100) + .Where(n => !Enumerable.Range(2, (int)Math.Sqrt(n) - 1).Any(d => n % d == 0)) + .ToList(); + + return primes.Contains(number); + } + #endregion + } +} diff --git a/MathGame.ivangar/MathGame.ivangar/GameCenter.cs b/MathGame.ivangar/MathGame.ivangar/GameCenter.cs new file mode 100644 index 00000000..0e412e0f --- /dev/null +++ b/MathGame.ivangar/MathGame.ivangar/GameCenter.cs @@ -0,0 +1,80 @@ +using MathGame.ivangar.Enums; +using MathGame.ivangar.Validators; + +namespace MathGame.ivangar +{ + public class GameCenter + { + private readonly List _games = []; + private bool _exitGame = false; + + public void Start() + { + string? menuOption = Menu.GameIntro(); + + while (true) + { + while (!MenuValidator.ValidateMainOptions(menuOption)) + { + menuOption = Console.ReadLine(); + } + + Enum.TryParse(menuOption!.Trim(), ignoreCase: true, out MainMenuItems option); + + switch (option) + { + case MainMenuItems.Play: + PlayGame(); + break; + case MainMenuItems.History: + MathGameHistory(); + break; + case MainMenuItems.Exit: + _exitGame = true; + Exit(); + break; + } + + if (_exitGame) + break; + + Menu.PrintMenu(); + menuOption = Console.ReadLine(); + } + } + + public void PlayGame() + { + var mathGame = new Game(); + mathGame.Play(); + _games.Add(mathGame); + } + + public void MathGameHistory() + { + if (_games.Count == 0) + Console.WriteLine("\nYou don't have any registered game played.\n"); + + foreach (var (index, game) in _games.Select((g, i) => (i, g))) + { + Console.WriteLine($"\n\t\tGame #{index + 1}\n"); + game.PrintGameHistory(); + game.PrintScore(); + } + } + + public void Exit() + { + if (_games.Count != 0) + { + Console.WriteLine("\nThank you for playing the Math Game. Here is your Math Game history\n\n"); + MathGameHistory(); + } + + else + Console.WriteLine("\nThank you for playing the Math Game."); + + Console.WriteLine("\nHave a nice day!"); + } + } +} diff --git a/MathGame.ivangar/MathGame.ivangar/IGame.cs b/MathGame.ivangar/MathGame.ivangar/IGame.cs new file mode 100644 index 00000000..8c6019df --- /dev/null +++ b/MathGame.ivangar/MathGame.ivangar/IGame.cs @@ -0,0 +1,10 @@ +namespace MathGame.ivangar +{ + public interface IGame + { + void Play(); + void FinishGame(); + void PrintGameHistory(); + void PrintScore(); + } +} diff --git a/MathGame.ivangar/MathGame.ivangar/MathGame.ivangar.csproj b/MathGame.ivangar/MathGame.ivangar/MathGame.ivangar.csproj new file mode 100644 index 00000000..ed9781c2 --- /dev/null +++ b/MathGame.ivangar/MathGame.ivangar/MathGame.ivangar.csproj @@ -0,0 +1,10 @@ + + + + Exe + net10.0 + enable + enable + + + diff --git a/MathGame.ivangar/MathGame.ivangar/MathOperation.cs b/MathGame.ivangar/MathGame.ivangar/MathOperation.cs new file mode 100644 index 00000000..4a35c383 --- /dev/null +++ b/MathGame.ivangar/MathGame.ivangar/MathOperation.cs @@ -0,0 +1,17 @@ +namespace MathGame.ivangar +{ + public record MathOperation + { + public int OperandA { get; init; } + public int OperandB { get; init; } + public char Operation { get; init; } + public int UserAnswer { get; init; } + public string ScoreMark { get; init; } = string.Empty; + + public override string ToString() + { + var leftHandSide = $"{OperandA} {Operation} {OperandB} = {UserAnswer}"; + return $"{leftHandSide,-20} {ScoreMark,-5}"; + } + } +} diff --git a/MathGame.ivangar/MathGame.ivangar/Menu.cs b/MathGame.ivangar/MathGame.ivangar/Menu.cs new file mode 100644 index 00000000..d362ce5d --- /dev/null +++ b/MathGame.ivangar/MathGame.ivangar/Menu.cs @@ -0,0 +1,64 @@ +using MathGame.ivangar.Enums; + +namespace MathGame.ivangar +{ + public static class Menu + { + public static string? GameIntro() + { + Console.WriteLine("\nWelcome to the Math Game!"); + Console.WriteLine("We are going to test your math skills!"); + PrintMenu(); + return Console.ReadLine(); + } + + public static void PrintMenu(bool invalid = false) + { + if (invalid) + Console.WriteLine("\nInvalid input."); + + Console.WriteLine("\nPlease choose any of the following options (you have to type the word, i.e. 'play'):\n"); + PrintMenuOptions(); + } + + public static void PrintMenuOptions() + { + foreach (MainMenuItems option in Enum.GetValues(typeof(MainMenuItems))) + Console.WriteLine($"\t{(int)option}. {option}"); + + Console.WriteLine("\n"); + } + + public static char StartGamePrompt(int maxNumberOfQuestions, int currentQuestionNumber) + { + Console.WriteLine($"\nThis game has {maxNumberOfQuestions} questions."); + return PrintGameOptions(invalid: false, currentQuestionNumber); + } + + public static char PrintGameOptions(bool invalid = false, int currentQuestionNumber = -1) + { + if (invalid) + Console.Write("\nInvalid operation selected! "); + + if (currentQuestionNumber > 0) + Console.WriteLine($"\n\n\tQuestion #{currentQuestionNumber}\n"); + + Console.Write("Choose an operation (type any of the following options: +, -, *, /): "); + ConsoleKeyInfo keyInfo = Console.ReadKey(); + return keyInfo.KeyChar; + } + + public static char ContinueGamePrompt(bool invalid = false) + { + if (invalid) + Console.Write("\nInvalid input. Please enter 'y' or 'n': "); + + else + Console.Write("\nWould you like to continue the game? (y/n): "); + + ConsoleKeyInfo keyInfo = Console.ReadKey(); + char continueInput = char.ToLower(keyInfo.KeyChar); + return continueInput; + } + } +} diff --git a/MathGame.ivangar/MathGame.ivangar/Program.cs b/MathGame.ivangar/MathGame.ivangar/Program.cs new file mode 100644 index 00000000..913acee2 --- /dev/null +++ b/MathGame.ivangar/MathGame.ivangar/Program.cs @@ -0,0 +1,4 @@ +using MathGame.ivangar; + +var gameCenter = new GameCenter(); +gameCenter.Start(); \ No newline at end of file diff --git a/MathGame.ivangar/MathGame.ivangar/Validators/GameValidator.cs b/MathGame.ivangar/MathGame.ivangar/Validators/GameValidator.cs new file mode 100644 index 00000000..bd9d3913 --- /dev/null +++ b/MathGame.ivangar/MathGame.ivangar/Validators/GameValidator.cs @@ -0,0 +1,27 @@ +namespace MathGame.ivangar.Validators +{ + public class GameValidator + { + public void ValidateAnswer(int result, int userAnswer, ref int score) + { + if (result == userAnswer) + { + Console.WriteLine("Correct Answer!"); + score++; + } + + else + Console.WriteLine($"Incorrect Answer! The correct answer is: {result}"); + } + + public bool ValidateContinueGame(char input) + { + while (input != 'y' && input != 'n') + { + input = Menu.ContinueGamePrompt(invalid: true); + } + + return input == 'y'; + } + } +} diff --git a/MathGame.ivangar/MathGame.ivangar/Validators/MenuValidator.cs b/MathGame.ivangar/MathGame.ivangar/Validators/MenuValidator.cs new file mode 100644 index 00000000..95d3f247 --- /dev/null +++ b/MathGame.ivangar/MathGame.ivangar/Validators/MenuValidator.cs @@ -0,0 +1,25 @@ +using MathGame.ivangar.Enums; + +namespace MathGame.ivangar.Validators +{ + public static class MenuValidator + { + public static bool ValidateMainOptions(string? option) + { + if (string.IsNullOrEmpty(option) || string.IsNullOrWhiteSpace(option)) + { + Menu.PrintMenu(true); + return false; + } + + var validOption = Enum.TryParse(option.Trim(), ignoreCase: true, out _); + + if (!validOption) + { + Menu.PrintMenu(true); + } + + return validOption; + } + } +}