diff --git a/34_Digits/csharp/Game.cs b/34_Digits/csharp/Game.cs index 2ddab01d..19f225b3 100644 --- a/34_Digits/csharp/Game.cs +++ b/34_Digits/csharp/Game.cs @@ -1,36 +1,80 @@ -namespace Digits +namespace Digits; + +internal class GameSeries { - internal class Game + private readonly IReadOnlyList _weights = new List { 0, 1, 3 }.AsReadOnly(); + + private readonly IReadWrite _io; + private readonly IRandom _random; + + public GameSeries(IReadWrite io, IRandom random) { - private readonly IReadWrite _io; - private readonly IRandom _random; - - public Game(IReadWrite io, IRandom random) - { - _io = io; - _random = random; - } - - internal void Play() - { - _io.Write(Streams.Introduction); - - if (_io.ReadNumber(Prompts.ForInstructions) != 0) - { - _io.Write(Streams.Instructions); - } - - do - { - PlayOne(); - } while (_io.ReadNumber(Prompts.WantToTryAgain) == 1); - - _io.Write(Streams.Thanks); - } - - private void PlayOne() - { - - } + _io = io; + _random = random; } -} \ No newline at end of file + + internal void Play() + { + _io.Write(Streams.Introduction); + + if (_io.ReadNumber(Prompts.ForInstructions) != 0) + { + _io.Write(Streams.Instructions); + } + + do + { + new Game(_io, _random).Play(); + } while (_io.ReadNumber(Prompts.WantToTryAgain) == 1); + + _io.Write(Streams.Thanks); + } +} + +internal class Game +{ + private readonly IReadWrite _io; + private readonly Guesser _guesser; + + public Game(IReadWrite io, IRandom random) + { + _io = io; + _guesser = new Guesser(random); + } + + public void Play() + { + var correctGuesses = 0; + + for (int round = 0; round < 3; round++) + { + var digits = _io.Read10Digits(Prompts.TenNumbers, Streams.TryAgain); + + correctGuesses = GuessDigits(digits, correctGuesses); + } + + _io.Write(correctGuesses switch + { + < 10 => Streams.YouWin, + 10 => Streams.ItsATie, + > 10 => Streams.IWin + }); + } + + private int GuessDigits(IEnumerable digits, int correctGuesses) + { + _io.Write(Streams.Headings); + + foreach (var digit in digits) + { + var guess = _guesser.GuessNextDigit(); + if (guess == digit) { correctGuesses++; } + + _io.WriteLine(Formats.GuessResult, guess, digit, guess == digit ? "Right" : "Wrong", correctGuesses); + + _guesser.ObserveActualDigit(digit); + } + + return correctGuesses; + } +} diff --git a/34_Digits/csharp/Guesser.cs b/34_Digits/csharp/Guesser.cs new file mode 100644 index 00000000..debeed18 --- /dev/null +++ b/34_Digits/csharp/Guesser.cs @@ -0,0 +1,52 @@ +namespace Digits; + +internal class Guesser +{ + private readonly IReadOnlyList _weights = new List { 0, 1, 3 }.AsReadOnly(); + private readonly int[][,] _matrices = new[] { new int[3, 3], new int[9, 3], new int[27, 3] }; + private readonly int[] _indices = new[] { 2, 8, 26 }; + private readonly IRandom _random; + + public Guesser(IRandom random) + { + _random = random; + + for (int j = 0; j < 3; j++) + { + for (int i = 0; i < 3; i++) { _matrices[0][i, j] = 9; } + for (int i = 0; i < 9; i++) { _matrices[1][i, j] = i == 4 * j ? 2 : 3; } + for (int i = 0; i < 27; i++) { _matrices[2][i, j] = 1; } + } + } + + public int GuessNextDigit() + { + var currentSum = 0; + var guess = 0; + + for (int j = 0; j < 3; j++) + { + var sum = Enumerable.Range(0, 3).Aggregate((s, i) => s + GetWeightedValue(i, j)); + if (sum > currentSum || _random.NextFloat() >= 0.5) + { + currentSum = sum; + guess = j; + } + } + + return guess; + } + + public void ObserveActualDigit(int digit) + { + for (int i = 0; i < 3; i++) + { + _matrices[i][_indices[i], digit]++; + } + _indices[2] = _indices[2] % 9 * 3 + digit; + _indices[1] = _indices[2] % 9; + _indices[0] = digit; + } + + private int GetWeightedValue(int matrix, int row) => _weights[matrix] * _matrices[matrix][_indices[matrix], row]; +} \ No newline at end of file diff --git a/34_Digits/csharp/IOExtensions.cs b/34_Digits/csharp/IOExtensions.cs new file mode 100644 index 00000000..cd6af7ad --- /dev/null +++ b/34_Digits/csharp/IOExtensions.cs @@ -0,0 +1,20 @@ +namespace Digits; + +internal static class IOExtensions +{ + internal static IEnumerable Read10Digits(this IReadWrite io, string prompt, Stream retryText) + { + while (true) + { + var numbers = new float[10]; + io.ReadNumbers(prompt, numbers); + + if (numbers.All(n => n == 0 || n == 1 || n == 2)) + { + return numbers.Select(n => (int)n); + } + + io.Write(retryText); + } + } +} \ No newline at end of file diff --git a/34_Digits/csharp/Program.cs b/34_Digits/csharp/Program.cs index a427b3c8..1b5dd2e0 100644 --- a/34_Digits/csharp/Program.cs +++ b/34_Digits/csharp/Program.cs @@ -3,4 +3,4 @@ global using Games.Common.IO; global using Games.Common.Randomness; global using static Digits.Resources.Resource; -new Game(new ConsoleIO(), new RandomNumberGenerator()).Play(); \ No newline at end of file +new GameSeries(new ConsoleIO(), new RandomNumberGenerator()).Play(); \ No newline at end of file diff --git a/34_Digits/csharp/Resources/GuessResult.txt b/34_Digits/csharp/Resources/GuessResult.txt new file mode 100644 index 00000000..4e233e03 --- /dev/null +++ b/34_Digits/csharp/Resources/GuessResult.txt @@ -0,0 +1 @@ + {0} {1} {2} {3} \ No newline at end of file diff --git a/34_Digits/csharp/Resources/Headings.txt b/34_Digits/csharp/Resources/Headings.txt index b9ece8ff..8289cdf6 100644 --- a/34_Digits/csharp/Resources/Headings.txt +++ b/34_Digits/csharp/Resources/Headings.txt @@ -1 +1,3 @@ + My guess Your no. Result No. right + diff --git a/34_Digits/csharp/Resources/Resource.cs b/34_Digits/csharp/Resources/Resource.cs index 0a939802..2e935955 100644 --- a/34_Digits/csharp/Resources/Resource.cs +++ b/34_Digits/csharp/Resources/Resource.cs @@ -24,6 +24,11 @@ internal static class Resource public static string WantToTryAgain => GetString(); } + internal static class Formats + { + public static string GuessResult => GetString(); + } + private static string GetString([CallerMemberName] string? name = null) { using var stream = GetStream(name); diff --git a/34_Digits/csharp/Resources/TenNumbers.txt b/34_Digits/csharp/Resources/TenNumbers.txt index 5286066b..ad03893a 100644 --- a/34_Digits/csharp/Resources/TenNumbers.txt +++ b/34_Digits/csharp/Resources/TenNumbers.txt @@ -1 +1,2 @@ + Ten numbers, please \ No newline at end of file