Merge pull request #784 from drewjcooper/csharp-34-digits

C# 34 digits
This commit is contained in:
Jeff Atwood
2022-08-25 14:40:01 -07:00
committed by GitHub
20 changed files with 287 additions and 0 deletions

View File

@@ -6,4 +6,12 @@
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<EmbeddedResource Include="Resources/*.txt" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\00_Common\dotnet\Games.Common\Games.Common.csproj" />
</ItemGroup>
</Project>

80
34_Digits/csharp/Game.cs Normal file
View File

@@ -0,0 +1,80 @@
namespace Digits;
internal class GameSeries
{
private readonly IReadOnlyList<int> _weights = new List<int> { 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<int> 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;
}
}

View File

@@ -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);
}

View File

@@ -0,0 +1,20 @@
namespace Digits;
internal static class IOExtensions
{
internal static IEnumerable<int> 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);
}
}
}

View File

@@ -0,0 +1,27 @@
namespace Digits;
internal class Matrix
{
private readonly int _weight;
private readonly int[,] _values;
public Matrix(int width, int weight, Func<int, int, int> 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]++;
}

View File

@@ -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;
}
}

View File

@@ -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();

View File

@@ -0,0 +1 @@
For instructions, type '1', else type '0'

View File

@@ -0,0 +1 @@
{0} {1} {2} {3}

View File

@@ -0,0 +1,3 @@
My guess Your no. Result No. right

View File

@@ -0,0 +1,4 @@
I guessed more than 1/3 of your numbers.
I win.

View File

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

View File

@@ -0,0 +1,6 @@
Digits
Creative Computing Morristown, New Jersey
This is a game of guessing.

View File

@@ -0,0 +1,4 @@
I guessed exactly 1/3 of your numbers.
It's a tie game.

View File

@@ -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}'.");
}

View File

@@ -0,0 +1,2 @@
Ten numbers, please

View File

@@ -0,0 +1,2 @@
Thanks for the game

View File

@@ -0,0 +1,2 @@
Only use the digits '0', '1', or '2'.
Let's try again.

View File

@@ -0,0 +1 @@
Do you want to try again (1 for yes, 0 for no)

View File

@@ -0,0 +1,4 @@
I guessed less than 1/3 of your numbers.
You beat me. Congratulations *****