diff --git a/34_Digits/csharp/Digits.csproj b/34_Digits/csharp/Digits.csproj index d3fe4757..3870320c 100644 --- a/34_Digits/csharp/Digits.csproj +++ b/34_Digits/csharp/Digits.csproj @@ -6,4 +6,12 @@ enable enable + + + + + + + + diff --git a/34_Digits/csharp/Game.cs b/34_Digits/csharp/Game.cs new file mode 100644 index 00000000..19f225b3 --- /dev/null +++ b/34_Digits/csharp/Game.cs @@ -0,0 +1,80 @@ +namespace Digits; + +internal class GameSeries +{ + private readonly IReadOnlyList _weights = new List { 0, 1, 3 }.AsReadOnly(); + + private readonly IReadWrite _io; + private readonly IRandom _random; + + public GameSeries(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 + { + 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..9cda59e9 --- /dev/null +++ b/34_Digits/csharp/Guesser.cs @@ -0,0 +1,32 @@ +namespace Digits; + +internal class Guesser +{ + private readonly Memory _matrices = new(); + private readonly IRandom _random; + + public Guesser(IRandom random) + { + _random = random; + } + + public int GuessNextDigit() + { + var currentSum = 0; + var guess = 0; + + for (int i = 0; i < 3; i++) + { + var sum = _matrices.GetWeightedSum(i); + if (sum > currentSum || _random.NextFloat() >= 0.5) + { + currentSum = sum; + guess = i; + } + } + + return guess; + } + + public void ObserveActualDigit(int digit) => _matrices.ObserveDigit(digit); +} 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/Matrix.cs b/34_Digits/csharp/Matrix.cs new file mode 100644 index 00000000..c07b4553 --- /dev/null +++ b/34_Digits/csharp/Matrix.cs @@ -0,0 +1,27 @@ +namespace Digits; + +internal class Matrix +{ + private readonly int _weight; + private readonly int[,] _values; + + public Matrix(int width, int weight, Func seedFactory) + { + _weight = weight; + _values = new int[width, 3]; + + for (int i = 0; i < width; i++) + for (int j = 0; j < 3; j++) + { + _values[i, j] = seedFactory.Invoke(i, j); + } + + Index = width - 1; + } + + public int Index { get; set; } + + public int GetWeightedValue(int row) => _weight * _values[Index, row]; + + public int IncrementValue(int row) => _values[Index, row]++; +} \ No newline at end of file diff --git a/34_Digits/csharp/Memory.cs b/34_Digits/csharp/Memory.cs new file mode 100644 index 00000000..a3023351 --- /dev/null +++ b/34_Digits/csharp/Memory.cs @@ -0,0 +1,30 @@ +namespace Digits; + +public class Memory +{ + private readonly Matrix[] _matrices; + + public Memory() + { + _matrices = new[] + { + new Matrix(27, 3, (_, _) => 1), + new Matrix(9, 1, (i, j) => i == 4 * j ? 2 : 3), + new Matrix(3, 0, (_, _) => 9) + }; + } + + public int GetWeightedSum(int row) => _matrices.Select(m => m.GetWeightedValue(row)).Sum(); + + public void ObserveDigit(int digit) + { + for (int i = 0; i < 3; i++) + { + _matrices[i].IncrementValue(digit); + } + + _matrices[0].Index = _matrices[0].Index % 9 * 3 + digit; + _matrices[1].Index = _matrices[0].Index % 9; + _matrices[2].Index = digit; + } +} \ No newline at end of file diff --git a/34_Digits/csharp/Program.cs b/34_Digits/csharp/Program.cs new file mode 100644 index 00000000..1b5dd2e0 --- /dev/null +++ b/34_Digits/csharp/Program.cs @@ -0,0 +1,6 @@ +global using Digits; +global using Games.Common.IO; +global using Games.Common.Randomness; +global using static Digits.Resources.Resource; + +new GameSeries(new ConsoleIO(), new RandomNumberGenerator()).Play(); \ No newline at end of file diff --git a/34_Digits/csharp/Resources/ForInstructions.txt b/34_Digits/csharp/Resources/ForInstructions.txt new file mode 100644 index 00000000..1c16d5f4 --- /dev/null +++ b/34_Digits/csharp/Resources/ForInstructions.txt @@ -0,0 +1 @@ +For instructions, type '1', else type '0' \ 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 new file mode 100644 index 00000000..8289cdf6 --- /dev/null +++ b/34_Digits/csharp/Resources/Headings.txt @@ -0,0 +1,3 @@ + +My guess Your no. Result No. right + diff --git a/34_Digits/csharp/Resources/IWin.txt b/34_Digits/csharp/Resources/IWin.txt new file mode 100644 index 00000000..491f69cf --- /dev/null +++ b/34_Digits/csharp/Resources/IWin.txt @@ -0,0 +1,4 @@ + +I guessed more than 1/3 of your numbers. +I win. + diff --git a/34_Digits/csharp/Resources/Instructions.txt b/34_Digits/csharp/Resources/Instructions.txt new file mode 100644 index 00000000..f9ff2a16 --- /dev/null +++ b/34_Digits/csharp/Resources/Instructions.txt @@ -0,0 +1,11 @@ + +Please take a piece of paper and write down +the digits '0', '1', or '2' thirty times at random. +Arrange them in three lines of ten digits each. +I will ask for then ten at a time. +I will always guess them first and then look at your +next number to see if I was right. By pure luck, +I ought to be right ten times. But I hope to do better +than that ***** + + diff --git a/34_Digits/csharp/Resources/Introduction.txt b/34_Digits/csharp/Resources/Introduction.txt new file mode 100644 index 00000000..e4d2d93e --- /dev/null +++ b/34_Digits/csharp/Resources/Introduction.txt @@ -0,0 +1,6 @@ + Digits + Creative Computing Morristown, New Jersey + + + +This is a game of guessing. diff --git a/34_Digits/csharp/Resources/ItsATie.txt b/34_Digits/csharp/Resources/ItsATie.txt new file mode 100644 index 00000000..0e92fbf6 --- /dev/null +++ b/34_Digits/csharp/Resources/ItsATie.txt @@ -0,0 +1,4 @@ + +I guessed exactly 1/3 of your numbers. +It's a tie game. + diff --git a/34_Digits/csharp/Resources/Resource.cs b/34_Digits/csharp/Resources/Resource.cs new file mode 100644 index 00000000..2e935955 --- /dev/null +++ b/34_Digits/csharp/Resources/Resource.cs @@ -0,0 +1,43 @@ +using System.Reflection; +using System.Runtime.CompilerServices; + +namespace Digits.Resources; + +internal static class Resource +{ + internal static class Streams + { + public static Stream Introduction => GetStream(); + public static Stream Instructions => GetStream(); + public static Stream TryAgain => GetStream(); + public static Stream ItsATie => GetStream(); + public static Stream IWin => GetStream(); + public static Stream YouWin => GetStream(); + public static Stream Thanks => GetStream(); + public static Stream Headings => GetStream(); + } + + internal static class Prompts + { + public static string ForInstructions => GetString(); + public static string TenNumbers => GetString(); + 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); + using var reader = new StreamReader(stream); + return reader.ReadToEnd(); + } + + + private static Stream GetStream([CallerMemberName] string? name = null) => + Assembly.GetExecutingAssembly().GetManifestResourceStream($"{typeof(Resource).Namespace}.{name}.txt") + ?? throw new Exception($"Could not find embedded resource stream '{name}'."); +} \ No newline at end of file diff --git a/34_Digits/csharp/Resources/TenNumbers.txt b/34_Digits/csharp/Resources/TenNumbers.txt new file mode 100644 index 00000000..ad03893a --- /dev/null +++ b/34_Digits/csharp/Resources/TenNumbers.txt @@ -0,0 +1,2 @@ + +Ten numbers, please \ No newline at end of file diff --git a/34_Digits/csharp/Resources/Thanks.txt b/34_Digits/csharp/Resources/Thanks.txt new file mode 100644 index 00000000..15d42e1b --- /dev/null +++ b/34_Digits/csharp/Resources/Thanks.txt @@ -0,0 +1,2 @@ + +Thanks for the game diff --git a/34_Digits/csharp/Resources/TryAgain.txt b/34_Digits/csharp/Resources/TryAgain.txt new file mode 100644 index 00000000..74bdca68 --- /dev/null +++ b/34_Digits/csharp/Resources/TryAgain.txt @@ -0,0 +1,2 @@ +Only use the digits '0', '1', or '2'. +Let's try again. diff --git a/34_Digits/csharp/Resources/WantToTryAgain.txt b/34_Digits/csharp/Resources/WantToTryAgain.txt new file mode 100644 index 00000000..38f4509d --- /dev/null +++ b/34_Digits/csharp/Resources/WantToTryAgain.txt @@ -0,0 +1 @@ +Do you want to try again (1 for yes, 0 for no) \ No newline at end of file diff --git a/34_Digits/csharp/Resources/YouWin.txt b/34_Digits/csharp/Resources/YouWin.txt new file mode 100644 index 00000000..87b26b38 --- /dev/null +++ b/34_Digits/csharp/Resources/YouWin.txt @@ -0,0 +1,4 @@ + +I guessed less than 1/3 of your numbers. +You beat me. Congratulations ***** +