diff --git a/.idea/.gitignore b/.idea/.gitignore
new file mode 100644
index 00000000..0fbab21b
--- /dev/null
+++ b/.idea/.gitignore
@@ -0,0 +1,15 @@
+# Default ignored files
+/shelf/
+/workspace.xml
+# Rider ignored files
+/modules.xml
+/.idea.CodeReviews.Console.MathGame.iml
+/projectSettingsUpdater.xml
+/contentModel.xml
+# Editor-based HTTP Client requests
+/httpRequests/
+# Ignored default folder with query files
+/queries/
+# Datasource local storage ignored files
+/dataSources/
+/dataSources.local.xml
diff --git a/.idea/encodings.xml b/.idea/encodings.xml
new file mode 100644
index 00000000..df87cf95
--- /dev/null
+++ b/.idea/encodings.xml
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/.idea/indexLayout.xml b/.idea/indexLayout.xml
new file mode 100644
index 00000000..7b08163c
--- /dev/null
+++ b/.idea/indexLayout.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 00000000..35eb1ddf
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/MathGame.Dknx8888/.idea/.idea.MathGame.Dknx8888/.idea/.gitignore b/MathGame.Dknx8888/.idea/.idea.MathGame.Dknx8888/.idea/.gitignore
new file mode 100644
index 00000000..9a24c68e
--- /dev/null
+++ b/MathGame.Dknx8888/.idea/.idea.MathGame.Dknx8888/.idea/.gitignore
@@ -0,0 +1,15 @@
+# Default ignored files
+/shelf/
+/workspace.xml
+# Rider ignored files
+/modules.xml
+/projectSettingsUpdater.xml
+/contentModel.xml
+/.idea.ConsoleApp1.iml
+# Editor-based HTTP Client requests
+/httpRequests/
+# Ignored default folder with query files
+/queries/
+# Datasource local storage ignored files
+/dataSources/
+/dataSources.local.xml
diff --git a/MathGame.Dknx8888/.idea/.idea.MathGame.Dknx8888/.idea/encodings.xml b/MathGame.Dknx8888/.idea/.idea.MathGame.Dknx8888/.idea/encodings.xml
new file mode 100644
index 00000000..df87cf95
--- /dev/null
+++ b/MathGame.Dknx8888/.idea/.idea.MathGame.Dknx8888/.idea/encodings.xml
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/MathGame.Dknx8888/.idea/.idea.MathGame.Dknx8888/.idea/indexLayout.xml b/MathGame.Dknx8888/.idea/.idea.MathGame.Dknx8888/.idea/indexLayout.xml
new file mode 100644
index 00000000..7b08163c
--- /dev/null
+++ b/MathGame.Dknx8888/.idea/.idea.MathGame.Dknx8888/.idea/indexLayout.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/MathGame.Dknx8888/.idea/.idea.MathGame.Dknx8888/.idea/vcs.xml b/MathGame.Dknx8888/.idea/.idea.MathGame.Dknx8888/.idea/vcs.xml
new file mode 100644
index 00000000..6c0b8635
--- /dev/null
+++ b/MathGame.Dknx8888/.idea/.idea.MathGame.Dknx8888/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/MathGame.Dknx8888/Difficulty.cs b/MathGame.Dknx8888/Difficulty.cs
new file mode 100644
index 00000000..daa9e80c
--- /dev/null
+++ b/MathGame.Dknx8888/Difficulty.cs
@@ -0,0 +1,8 @@
+namespace MathGame.Dknx8888;
+
+public enum Difficulty
+{
+ Easy,
+ Medium,
+ Hard
+}
\ No newline at end of file
diff --git a/MathGame.Dknx8888/GameHistory.cs b/MathGame.Dknx8888/GameHistory.cs
new file mode 100644
index 00000000..c6bb1561
--- /dev/null
+++ b/MathGame.Dknx8888/GameHistory.cs
@@ -0,0 +1,115 @@
+using System.Text.Json;
+using static MathGame.Dknx8888.GameResultTemplate;
+
+namespace MathGame.Dknx8888;
+
+public class GameHistory
+{
+ private readonly JsonSerializerOptions _options = new() { WriteIndented = true };
+ public void ShowHistory()
+ {
+ while (true)
+ {
+ Console.Clear();
+
+ var filePath = GameHistoryJson.FilePath;
+ if (!File.Exists(filePath))
+ {
+ EmptyHistoryError();
+ return;
+ }
+
+ var json = File.ReadAllText(filePath);
+ if (string.IsNullOrWhiteSpace(json))
+ {
+ EmptyHistoryError();
+ return;
+ }
+
+ var gameResults = JsonSerializer.Deserialize>(json, _options) ?? [];
+ if (gameResults.Count == 0)
+ {
+ EmptyHistoryError();
+ return;
+ }
+
+ Console.WriteLine("Game History\n");
+ Console.WriteLine("Please select a game via ID (1,2, etc.) to show more details");
+ Console.WriteLine("Type q to go back\n");
+ Console.WriteLine($"{"ID",-4} | {"Date and Time Started",-22} | {"Score",-7} | {"Mode",-15} | {"Duration (sec)",-10}");
+ Console.WriteLine(new string('-', 76));
+
+ // Show from latest (bad idea?)
+ // gameResults.Reverse();
+ for (var i = 0; i < gameResults.Count; i++)
+ {
+ var (gameMode, _, score, duration, _, startTime) = gameResults[i];
+
+ Console.WriteLine($"{i + 1,-4} | {startTime,-22} | {score,-7} | {gameMode,-15} | {duration,-10:F2}");
+ }
+
+ int selectedId;
+ bool isValidId;
+
+ do
+ {
+ var input = Console.ReadLine()?.Trim();
+
+ // while loop in GameSession.cs does not work here because of this q
+ if (string.Equals(input, "q", StringComparison.OrdinalIgnoreCase))
+ {
+ return;
+ }
+
+ isValidId = int.TryParse(input, out selectedId)
+ && selectedId >= 1
+ && selectedId <= gameResults.Count;
+
+ if (!isValidId)
+ {
+ Console.WriteLine("Please enter a valid ID number, or q to go back");
+ }
+ } while (!isValidId);
+
+ var selectedGameResult = gameResults[selectedId - 1];
+ ShowDetails(selectedGameResult);
+ }
+ }
+
+ private static void EmptyHistoryError()
+ {
+ Console.WriteLine("No game history found.");
+ Console.WriteLine("Press any key to continue...");
+ Console.ReadKey();
+ }
+
+ private static void ShowDetails(GameResult selectedGameResult)
+ {
+ var (gameMode, difficulty, score, duration, questionResults, startTime) = selectedGameResult;
+
+ Console.Clear();
+ Console.WriteLine($"Start time: {startTime}");
+ Console.WriteLine($"Game mode: {gameMode}");
+ Console.WriteLine($"Difficulty: {difficulty}");
+ Console.WriteLine($"Duration (sec): {duration:F2}");
+ Console.WriteLine($"Score: {score}/5\n");
+
+ // Way better idea to include id in the records
+ var questionId = 1;
+ // Mind the one spaces
+ Console.WriteLine($"{"Q. Number", -10} {"Result", -8} {"Question", -12} {"Your Answer", 12} {"Correct Answer", 14}");
+ Console.WriteLine(new string('-', 62));
+
+ foreach (var questionResult in questionResults)
+ {
+ var (num1, num2, sign, playerAnswer, correctAnswer, isCorrect) = questionResult;
+ var correctDisplay = isCorrect ? "Right" : "Wrong";
+ var questionDisplay = $"{num1} {sign} {num2}";
+
+ Console.WriteLine($"{questionId, -10} {correctDisplay, -8} {questionDisplay, -12} {playerAnswer, 12} {correctAnswer, 14}");
+ ++questionId;
+ }
+ Console.WriteLine("Press any key to continue...");
+ Console.ReadKey();
+ }
+}
\ No newline at end of file
diff --git a/MathGame.Dknx8888/GameHistoryJson.cs b/MathGame.Dknx8888/GameHistoryJson.cs
new file mode 100644
index 00000000..1a25a48b
--- /dev/null
+++ b/MathGame.Dknx8888/GameHistoryJson.cs
@@ -0,0 +1,18 @@
+using System.Runtime.CompilerServices;
+
+namespace MathGame.Dknx8888;
+
+// Repository pattern in the future?
+public static class GameHistoryJson
+{
+ public static string FilePath => GetGameResultsFilePath();
+
+ // CallerFilePath gets the path of the source file that called this func
+ private static string GetGameResultsFilePath([CallerFilePath] string sourceFilePath = "")
+ {
+ var sourceDirectory = Path.GetDirectoryName(sourceFilePath)
+ ?? throw new InvalidOperationException("Unable to determine the source file directory.");
+
+ return Path.Combine(sourceDirectory, "game-results.json");
+ }
+}
\ No newline at end of file
diff --git a/MathGame.Dknx8888/GameMode.cs b/MathGame.Dknx8888/GameMode.cs
new file mode 100644
index 00000000..ddeeb598
--- /dev/null
+++ b/MathGame.Dknx8888/GameMode.cs
@@ -0,0 +1,10 @@
+namespace MathGame.Dknx8888;
+
+public enum GameMode
+{
+ Addition,
+ Subtraction,
+ Multiplication,
+ Division,
+ Random
+}
\ No newline at end of file
diff --git a/MathGame.Dknx8888/GameResultTemplate.cs b/MathGame.Dknx8888/GameResultTemplate.cs
new file mode 100644
index 00000000..9e422fe1
--- /dev/null
+++ b/MathGame.Dknx8888/GameResultTemplate.cs
@@ -0,0 +1,22 @@
+namespace MathGame.Dknx8888;
+
+public static class GameResultTemplate
+{
+ public record QuestionResult (
+ int Num1,
+ int Num2,
+ char Sign,
+ double PlayerAnswer,
+ int CorrectAnswer,
+ bool IsCorrect
+ );
+
+ public record GameResult (
+ GameMode GameMode,
+ Difficulty Difficulty,
+ int Score,
+ double Duration,
+ List Questions,
+ DateTime StartTime
+ );
+}
\ No newline at end of file
diff --git a/MathGame.Dknx8888/GameSession.cs b/MathGame.Dknx8888/GameSession.cs
new file mode 100644
index 00000000..fc4c402a
--- /dev/null
+++ b/MathGame.Dknx8888/GameSession.cs
@@ -0,0 +1,156 @@
+using System.Diagnostics;
+using System.Text.Json;
+using static MathGame.Dknx8888.GameResultTemplate;
+
+namespace MathGame.Dknx8888;
+
+public class GameSession(GameMode gameMode, Difficulty difficulty)
+{
+ private readonly Random _random = new();
+ private readonly JsonSerializerOptions _options = new JsonSerializerOptions { WriteIndented = true };
+
+ public void Start()
+ {
+ var count = 0;
+ var score = 0;
+ HashSet<(int, int, GameMode)> questions = [];
+ var timer = Stopwatch.StartNew();
+ List questionResults = [];
+ List gameResults;
+ var startingDateTime = DateTime.Now;
+
+ Console.Clear();
+ Console.WriteLine("Please answer the following questions: ");
+
+ // May implement quitting in the middle of the game later
+ while (count < 5)
+ {
+ int num1;
+ int num2;
+ GameMode selectedGameMode;
+
+ // Handle duplicate questions
+ do
+ {
+ (num1, num2) = NumGen();
+
+ selectedGameMode = gameMode == GameMode.Random ? GetRandomGameMode() : gameMode;
+
+ // Division handling
+ if (selectedGameMode != GameMode.Division) continue;
+ while (num2 == 0 || num1 % num2 != 0)
+ {
+ (num1, num2) = NumGen();
+ }
+ } while (!questions.Add((num1, num2, selectedGameMode))); // True if new q, False if already had
+
+ var sign = selectedGameMode switch
+ {
+ GameMode.Addition => '+',
+ GameMode.Subtraction => '-',
+ GameMode.Multiplication => '*',
+ GameMode.Division => '/',
+ _ => throw new InvalidOperationException("Invalid game mode selected.")
+ };
+
+ var correctResult = selectedGameMode switch
+ {
+ GameMode.Addition => num1 + num2,
+ GameMode.Subtraction => num1 - num2,
+ GameMode.Multiplication => num1 * num2,
+ GameMode.Division => num1 / num2,
+ _ => throw new InvalidOperationException("Invalid game mode selected.")
+ };
+
+ Console.WriteLine($"{num1} {sign} {num2} = ?");
+
+ // Checking int would give the player a hint instead so nah
+ double playerInput;
+ // Put ReadLine in while to avoid infinite loops
+ while (!double.TryParse(Console.ReadLine()?.Trim(), out playerInput))
+ {
+ Console.WriteLine("Please enter a valid number");
+ }
+
+ // ReSharper disable once CompareOfFloatsByEqualityOperator
+ var isCorrect = playerInput == correctResult;
+ if (isCorrect)
+ {
+ ++score;
+ Console.WriteLine("Congratulations! You are correct!");
+ }
+ else
+ {
+ Console.WriteLine($"You are incorrect. The correct answer is {correctResult}");
+ }
+
+ // Add record
+ questionResults.Add(new QuestionResult (
+ num1,
+ num2,
+ sign,
+ playerInput,
+ correctResult,
+ isCorrect
+ ));
+
+ count++;
+ }
+ timer.Stop();
+ var duration = timer.Elapsed.TotalSeconds;
+ Console.WriteLine($"Your final score is {score} out of 5!");
+ Console.WriteLine($"You completed this game in {duration:F2} seconds.");
+
+ // Save
+ var gameResult = new GameResult(
+ gameMode,
+ difficulty,
+ score,
+ duration,
+ questionResults,
+ startingDateTime
+ );
+
+ var filePath = GameHistoryJson.FilePath;
+ if (File.Exists(filePath))
+ {
+ var existingJson = File.ReadAllText(filePath);
+ gameResults = JsonSerializer.Deserialize>(existingJson, _options) ?? [];
+ }
+ else
+ {
+ gameResults = [];
+ }
+
+ gameResults.Add(gameResult);
+ var json = JsonSerializer.Serialize(gameResults, _options);
+ File.WriteAllText(filePath, json);
+ Console.WriteLine($"Game history saved to: {filePath}");
+
+ Console.WriteLine("Press any key to continue...");
+ Console.ReadKey();
+ }
+
+ private (int, int) NumGen()
+ {
+ return difficulty switch
+ {
+ Difficulty.Easy => (_random.Next(0, 10), _random.Next(0, 10)),
+ Difficulty.Medium => (_random.Next(10, 101), _random.Next(0, 10)),
+ Difficulty.Hard => (_random.Next(10, 101), _random.Next(10, 101)),
+ _ => throw new InvalidOperationException("Somehow an invalid difficulty is selected. Fix your bug.")
+ };
+ }
+
+ private GameMode GetRandomGameMode()
+ {
+ GameMode[] gameModes =
+ [
+ GameMode.Addition,
+ GameMode.Subtraction,
+ GameMode.Multiplication,
+ GameMode.Division
+ ];
+ return gameModes[_random.Next(gameModes.Length)];
+ }
+}
diff --git a/MathGame.Dknx8888/MathGame.Dknx8888.csproj b/MathGame.Dknx8888/MathGame.Dknx8888.csproj
new file mode 100644
index 00000000..6c1dc922
--- /dev/null
+++ b/MathGame.Dknx8888/MathGame.Dknx8888.csproj
@@ -0,0 +1,10 @@
+
+
+
+ Exe
+ net10.0
+ enable
+ enable
+
+
+
diff --git a/MathGame.Dknx8888/MathGame.Dknx8888.sln b/MathGame.Dknx8888/MathGame.Dknx8888.sln
new file mode 100644
index 00000000..195c6325
--- /dev/null
+++ b/MathGame.Dknx8888/MathGame.Dknx8888.sln
@@ -0,0 +1,16 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MathGame.Dknx8888", "MathGame.Dknx8888.csproj", "{A06D9D56-E8B5-402E-BEDC-BE092E066CCE}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {A06D9D56-E8B5-402E-BEDC-BE092E066CCE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {A06D9D56-E8B5-402E-BEDC-BE092E066CCE}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {A06D9D56-E8B5-402E-BEDC-BE092E066CCE}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {A06D9D56-E8B5-402E-BEDC-BE092E066CCE}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+EndGlobal
diff --git a/MathGame.Dknx8888/Menu.cs b/MathGame.Dknx8888/Menu.cs
new file mode 100644
index 00000000..5adef639
--- /dev/null
+++ b/MathGame.Dknx8888/Menu.cs
@@ -0,0 +1,124 @@
+namespace MathGame.Dknx8888;
+
+public class Menu
+{
+ private Difficulty _difficulty = Difficulty.Easy;
+
+ // NOTE: This is an expression-bodied property, using => not =
+ private string DifficultyDisplay => _difficulty switch
+ {
+ Difficulty.Easy => "Easy",
+ Difficulty.Medium => "Medium",
+ Difficulty.Hard => "Hard",
+ _ => throw new ArgumentOutOfRangeException()
+ };
+
+ public void ShowMenu()
+ {
+ while (true)
+ {
+ Console.Clear();
+ Console.WriteLine("Welcome to A Simple Math Game!");
+ Console.WriteLine($"\nCurrently selected difficulty: {DifficultyDisplay}\n");
+ Console.WriteLine("Press choose one of the options below (1-4):");
+ Console.WriteLine("1. Start");
+ Console.WriteLine("2. View Game History");
+ Console.WriteLine("3. Choose Difficulty");
+ Console.WriteLine("4. Quit");
+
+ var input = Console.ReadLine()?.Trim();
+
+ switch (input)
+ {
+ case "1":
+ ShowGameModes();
+ break;
+
+ case "2":
+ var gameHistory = new GameHistory();
+ gameHistory.ShowHistory();
+ break;
+
+ case "3":
+ DifficultyMenu();
+ break;
+
+ case "4":
+ Console.WriteLine("\nGoodbye!");
+ return;
+ }
+ }
+ }
+
+ private void ShowGameModes()
+ {
+ while (true)
+ {
+ Console.Clear();
+ Console.WriteLine("Choose one of the modes below (1-6):");
+ Console.WriteLine("1. Addition");
+ Console.WriteLine("2. Subtraction");
+ Console.WriteLine("3. Multiplication");
+ Console.WriteLine("4. Division");
+ Console.WriteLine("5. Random");
+ Console.WriteLine("6. Go Back");
+
+ var gameModeInput = Console.ReadLine()?.Trim();
+
+ // Go back (exit the while loop)
+ if (gameModeInput == "6")
+ {
+ return;
+ }
+
+ GameMode? gameMode = gameModeInput switch
+ {
+ "1" => GameMode.Addition,
+ "2" => GameMode.Subtraction,
+ "3" => GameMode.Multiplication,
+ "4" => GameMode.Division,
+ "5" => GameMode.Random,
+ _ => null
+ };
+
+ // Nothing ever happens.
+ if (gameMode is null)
+ {
+ continue;
+ }
+
+ // Starts game here (so .Value here is needed because gameMode is nullable)
+ var gameSession = new GameSession(gameMode.Value, _difficulty);
+ gameSession.Start();
+ }
+ }
+
+ private void DifficultyMenu()
+ {
+ while (true)
+ {
+ Console.Clear();
+ Console.WriteLine($"Currently Selected Difficulty: {DifficultyDisplay}\n");
+ Console.WriteLine("Please select one of the difficulties below (1-3): ");
+ Console.WriteLine("1. Easy (1 digit operations) - Default");
+ Console.WriteLine("2. Medium (1 digit number and 2 digit number operations)");
+ Console.WriteLine("3: Hard (2 digit number operations)");
+ Console.WriteLine("4: Go Back");
+
+ var difficultyInput = Console.ReadLine()?.Trim();
+
+ if (difficultyInput == "4")
+ {
+ break;
+ }
+
+ _difficulty = difficultyInput switch
+ {
+ "1" => Difficulty.Easy,
+ "2" => Difficulty.Medium,
+ "3" => Difficulty.Hard,
+ _ => _difficulty // Invalid keeps the selected one
+ };
+ }
+ }
+}
\ No newline at end of file
diff --git a/MathGame.Dknx8888/Program.cs b/MathGame.Dknx8888/Program.cs
new file mode 100644
index 00000000..4ffbcc5f
--- /dev/null
+++ b/MathGame.Dknx8888/Program.cs
@@ -0,0 +1,4 @@
+using MathGame.Dknx8888;
+
+var menu = new Menu();
+menu.ShowMenu();
\ No newline at end of file