diff --git a/djimon44.MathGame/djimon44.MathGame.slnx b/djimon44.MathGame/djimon44.MathGame.slnx new file mode 100644 index 00000000..3911293f --- /dev/null +++ b/djimon44.MathGame/djimon44.MathGame.slnx @@ -0,0 +1,3 @@ + + + diff --git a/djimon44.MathGame/djimon44.MathGame/Enums/Difficulty.cs b/djimon44.MathGame/djimon44.MathGame/Enums/Difficulty.cs new file mode 100644 index 00000000..6ccfe2d5 --- /dev/null +++ b/djimon44.MathGame/djimon44.MathGame/Enums/Difficulty.cs @@ -0,0 +1,8 @@ +namespace MathGame.Enums; + +public enum Difficulty +{ + Easy, + Medium, + Hard +} diff --git a/djimon44.MathGame/djimon44.MathGame/Enums/GameType.cs b/djimon44.MathGame/djimon44.MathGame/Enums/GameType.cs new file mode 100644 index 00000000..9bd04ec2 --- /dev/null +++ b/djimon44.MathGame/djimon44.MathGame/Enums/GameType.cs @@ -0,0 +1,10 @@ +namespace MathGame.Enums; + +public enum GameType +{ + Addition, + Subtraction, + Multiplication, + Division, + Random +} diff --git a/djimon44.MathGame/djimon44.MathGame/Interfaces/IGameEngine.cs b/djimon44.MathGame/djimon44.MathGame/Interfaces/IGameEngine.cs new file mode 100644 index 00000000..0bfc6627 --- /dev/null +++ b/djimon44.MathGame/djimon44.MathGame/Interfaces/IGameEngine.cs @@ -0,0 +1,9 @@ +using MathGame.Enums; +using MathGame.Models; + +namespace MathGame.Interfaces; + +public interface IGameEngine +{ + GameSession Play(GameType game, Difficulty difficulty); +} diff --git a/djimon44.MathGame/djimon44.MathGame/Interfaces/IQuestionGenerator.cs b/djimon44.MathGame/djimon44.MathGame/Interfaces/IQuestionGenerator.cs new file mode 100644 index 00000000..f3210336 --- /dev/null +++ b/djimon44.MathGame/djimon44.MathGame/Interfaces/IQuestionGenerator.cs @@ -0,0 +1,9 @@ +using MathGame.Enums; +using MathGame.Models; + +namespace MathGame.Interfaces; + +public interface IQuestionGenerator +{ + MathQuestion Generate(GameType game, DifficultySettings difficulty); +} diff --git a/djimon44.MathGame/djimon44.MathGame/Models/DifficultySettings.cs b/djimon44.MathGame/djimon44.MathGame/Models/DifficultySettings.cs new file mode 100644 index 00000000..36c0a224 --- /dev/null +++ b/djimon44.MathGame/djimon44.MathGame/Models/DifficultySettings.cs @@ -0,0 +1,13 @@ +namespace MathGame.Models; + +public class DifficultySettings +{ + public int MaxValue { get; } + public int TimeLimitSeconds { get; } + + public DifficultySettings(int maxValue, int timeLimitSeconds) + { + MaxValue = maxValue; + TimeLimitSeconds = timeLimitSeconds; + } +} diff --git a/djimon44.MathGame/djimon44.MathGame/Models/GameSession.cs b/djimon44.MathGame/djimon44.MathGame/Models/GameSession.cs new file mode 100644 index 00000000..4ae1ea6b --- /dev/null +++ b/djimon44.MathGame/djimon44.MathGame/Models/GameSession.cs @@ -0,0 +1,25 @@ +using MathGame.Enums; + +namespace MathGame.Models; + +public class GameSession +{ + // Fields: + public DateTime PlayedOn { get; } + public GameType Game { get; } + public int Score { get; } + public int TotalQuestions { get; } + public TimeSpan Duration { get; } + public Difficulty Difficulty { get; } + + // Constructor + public GameSession(GameType game, int score, int totalQuestions, TimeSpan duration, Difficulty difficulty) + { + PlayedOn = DateTime.Now; + Game = game; + Score = score; + TotalQuestions = totalQuestions; + Duration = duration; + Difficulty = difficulty; + } +} diff --git a/djimon44.MathGame/djimon44.MathGame/Models/MathQuestion.cs b/djimon44.MathGame/djimon44.MathGame/Models/MathQuestion.cs new file mode 100644 index 00000000..1f2ce21a --- /dev/null +++ b/djimon44.MathGame/djimon44.MathGame/Models/MathQuestion.cs @@ -0,0 +1,54 @@ +using MathGame.Enums; + +namespace MathGame.Models; + +public class MathQuestion +{ + // Fields: + public int OperandA { get; } + public int OperandB { get; } + public GameType Game { get; } + public int CorrectAnswer { get; } + public int? PlayerAnswer { get; private set; } // ? means it can be null; private set means only changable within the class + + public bool IsAnswered + { + get { return PlayerAnswer.HasValue; } + } + + public bool IsCorrect + { + get { return IsAnswered && (PlayerAnswer == CorrectAnswer); } + } + + // Constructor: + public MathQuestion(int operandA, int operandB, GameType game) + { + OperandA = operandA; + OperandB = operandB; + Game = game; + CorrectAnswer = ComputeAnswer(); + } + + // Methods + public void RecordAnswer(int answer) + { + this.PlayerAnswer = answer; + } + + private int ComputeAnswer() { + switch (Game) { + case GameType.Addition: + return OperandA + OperandB; + case GameType.Subtraction: + return OperandA - OperandB; + case GameType.Multiplication: + return OperandA * OperandB; + case GameType.Division: + return OperandA / OperandB; + + default: + throw new InvalidOperationException("Unknown Operation."); + } + } +} diff --git a/djimon44.MathGame/djimon44.MathGame/Program.cs b/djimon44.MathGame/djimon44.MathGame/Program.cs new file mode 100644 index 00000000..009dfedb --- /dev/null +++ b/djimon44.MathGame/djimon44.MathGame/Program.cs @@ -0,0 +1,80 @@ +using MathGame.Enums; +using MathGame.Models; +using MathGame.Services; +using MathGame.UI; + +var ui = new ConsoleUI(); +var generator = new QuestionGenerator(); +var history = new GameHistory(); +var engine = new GameEngine(generator, ui); + +while (true) +{ + string choiceGame = ui.ShowMainMenu(); + + if (choiceGame == "0") break; + + if (choiceGame == "5") + { + ui.DisplayHistory(history.GetAll()); + continue; + } + + GameType game; + + switch (choiceGame) + { + case "1": + game = GameType.Addition; + break; + case "2": + game = GameType.Subtraction; + break; + case "3": + game = GameType.Multiplication; + break; + case "4": + game = GameType.Division; + break; + case "r": + game = GameType.Random; + break; + default: + // Use a "sentinel" value to indicate something went wrong + game = (GameType)(-1); + break; + } + + if ((int)game == -1) + { + Console.WriteLine("Invalid choice. Press any key..."); + Console.ReadKey(); + continue; + } + + string choiceDifficulty = ui.ShowDifficultyMenu(); + Difficulty difficulty; + + switch (choiceDifficulty) + { + case "1": + difficulty = Difficulty.Easy; + break; + case "2": + difficulty = Difficulty.Medium; + break; + case "3": + difficulty= Difficulty.Hard; + break; + + default: + Console.WriteLine("\nInvalid choice. Press any key to try again..."); + Console.ReadKey(); + continue; + } + + GameSession session = engine.Play(game, difficulty); + history.Add(session); +} + +Console.WriteLine("Thanks for playing!"); \ No newline at end of file diff --git a/djimon44.MathGame/djimon44.MathGame/Services/GameEngine.cs b/djimon44.MathGame/djimon44.MathGame/Services/GameEngine.cs new file mode 100644 index 00000000..10bb16c2 --- /dev/null +++ b/djimon44.MathGame/djimon44.MathGame/Services/GameEngine.cs @@ -0,0 +1,83 @@ +using System.Diagnostics; +using MathGame.Enums; +using MathGame.Interfaces; +using MathGame.Models; +using MathGame.UI; + +namespace MathGame.Services; + +public class GameEngine : IGameEngine +{ + private const int TotalQuestions = 5; + + private readonly QuestionGenerator _generator; + private readonly ConsoleUI _ui; + + public GameEngine(QuestionGenerator generator, ConsoleUI ui) + { + _generator = generator; + _ui = ui; + } + + public GameSession Play(GameType game, Difficulty difficulty) + { + int score = 0; + DifficultySettings settings = GetDifficultySettings(difficulty); + Stopwatch totalSessionTime = Stopwatch.StartNew(); + + for (int i = 0; i < TotalQuestions; i++) + { + MathQuestion question = _generator.Generate(game, settings); + + _ui.DisplayQuestion(question, i + 1, TotalQuestions, settings.TimeLimitSeconds); + + Stopwatch questionTimer = Stopwatch.StartNew(); + int playerAnswer = _ui.GetPlayerAnswer(); + questionTimer.Stop(); + + bool outOfTime = questionTimer.Elapsed.TotalSeconds > settings.TimeLimitSeconds; + + if (outOfTime) + { + _ui.DisplayFeedback(correct: false, question.CorrectAnswer); + question.RecordAnswer(int.MinValue); // flag as false answer + } + else + { + question.RecordAnswer(playerAnswer); + + if (question.IsCorrect) + { + score++; + _ui.DisplayFeedback(correct: true); + } + else + { + _ui.DisplayFeedback(correct: false, question.CorrectAnswer); + } + } + } + + totalSessionTime.Stop(); + + var session = new GameSession(game, score, TotalQuestions, totalSessionTime.Elapsed, difficulty); + _ui.DisplaySummary(session); + return session; + } + + private static DifficultySettings GetDifficultySettings(Difficulty difficulty) + { + switch (difficulty) + { + case Difficulty.Easy: + return new DifficultySettings(10, 60); + case Difficulty.Medium: + return new DifficultySettings(20, 90); + case Difficulty.Hard: + return new DifficultySettings(35, 90); + + default: + return new DifficultySettings(10, 60); + } + } +} diff --git a/djimon44.MathGame/djimon44.MathGame/Services/GameHistory.cs b/djimon44.MathGame/djimon44.MathGame/Services/GameHistory.cs new file mode 100644 index 00000000..1970a7d7 --- /dev/null +++ b/djimon44.MathGame/djimon44.MathGame/Services/GameHistory.cs @@ -0,0 +1,18 @@ +using MathGame.Models; + +namespace MathGame.Services; + +public class GameHistory +{ + private readonly List _sessions = new(); + + public void Add(GameSession session) + { + _sessions.Add(session); + } + + public IReadOnlyList GetAll() + { + return _sessions.AsReadOnly(); + } +} diff --git a/djimon44.MathGame/djimon44.MathGame/Services/QuestionGenerator.cs b/djimon44.MathGame/djimon44.MathGame/Services/QuestionGenerator.cs new file mode 100644 index 00000000..d6b9f15c --- /dev/null +++ b/djimon44.MathGame/djimon44.MathGame/Services/QuestionGenerator.cs @@ -0,0 +1,52 @@ +using MathGame.Enums; +using MathGame.Interfaces; +using MathGame.Models; + +namespace MathGame.Services; + +public class QuestionGenerator : IQuestionGenerator +{ + private readonly Random _random = new(); + + public MathQuestion Generate(GameType game, DifficultySettings difficulty) + { + if (game == GameType.Division) + return GenerateDivision(difficulty); + else if (game == GameType.Multiplication) + return GenerateMultiplication(difficulty); + else if (game == GameType.Random) + return GenerateRandom(difficulty); + else + return GenerateStandart(game, difficulty); + } + + private MathQuestion GenerateStandart(GameType game, DifficultySettings difficulty) + { + int a = _random.Next(1, difficulty.MaxValue + 1); + int b = _random.Next(1, difficulty.MaxValue + 1); + return new MathQuestion(a, b, game); + } + + private MathQuestion GenerateDivision(DifficultySettings difficulty) + { + int divisor = _random.Next(1, difficulty.MaxValue + 1); + int maxQuotient = difficulty.MaxValue / divisor; + int quotient = _random.Next(1, maxQuotient + 1); + int dividend = divisor * quotient; + return new MathQuestion(dividend, divisor, GameType.Division); + } + + private MathQuestion GenerateMultiplication(DifficultySettings difficulty) + { + int multiplicand = _random.Next(1, difficulty.MaxValue + 1); + int multiplier = _random.Next(1, difficulty.MaxValue + 1); + return new MathQuestion(multiplicand, multiplier, GameType.Multiplication); + } + + private MathQuestion GenerateRandom(DifficultySettings difficulty) + { + int randomEnum = _random.Next(0, 4); + GameType randomGame = (GameType)randomEnum; + return Generate(randomGame, difficulty); + } +} diff --git a/djimon44.MathGame/djimon44.MathGame/UI/ConsoleUI.cs b/djimon44.MathGame/djimon44.MathGame/UI/ConsoleUI.cs new file mode 100644 index 00000000..2c08c24f --- /dev/null +++ b/djimon44.MathGame/djimon44.MathGame/UI/ConsoleUI.cs @@ -0,0 +1,121 @@ +using MathGame.Enums; +using MathGame.Models; + +namespace MathGame.UI; + +public class ConsoleUI +{ + public string ShowMainMenu() + { + Console.Clear(); + Console.WriteLine("=== MATH GAME ==="); + Console.WriteLine("R. Random"); + Console.WriteLine("1. Addition"); + Console.WriteLine("2. Subtraction"); + Console.WriteLine("3. Multiplication"); + Console.WriteLine("4. Division"); + Console.WriteLine("5. View History"); + Console.WriteLine("0. Exit"); + Console.Write("\nChoose an option: "); + + return Console.ReadLine()?.Trim().ToLower() ?? string.Empty; + } + + public string ShowDifficultyMenu() + { + Console.Clear(); + Console.WriteLine("=== PICK DIFFICULTY ==="); + Console.WriteLine("1. Easy"); + Console.WriteLine("2. Medium"); + Console.WriteLine("3. Hard"); + + return Console.ReadLine()?.Trim().ToLower() ?? string.Empty; + } + + public void DisplayQuestion(MathQuestion question, int number, int total, int timeLimit) + { + Console.Clear(); + Console.WriteLine($"=== {question.Game} Game ==="); + Console.WriteLine($"[You have {timeLimit} seconds!]"); + Console.WriteLine($"\nQuestion [{number}/{total}]"); + Console.Write($"{question.OperandA} {OperationSymbol(question.Game)} {question.OperandB} = "); + } + + public int GetPlayerAnswer() + { + while (true) + { + if (int.TryParse(Console.ReadLine(), out int answer)) + { + return answer; + } + + Console.Write("Please enter a whole number: "); + } + } + + public void DisplayFeedback(bool correct, int correctAnswer = 0) + { + if (correct) + { + Console.WriteLine("Correct!"); + Console.WriteLine("Press any key to continue..."); + Console.ReadKey(); + } + else + { + Console.WriteLine($"Wrong/Out of time. The answer is: [{correctAnswer}]"); + Console.WriteLine("Press any key to continue..."); + Console.ReadKey(); + } + } + + public void DisplaySummary(GameSession session) + { + Console.Clear(); + Console.WriteLine($"\nGame over! You scored {session.Score}/{session.TotalQuestions}."); + Console.WriteLine($"Completion time: {session.Duration}"); + Console.WriteLine("Press any key to return to the menu..."); + Console.ReadKey(); + } + + public void DisplayHistory(IReadOnlyList history) + { + Console.Clear(); + Console.WriteLine("=== GAME HISTORY ===\n"); + + if (!history.Any()) + { + Console.WriteLine("No games played yet."); + } + else + { + foreach (GameSession session in history) + { + Console.WriteLine( + $"{session.PlayedOn:HH:mm:ss} | {session.Duration} | {session.Game,-15} Game | {session.Difficulty} | {session.Score}/{session.TotalQuestions}"); + } + + Console.WriteLine("\nPress any key to return..."); + Console.ReadKey(); + } + } + + private static string OperationSymbol(GameType op) + { + switch (op) + { + case GameType.Addition: + return "+"; + case GameType.Subtraction: + return "-"; + case GameType.Multiplication: + return "*"; + case GameType.Division: + return "/"; + + default: + return "?"; + } + } +} diff --git a/djimon44.MathGame/djimon44.MathGame/djimon44.MathGame.csproj b/djimon44.MathGame/djimon44.MathGame/djimon44.MathGame.csproj new file mode 100644 index 00000000..ed9781c2 --- /dev/null +++ b/djimon44.MathGame/djimon44.MathGame/djimon44.MathGame.csproj @@ -0,0 +1,10 @@ + + + + Exe + net10.0 + enable + enable + + +