diff --git a/26_Chomp/csharp/Chomp.csproj b/26_Chomp/csharp/Chomp.csproj
index d3fe4757..3870320c 100644
--- a/26_Chomp/csharp/Chomp.csproj
+++ b/26_Chomp/csharp/Chomp.csproj
@@ -6,4 +6,12 @@
enable
enable
+
+
+
+
+
+
+
+
diff --git a/26_Chomp/csharp/Cookie.cs b/26_Chomp/csharp/Cookie.cs
new file mode 100644
index 00000000..725eadfe
--- /dev/null
+++ b/26_Chomp/csharp/Cookie.cs
@@ -0,0 +1,56 @@
+using System.Text;
+
+namespace Chomp;
+
+internal class Cookie
+{
+ private readonly int _rowCount;
+ private readonly int _columnCount;
+ private readonly char[][] _bits;
+
+ public Cookie(int rowCount, int columnCount)
+ {
+ _rowCount = rowCount;
+ _columnCount = columnCount;
+
+ // The calls to Math.Max here are to duplicate the original behaviour
+ // when negative values are given for the row or column count.
+ _bits = new char[Math.Max(_rowCount, 1)][];
+ for (int row = 0; row < _bits.Length; row++)
+ {
+ _bits[row] = Enumerable.Repeat('*', Math.Max(_columnCount, 1)).ToArray();
+ }
+ _bits[0][0] = 'P';
+ }
+
+ public bool TryChomp(int row, int column, out char chomped)
+ {
+ if (row < 1 || row > _rowCount || column < 1 || column > _columnCount || _bits[row - 1][column - 1] == ' ')
+ {
+ chomped = default;
+ return false;
+ }
+
+ chomped = _bits[row - 1][column - 1];
+
+ for (int r = row; r <= _rowCount; r++)
+ {
+ for (int c = column; c <= _columnCount; c++)
+ {
+ _bits[r - 1][c - 1] = ' ';
+ }
+ }
+
+ return true;
+ }
+
+ public override string ToString()
+ {
+ var builder = new StringBuilder().AppendLine(" 1 2 3 4 5 6 7 8 9");
+ for (int row = 1; row <= _bits.Length; row++)
+ {
+ builder.Append(' ').Append(row).Append(" ").AppendLine(string.Join(' ', _bits[row - 1]));
+ }
+ return builder.ToString();
+ }
+}
\ No newline at end of file
diff --git a/26_Chomp/csharp/Game.cs b/26_Chomp/csharp/Game.cs
new file mode 100644
index 00000000..77a8ccd1
--- /dev/null
+++ b/26_Chomp/csharp/Game.cs
@@ -0,0 +1,64 @@
+namespace Chomp;
+
+internal class Game
+{
+ private readonly IReadWrite _io;
+
+ public Game(IReadWrite io)
+ {
+ _io = io;
+ }
+
+ internal void Play()
+ {
+ _io.Write(Resource.Streams.Introduction);
+ if (_io.ReadNumber("Do you want the rules (1=Yes, 0=No!)") != 0)
+ {
+ _io.Write(Resource.Streams.Rules);
+ }
+
+ while (true)
+ {
+ _io.Write(Resource.Streams.HereWeGo);
+
+ var (playerCount, rowCount, columnCount) = _io.ReadParameters();
+
+ var loser = Play(new Cookie(rowCount, columnCount), new PlayerNumber(playerCount));
+
+ _io.WriteLine(string.Format(Resource.Formats.YouLose, loser));
+
+ if (_io.ReadNumber("Again (1=Yes, 0=No!)") != 1) { break; }
+ }
+ }
+
+ private PlayerNumber Play(Cookie cookie, PlayerNumber player)
+ {
+ while (true)
+ {
+ _io.WriteLine(cookie);
+
+ var poisoned = Chomp(cookie, player);
+
+ if (poisoned) { return player; }
+
+ player++;
+ }
+ }
+
+ private bool Chomp(Cookie cookie, PlayerNumber player)
+ {
+ while (true)
+ {
+ _io.WriteLine(string.Format(Resource.Formats.Player, player));
+
+ var (row, column) = _io.Read2Numbers(Resource.Prompts.Coordinates);
+
+ if (cookie.TryChomp((int)row, (int)column, out char chomped))
+ {
+ return chomped == 'P';
+ }
+
+ _io.Write(Resource.Streams.NoFair);
+ }
+ }
+}
diff --git a/26_Chomp/csharp/IOExtensions.cs b/26_Chomp/csharp/IOExtensions.cs
new file mode 100644
index 00000000..c94053a9
--- /dev/null
+++ b/26_Chomp/csharp/IOExtensions.cs
@@ -0,0 +1,24 @@
+namespace Chomp;
+
+internal static class IOExtensions
+{
+ public static (float, int, int) ReadParameters(this IReadWrite io)
+ => (
+ (int)io.ReadNumber(Resource.Prompts.HowManyPlayers),
+ io.ReadNumberWithMax(Resource.Prompts.HowManyRows, 9, Resource.Strings.TooManyRows),
+ io.ReadNumberWithMax(Resource.Prompts.HowManyColumns, 9, Resource.Strings.TooManyColumns)
+ );
+
+ private static int ReadNumberWithMax(this IReadWrite io, string initialPrompt, int max, string reprompt)
+ {
+ var prompt = initialPrompt;
+
+ while (true)
+ {
+ var response = io.ReadNumber(prompt);
+ if (response <= 9) { return (int)response; }
+
+ prompt = $"{reprompt} {initialPrompt.ToLowerInvariant()}";
+ }
+ }
+}
\ No newline at end of file
diff --git a/26_Chomp/csharp/PlayerNumber.cs b/26_Chomp/csharp/PlayerNumber.cs
new file mode 100644
index 00000000..16138a9b
--- /dev/null
+++ b/26_Chomp/csharp/PlayerNumber.cs
@@ -0,0 +1,32 @@
+namespace Chomp;
+
+internal class PlayerNumber
+{
+ private readonly float _playerCount;
+ private int _counter;
+ private float _number;
+
+ // The original code does not constrain playerCount to be an integer
+ public PlayerNumber(float playerCount)
+ {
+ _playerCount = playerCount;
+ _number = 0;
+ Increment();
+ }
+
+ public static PlayerNumber operator ++(PlayerNumber number) => number.Increment();
+
+ private PlayerNumber Increment()
+ {
+ if (_playerCount == 0) { throw new DivideByZeroException(); }
+
+ // The increment logic here is the same as the original program, and exhibits
+ // interesting behaviour when _playerCount is not an integer.
+ _counter++;
+ _number = _counter - (float)Math.Floor(_counter / _playerCount) * _playerCount;
+ if (_number == 0) { _number = _playerCount; }
+ return this;
+ }
+
+ public override string ToString() => (_number >= 0 ? " " : "") + _number.ToString();
+}
\ No newline at end of file
diff --git a/26_Chomp/csharp/Program.cs b/26_Chomp/csharp/Program.cs
new file mode 100644
index 00000000..f541259c
--- /dev/null
+++ b/26_Chomp/csharp/Program.cs
@@ -0,0 +1,5 @@
+global using Games.Common.IO;
+global using Chomp.Resources;
+using Chomp;
+
+new Game(new ConsoleIO()).Play();
\ No newline at end of file
diff --git a/26_Chomp/csharp/Resources/Coordinates.txt b/26_Chomp/csharp/Resources/Coordinates.txt
new file mode 100644
index 00000000..e78dd644
--- /dev/null
+++ b/26_Chomp/csharp/Resources/Coordinates.txt
@@ -0,0 +1 @@
+Coordinates of Chomp (row, column)
\ No newline at end of file
diff --git a/26_Chomp/csharp/Resources/HereWeGo.txt b/26_Chomp/csharp/Resources/HereWeGo.txt
new file mode 100644
index 00000000..00975d31
--- /dev/null
+++ b/26_Chomp/csharp/Resources/HereWeGo.txt
@@ -0,0 +1,2 @@
+Here we go...
+
diff --git a/26_Chomp/csharp/Resources/HowManyColumns.txt b/26_Chomp/csharp/Resources/HowManyColumns.txt
new file mode 100644
index 00000000..e323f959
--- /dev/null
+++ b/26_Chomp/csharp/Resources/HowManyColumns.txt
@@ -0,0 +1 @@
+How many columns
\ No newline at end of file
diff --git a/26_Chomp/csharp/Resources/HowManyPlayers.txt b/26_Chomp/csharp/Resources/HowManyPlayers.txt
new file mode 100644
index 00000000..98ff0ad7
--- /dev/null
+++ b/26_Chomp/csharp/Resources/HowManyPlayers.txt
@@ -0,0 +1 @@
+How many players
\ No newline at end of file
diff --git a/26_Chomp/csharp/Resources/HowManyRows.txt b/26_Chomp/csharp/Resources/HowManyRows.txt
new file mode 100644
index 00000000..1ad9464e
--- /dev/null
+++ b/26_Chomp/csharp/Resources/HowManyRows.txt
@@ -0,0 +1 @@
+How many rows
\ No newline at end of file
diff --git a/26_Chomp/csharp/Resources/Introduction.txt b/26_Chomp/csharp/Resources/Introduction.txt
new file mode 100644
index 00000000..7c95e7a6
--- /dev/null
+++ b/26_Chomp/csharp/Resources/Introduction.txt
@@ -0,0 +1,6 @@
+ Chomp
+ Creative Computing Morristown, New Jersey
+
+
+
+This is the game of Chomp (Scientific American, Jan 1973)
diff --git a/26_Chomp/csharp/Resources/NoFair.txt b/26_Chomp/csharp/Resources/NoFair.txt
new file mode 100644
index 00000000..9fe310eb
--- /dev/null
+++ b/26_Chomp/csharp/Resources/NoFair.txt
@@ -0,0 +1 @@
+No fair. You're trying to chomp on empty space!
diff --git a/26_Chomp/csharp/Resources/Player.txt b/26_Chomp/csharp/Resources/Player.txt
new file mode 100644
index 00000000..f743df3a
--- /dev/null
+++ b/26_Chomp/csharp/Resources/Player.txt
@@ -0,0 +1 @@
+Player{0}
\ No newline at end of file
diff --git a/26_Chomp/csharp/Resources/Resource.cs b/26_Chomp/csharp/Resources/Resource.cs
new file mode 100644
index 00000000..d080b1fb
--- /dev/null
+++ b/26_Chomp/csharp/Resources/Resource.cs
@@ -0,0 +1,48 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+
+namespace Chomp.Resources;
+
+internal static class Resource
+{
+ internal static class Streams
+ {
+ public static Stream HereWeGo => GetStream();
+ public static Stream Introduction => GetStream();
+ public static Stream Rules => GetStream();
+ public static Stream NoFair => GetStream();
+ }
+
+ internal static class Formats
+ {
+ public static string Player => GetString();
+ public static string YouLose => GetString();
+ }
+
+ internal static class Prompts
+ {
+ public static string Coordinates => GetString();
+ public static string HowManyPlayers => GetString();
+ public static string HowManyRows => GetString();
+ public static string HowManyColumns => GetString();
+ public static string TooManyColumns => GetString();
+ }
+
+ internal static class Strings
+ {
+ public static string TooManyColumns => GetString();
+ public static string TooManyRows => 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/26_Chomp/csharp/Resources/Rules.txt b/26_Chomp/csharp/Resources/Rules.txt
new file mode 100644
index 00000000..2fd9177f
--- /dev/null
+++ b/26_Chomp/csharp/Resources/Rules.txt
@@ -0,0 +1,22 @@
+Chomp is for 1 or more players (humans only).
+
+Here's how a board looks (this one is 5 by 7):
+
+ 1 2 3 4 5 6 7 8 9
+ 1 P * * * * * *
+ 2 * * * * * * *
+ 3 * * * * * * *
+ 4 * * * * * * *
+ 5 * * * * * * *
+
+
+The board is a big cookie - R rows high and C columns
+wide. You input R and C at the start. In the upper left
+corner of the cookie is a poison square (P). The one who
+chomps the poison square loses. To take a chomp, type the
+row and column of one of the squares on the cookie.
+All of the squares below and to the right of that square
+(including that square, too) disappear -- Chomp!!
+No fair chomping on squares that have already been chomped,
+or that are outside the original dimensions of the cookie.
+
diff --git a/26_Chomp/csharp/Resources/TooManyColumns.txt b/26_Chomp/csharp/Resources/TooManyColumns.txt
new file mode 100644
index 00000000..1e8766a7
--- /dev/null
+++ b/26_Chomp/csharp/Resources/TooManyColumns.txt
@@ -0,0 +1 @@
+Too many rows (9 is maximum). Now,
\ No newline at end of file
diff --git a/26_Chomp/csharp/Resources/TooManyRows.txt b/26_Chomp/csharp/Resources/TooManyRows.txt
new file mode 100644
index 00000000..1e8766a7
--- /dev/null
+++ b/26_Chomp/csharp/Resources/TooManyRows.txt
@@ -0,0 +1 @@
+Too many rows (9 is maximum). Now,
\ No newline at end of file
diff --git a/26_Chomp/csharp/Resources/YouLose.txt b/26_Chomp/csharp/Resources/YouLose.txt
new file mode 100644
index 00000000..1015e271
--- /dev/null
+++ b/26_Chomp/csharp/Resources/YouLose.txt
@@ -0,0 +1 @@
+You lose, player{0}