mirror of
https://github.com/coding-horror/basic-computer-games.git
synced 2025-12-23 15:37:51 -08:00
@@ -6,4 +6,12 @@
|
|||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<EmbeddedResource Include="Resources/*.txt" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\..\00_Common\dotnet\Games.Common\Games.Common.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
56
26_Chomp/csharp/Cookie.cs
Normal file
56
26_Chomp/csharp/Cookie.cs
Normal file
@@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
64
26_Chomp/csharp/Game.cs
Normal file
64
26_Chomp/csharp/Game.cs
Normal file
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
24
26_Chomp/csharp/IOExtensions.cs
Normal file
24
26_Chomp/csharp/IOExtensions.cs
Normal file
@@ -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()}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
32
26_Chomp/csharp/PlayerNumber.cs
Normal file
32
26_Chomp/csharp/PlayerNumber.cs
Normal file
@@ -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();
|
||||||
|
}
|
||||||
5
26_Chomp/csharp/Program.cs
Normal file
5
26_Chomp/csharp/Program.cs
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
global using Games.Common.IO;
|
||||||
|
global using Chomp.Resources;
|
||||||
|
using Chomp;
|
||||||
|
|
||||||
|
new Game(new ConsoleIO()).Play();
|
||||||
1
26_Chomp/csharp/Resources/Coordinates.txt
Normal file
1
26_Chomp/csharp/Resources/Coordinates.txt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
Coordinates of Chomp (row, column)
|
||||||
2
26_Chomp/csharp/Resources/HereWeGo.txt
Normal file
2
26_Chomp/csharp/Resources/HereWeGo.txt
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
Here we go...
|
||||||
|
|
||||||
1
26_Chomp/csharp/Resources/HowManyColumns.txt
Normal file
1
26_Chomp/csharp/Resources/HowManyColumns.txt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
How many columns
|
||||||
1
26_Chomp/csharp/Resources/HowManyPlayers.txt
Normal file
1
26_Chomp/csharp/Resources/HowManyPlayers.txt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
How many players
|
||||||
1
26_Chomp/csharp/Resources/HowManyRows.txt
Normal file
1
26_Chomp/csharp/Resources/HowManyRows.txt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
How many rows
|
||||||
6
26_Chomp/csharp/Resources/Introduction.txt
Normal file
6
26_Chomp/csharp/Resources/Introduction.txt
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
Chomp
|
||||||
|
Creative Computing Morristown, New Jersey
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
This is the game of Chomp (Scientific American, Jan 1973)
|
||||||
1
26_Chomp/csharp/Resources/NoFair.txt
Normal file
1
26_Chomp/csharp/Resources/NoFair.txt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
No fair. You're trying to chomp on empty space!
|
||||||
1
26_Chomp/csharp/Resources/Player.txt
Normal file
1
26_Chomp/csharp/Resources/Player.txt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
Player{0}
|
||||||
48
26_Chomp/csharp/Resources/Resource.cs
Normal file
48
26_Chomp/csharp/Resources/Resource.cs
Normal file
@@ -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}'.");
|
||||||
|
}
|
||||||
22
26_Chomp/csharp/Resources/Rules.txt
Normal file
22
26_Chomp/csharp/Resources/Rules.txt
Normal file
@@ -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.
|
||||||
|
|
||||||
1
26_Chomp/csharp/Resources/TooManyColumns.txt
Normal file
1
26_Chomp/csharp/Resources/TooManyColumns.txt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
Too many rows (9 is maximum). Now,
|
||||||
1
26_Chomp/csharp/Resources/TooManyRows.txt
Normal file
1
26_Chomp/csharp/Resources/TooManyRows.txt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
Too many rows (9 is maximum). Now,
|
||||||
1
26_Chomp/csharp/Resources/YouLose.txt
Normal file
1
26_Chomp/csharp/Resources/YouLose.txt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
You lose, player{0}
|
||||||
Reference in New Issue
Block a user