diff --git a/MathGame.Cybernexus-code/MathGame.CyberNexus-code/Entities/Game.cs b/MathGame.Cybernexus-code/MathGame.CyberNexus-code/Entities/Game.cs new file mode 100644 index 00000000..59479052 --- /dev/null +++ b/MathGame.Cybernexus-code/MathGame.CyberNexus-code/Entities/Game.cs @@ -0,0 +1,87 @@ +using Spectre.Console; + +namespace Entities; + +public class Game +{ + public int Score {get; private set;} + private readonly IOperationProvider _operation; + private readonly GameSettings _gameSettings; + private readonly IGameTimer _timer; + private readonly GameMode _mode; + private readonly Random _rand; + public Game(IOperationProvider operation, DifficultyLevel difficultyLevel, IGameTimer timer, GameMode gameMode, Random random) + { + _operation = operation; + _mode = gameMode; + _gameSettings = new GameSettings(difficultyLevel); + _timer = timer; + _rand = random; + } + public GameResult Start() + { + int questionCount = 0; + AnsiConsole.MarkupLine($"[green]Game Started![/]"); + + _timer.Start(); + + while(questionCount < _gameSettings.QuestionCount) + { + Console.Clear(); + AnsiConsole.Markup($"================== \nScore: {Score} \n==================\n"); + AskQuestion(_rand); + questionCount++; + } + + var duration = _timer.Stop(); + + Console.Clear(); + AnsiConsole.MarkupLine($"You got {Score}/{_gameSettings.QuestionCount} questions correct! \n" + + "Press any key to return to the Main menu..."); + Console.ReadLine(); + + return new GameResult(Score, _operation.DisplayName , _gameSettings.DifficultyLevel, duration); + } + + + public void AskQuestion(Random rand) + { + + var question = GetQuestion(rand); + + int a = question.FirstNumber; + int b = question.SecondNumber; + string symbol = question.Symbol; + + var answer = AnsiConsole + .Ask("What is the solution to this math problem?" + + $"\n{a} {symbol} {b} ="); + + if(SubmitAnswer(answer, question.Answer)) + { + AnsiConsole.MarkupLine($"That is correct! {a} {symbol} {b} = [green]{question.Answer}[/] \n Press any key to continue..."); + Console.ReadKey(); + } + else + { + AnsiConsole.MarkupLine($"That is incorrect! {a} {symbol} {b} = {question.Answer} \n Press any key to continue..."); + Console.ReadKey(); + } + } + + public MathQuestion GetQuestion(Random rand) + { + return new MathQuestion(_operation.GetOperation(), rand, _gameSettings); + } + + public bool SubmitAnswer(int answer, int calculatedAnswer) + { + bool isCorrect = answer == calculatedAnswer; + if(isCorrect) + { + Score++; + } + return isCorrect; + } +} + diff --git a/MathGame.Cybernexus-code/MathGame.CyberNexus-code/Entities/GameResult.cs b/MathGame.Cybernexus-code/MathGame.CyberNexus-code/Entities/GameResult.cs new file mode 100644 index 00000000..9179caf4 --- /dev/null +++ b/MathGame.Cybernexus-code/MathGame.CyberNexus-code/Entities/GameResult.cs @@ -0,0 +1,14 @@ +public class GameResult +{ + public readonly int _score; + public readonly string _operation; + public readonly DifficultyLevel _difficultyLevel; + public readonly TimeSpan _duration; + public GameResult(int score, string mode, DifficultyLevel difficultyLevel, TimeSpan duration) + { + _score = score; + _operation = mode; + _difficultyLevel = difficultyLevel; + _duration = duration; + } +} \ No newline at end of file diff --git a/MathGame.Cybernexus-code/MathGame.CyberNexus-code/Entities/GameSettings.cs b/MathGame.Cybernexus-code/MathGame.CyberNexus-code/Entities/GameSettings.cs new file mode 100644 index 00000000..601c775e --- /dev/null +++ b/MathGame.Cybernexus-code/MathGame.CyberNexus-code/Entities/GameSettings.cs @@ -0,0 +1,19 @@ +public class GameSettings +{ + public DifficultyLevel DifficultyLevel {get;} + public int MinNumber {get;} = 1; + public int MaxNumber {get;} + public int QuestionCount {get;} + + public GameSettings(DifficultyLevel difficultyLevel) + { + DifficultyLevel = difficultyLevel; + (MaxNumber, QuestionCount) = DifficultyLevel switch + { + DifficultyLevel.Easy => (20,3), + DifficultyLevel.Normal => (50, 5), + DifficultyLevel.Hard => (100, 10), + _=> throw new ArgumentOutOfRangeException() + }; + } +} \ No newline at end of file diff --git a/MathGame.Cybernexus-code/MathGame.CyberNexus-code/Entities/MathQuestion.cs b/MathGame.Cybernexus-code/MathGame.CyberNexus-code/Entities/MathQuestion.cs new file mode 100644 index 00000000..9278b747 --- /dev/null +++ b/MathGame.Cybernexus-code/MathGame.CyberNexus-code/Entities/MathQuestion.cs @@ -0,0 +1,48 @@ +public class MathQuestion +{ + private readonly Operation _operation; + private readonly Random _rand; + public int Answer {get; private set;} + public int FirstNumber {get; private set;} + public int SecondNumber {get; private set;} + public string Symbol {get; private set;} = string.Empty; + public GameSettings Gamesettings {get; private set;} + public MathQuestion(Operation operation, Random rand, GameSettings gameSettings) + { + _operation = operation; + _rand = rand; + Gamesettings = gameSettings; + GenerateNumbers(); + CalculateAnswer(FirstNumber, SecondNumber); + } + + private void CalculateAnswer(int a, int b) + { + (Answer, Symbol) = _operation switch{ + Operation.Addition => (a + b, "+"), + Operation.Subtraction => (a - b, "-"), + Operation.Multiplication => (a * b, "*"), + Operation.Division => (a / b, "/"), + _=> throw new ArgumentOutOfRangeException() + }; + } + + private void GenerateNumbers() + { + FirstNumber = _rand.Next(Gamesettings.MinNumber, Gamesettings.MaxNumber + 1); + SecondNumber = _rand.Next(Gamesettings.MinNumber, Gamesettings.MaxNumber + 1); + + while(_operation == Operation.Division && FirstNumber % SecondNumber != 0) + { + FirstNumber = _rand.Next(Gamesettings.MinNumber, Gamesettings.MaxNumber + 1); + SecondNumber = _rand.Next(Gamesettings.MinNumber, Gamesettings.MaxNumber + 1); + } + + while(_operation == Operation.Subtraction && Gamesettings.DifficultyLevel != DifficultyLevel.Hard && FirstNumber < SecondNumber) + { + FirstNumber = _rand.Next(Gamesettings.MinNumber, Gamesettings.MaxNumber + 1); + } + } + + +} \ No newline at end of file diff --git a/MathGame.Cybernexus-code/MathGame.CyberNexus-code/Enums/DifficultyLevel.cs b/MathGame.Cybernexus-code/MathGame.CyberNexus-code/Enums/DifficultyLevel.cs new file mode 100644 index 00000000..8725ae94 --- /dev/null +++ b/MathGame.Cybernexus-code/MathGame.CyberNexus-code/Enums/DifficultyLevel.cs @@ -0,0 +1,6 @@ +public enum DifficultyLevel +{ + Easy, + Normal, + Hard +} \ No newline at end of file diff --git a/MathGame.Cybernexus-code/MathGame.CyberNexus-code/Enums/GameModes.cs b/MathGame.Cybernexus-code/MathGame.CyberNexus-code/Enums/GameModes.cs new file mode 100644 index 00000000..1871778d --- /dev/null +++ b/MathGame.Cybernexus-code/MathGame.CyberNexus-code/Enums/GameModes.cs @@ -0,0 +1,5 @@ +public enum GameMode +{ + Single, + Random +} \ No newline at end of file diff --git a/MathGame.Cybernexus-code/MathGame.CyberNexus-code/Enums/Operation.cs b/MathGame.Cybernexus-code/MathGame.CyberNexus-code/Enums/Operation.cs new file mode 100644 index 00000000..08a9d7d5 --- /dev/null +++ b/MathGame.Cybernexus-code/MathGame.CyberNexus-code/Enums/Operation.cs @@ -0,0 +1,7 @@ +public enum Operation +{ + Addition, + Subtraction, + Multiplication, + Division +} \ No newline at end of file diff --git a/MathGame.Cybernexus-code/MathGame.CyberNexus-code/Interfaces/IGameTimer.cs b/MathGame.Cybernexus-code/MathGame.CyberNexus-code/Interfaces/IGameTimer.cs new file mode 100644 index 00000000..219b6e20 --- /dev/null +++ b/MathGame.Cybernexus-code/MathGame.CyberNexus-code/Interfaces/IGameTimer.cs @@ -0,0 +1,5 @@ +public interface IGameTimer +{ + void Start(); + TimeSpan Stop(); +} \ No newline at end of file diff --git a/MathGame.Cybernexus-code/MathGame.CyberNexus-code/Interfaces/IOperationProvider.cs b/MathGame.Cybernexus-code/MathGame.CyberNexus-code/Interfaces/IOperationProvider.cs new file mode 100644 index 00000000..78d43913 --- /dev/null +++ b/MathGame.Cybernexus-code/MathGame.CyberNexus-code/Interfaces/IOperationProvider.cs @@ -0,0 +1,5 @@ +public interface IOperationProvider +{ + string DisplayName {get;} + Operation GetOperation(); +} \ No newline at end of file diff --git a/MathGame.Cybernexus-code/MathGame.CyberNexus-code/MathGame.csproj b/MathGame.Cybernexus-code/MathGame.CyberNexus-code/MathGame.csproj new file mode 100644 index 00000000..452a0af1 --- /dev/null +++ b/MathGame.Cybernexus-code/MathGame.CyberNexus-code/MathGame.csproj @@ -0,0 +1,14 @@ + + + + Exe + net10.0 + enable + enable + + + + + + + diff --git a/MathGame.Cybernexus-code/MathGame.CyberNexus-code/Program.cs b/MathGame.Cybernexus-code/MathGame.CyberNexus-code/Program.cs new file mode 100644 index 00000000..795dc070 --- /dev/null +++ b/MathGame.Cybernexus-code/MathGame.CyberNexus-code/Program.cs @@ -0,0 +1,62 @@ +using System.Net.Mail; +using Entities; +using Spectre.Console; + +List gameResults = new(); + +while (true) +{ + + Console.Clear(); + + var choice = AnsiConsole.Prompt( new SelectionPrompt().Title("==================\nMath Game").AddChoices("Start Game","View Scores", "Exit")); + + + if(choice == "Exit") + { + return; + }else if(choice == "View Scores") + { + var table = new Table().AddColumn("Difficulty").AddColumn("Operation").AddColumn("Score").AddColumn("Duration"); + + gameResults.ForEach(x => table.AddRow($"{x._difficultyLevel}",$"{x._operation}",$"{x._score}", $"{x._duration.ToString(@"mm\:ss")}")); + + AnsiConsole.Write(table); + AnsiConsole.MarkupLine("Press any key to continue..."); + Console.ReadKey(); + } + else + { + var rand = new Random(); + var timer = new GameTimer(); + + var mode = AnsiConsole + .Prompt(new SelectionPrompt() + .Title("==================\nMath Game\n==================\nPlease Select a game mode:") + .AddChoices(Enum.GetValues())); + + var difficulty = AnsiConsole + .Prompt(new SelectionPrompt() + .Title("==================\nMath Game\n==================\nPlease Select a difficulty:") + .AddChoices(Enum.GetValues())); + + if(mode == GameMode.Single) + { + var option = AnsiConsole.Prompt( + new SelectionPrompt().Title("==================\nMath Game\n==================\nPlease Select an opperation:").AddChoices(Enum.GetValues())); + + var fixedOperation = new FixedOperationProvider(option); + + var newGame = new Game(fixedOperation, difficulty, timer, mode, rand); + gameResults.Add(newGame.Start()); + } + else + { + var randomOperationProvider = new RandomOperationProvider(rand, Enum.GetValues().ToList()); + var newGame = new Game(randomOperationProvider, difficulty, timer, mode, rand); + gameResults.Add(newGame.Start()); + } + } +} + + diff --git a/MathGame.Cybernexus-code/MathGame.CyberNexus-code/Services/FixedOperationProvider.cs b/MathGame.Cybernexus-code/MathGame.CyberNexus-code/Services/FixedOperationProvider.cs new file mode 100644 index 00000000..2862ef7c --- /dev/null +++ b/MathGame.Cybernexus-code/MathGame.CyberNexus-code/Services/FixedOperationProvider.cs @@ -0,0 +1,10 @@ +public class FixedOperationProvider(Operation operation) : IOperationProvider +{ + public string DisplayName {get;} = operation.ToString(); + private readonly Operation _operation = operation; + + public Operation GetOperation() + { + return _operation; + } +} \ No newline at end of file diff --git a/MathGame.Cybernexus-code/MathGame.CyberNexus-code/Services/GameTimer.cs b/MathGame.Cybernexus-code/MathGame.CyberNexus-code/Services/GameTimer.cs new file mode 100644 index 00000000..d805dafa --- /dev/null +++ b/MathGame.Cybernexus-code/MathGame.CyberNexus-code/Services/GameTimer.cs @@ -0,0 +1,16 @@ +using System.Diagnostics; + +public class GameTimer: IGameTimer +{ + private long _startingTimestamp; + + public void Start() + { + _startingTimestamp = Stopwatch.GetTimestamp(); + } + + public TimeSpan Stop() + { + return Stopwatch.GetElapsedTime(_startingTimestamp); + } +} \ No newline at end of file diff --git a/MathGame.Cybernexus-code/MathGame.CyberNexus-code/Services/RandomOperationProvider.cs b/MathGame.Cybernexus-code/MathGame.CyberNexus-code/Services/RandomOperationProvider.cs new file mode 100644 index 00000000..244d02b4 --- /dev/null +++ b/MathGame.Cybernexus-code/MathGame.CyberNexus-code/Services/RandomOperationProvider.cs @@ -0,0 +1,19 @@ +using System.Buffers; + +public class RandomOperationProvider : IOperationProvider +{ + public string DisplayName {get;} = "Random"; + private readonly Random _rand; + private readonly List _operations; + public RandomOperationProvider(Random rand, List operations) + { + _rand = rand; + _operations = operations; + + } + public Operation GetOperation() + { + int index = _rand.Next(_operations.Count); + return _operations[index]; + } +} \ No newline at end of file diff --git a/MathGame.Cybernexus-code/MathGame.slnx b/MathGame.Cybernexus-code/MathGame.slnx new file mode 100644 index 00000000..fd3ffbaf --- /dev/null +++ b/MathGame.Cybernexus-code/MathGame.slnx @@ -0,0 +1,4 @@ + + + + diff --git a/MathGame.Cybernexus-code/Mathgame.Tests/Entities/MathQuestionTest.cs b/MathGame.Cybernexus-code/Mathgame.Tests/Entities/MathQuestionTest.cs new file mode 100644 index 00000000..c9f98d46 --- /dev/null +++ b/MathGame.Cybernexus-code/Mathgame.Tests/Entities/MathQuestionTest.cs @@ -0,0 +1,43 @@ + +namespace MathGame.Tests.Entities; + +public class MathQuestionTest +{ + private readonly Random _random = new(); + private readonly GameSettings _gameSettings = new GameSettings(DifficultyLevel.Easy); + + [Fact] + public void Addition_ShouldCalculateCorrectAnswer() + { + // Arrange + // Set up what the test needs + // Act + // Perform the behaviour we're testing + var question = new MathQuestion(Operation.Addition, _random, _gameSettings); + + // Assert + // Verify the result + Assert.Equal(question.FirstNumber + question.SecondNumber, question.Answer); + } + + [Fact] + public void Division_ShouldAlwaysProduceInteger() + { + var question = new MathQuestion(Operation.Division, _random, _gameSettings ); + + Assert.Equal(0, question.FirstNumber % question.SecondNumber); + } + + [Theory] + [InlineData(Operation.Addition, "+")] + [InlineData(Operation.Subtraction, "-")] + [InlineData(Operation.Multiplication, "*")] + [InlineData(Operation.Division, "/")] + + public void Operation_ShouldMatchSymbol(Operation operation, string symbol) + { + var question = new MathQuestion(operation, _random, _gameSettings); + + Assert.Equal(symbol, question.Symbol); + } +} \ No newline at end of file diff --git a/MathGame.Cybernexus-code/Mathgame.Tests/MathGame.Tests.csproj b/MathGame.Cybernexus-code/Mathgame.Tests/MathGame.Tests.csproj new file mode 100644 index 00000000..2be7a0d6 --- /dev/null +++ b/MathGame.Cybernexus-code/Mathgame.Tests/MathGame.Tests.csproj @@ -0,0 +1,25 @@ + + + + net10.0 + enable + enable + false + + + + + + + + + + + + + + + + + + \ No newline at end of file