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 *****
+