Merge branch 'coding-horror:main' into csharp-30-cube

This commit is contained in:
Andrew Cooper
2022-07-24 18:23:49 +10:00
committed by GitHub
43 changed files with 1005 additions and 1 deletions
+9
View File
@@ -0,0 +1,9 @@
[package]
name = "rust"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
rand = "0.8.5"
+214
View File
@@ -0,0 +1,214 @@
use std::{thread, time::Duration};
use rand::Rng;
// AI "learning" is not implemented. Don't have the time. - Ugur
fn main() {
loop {
let mut game = Game::default();
loop {
game.draw();
if game.play_turn(false) {
break;
}
}
}
}
enum DistributeResult {
Normal,
// Leftover beans
EndOnHomePit(bool),
// "true" if ended on Player Home Pit
EndOnEmptyPit(usize),
// "index" of the empty pit within the Row
ChosenEmpty,
}
struct Game {
pits: [u8; 14],
player_turn: bool,
}
impl Default for Game {
fn default() -> Self {
println!("\n\n\t\t AWARI");
println!("CREATIVE COMPUTING MORRISTOWN, NEW JERSEY");
Self {
pits: [3, 3, 3, 3, 3, 3, 0, 3, 3, 3, 3, 3, 3, 0],
player_turn: true,
}
}
}
impl Game {
fn step_through(&mut self, mut index: usize) -> usize {
let mut bean_amount = self.pits[index];
self.pits[index] = 0;
loop {
index += 1;
if index > self.pits.len() - 1 {
index = 0;
}
self.pits[index] += 1;
bean_amount -= 1;
if bean_amount == 0 {
return index;
}
}
}
fn play_turn(&mut self, is_repeat: bool) -> bool {
use DistributeResult::*;
if self.is_game_over() {
println!("\nGame Over!");
let (player_beans, ai_beans) = (self.pits[6], self.pits[13]);
if player_beans == ai_beans {
println!("It's a draw")
} else if player_beans > ai_beans {
println!("You win by {}", player_beans - ai_beans);
} else {
println!("I win by {}", ai_beans - player_beans);
}
return true;
}
let chosen_index = if self.player_turn {
player_prompt(if is_repeat { "Again?" } else { "Your move?" }) - 1
} else {
println!("========================");
thread::sleep(Duration::from_secs(1));
let non_empty_pits: Vec<usize> = self
.pits
.iter()
.enumerate()
.filter(|&(i, p)| (7..13).contains(&i) && *p > 0)
.map(|(i, _)| i)
.collect();
let random_index = rand::thread_rng().gen_range(0..non_empty_pits.len());
let ai_move = non_empty_pits[random_index];
println!("My move is {}", ai_move - 6);
println!("========================");
ai_move
};
match self.process_choice(chosen_index) {
Normal => (),
EndOnHomePit(player) => {
self.draw();
if player == self.player_turn && !is_repeat {
self.play_turn(true);
}
}
EndOnEmptyPit(last_index) => {
let opposite_index = 12 - last_index;
let home_index = if self.player_turn { 6 } else { 13 };
let won_beans = 1 + self.pits[opposite_index];
self.pits[last_index] = 0;
self.pits[opposite_index] = 0;
self.pits[home_index] += won_beans;
}
ChosenEmpty => {
println!("Chosen pit is empty");
return self.play_turn(is_repeat);
}
}
if !is_repeat {
self.player_turn = !self.player_turn;
}
false
}
pub fn process_choice(&mut self, index: usize) -> DistributeResult {
use DistributeResult::*;
if self.pits[index] == 0 {
return ChosenEmpty;
}
let last_index = self.step_through(index);
if last_index == 6 && self.player_turn {
return EndOnHomePit(true);
} else if last_index == 13 && !self.player_turn {
return EndOnHomePit(false);
} else if self.pits[last_index] == 1 {
return EndOnEmptyPit(last_index);
}
Normal
}
fn is_game_over(&self) -> bool {
let player_empty = !(0..6).any(|i| self.pits[i] > 0);
let ai_empty = !(7..13).any(|i| self.pits[i] > 0);
player_empty || ai_empty
}
fn draw(&self) {
let row_as_string = |player: bool| -> String {
let mut row_as_string = String::new();
let range = if player { 0..6 } else { 7..13 };
range.for_each(|i| {
let mut bean_amount_as_string = self.pits[i].to_string();
bean_amount_as_string.push_str(" ");
if player {
row_as_string.push_str(&bean_amount_as_string);
} else {
row_as_string.insert_str(0, &bean_amount_as_string);
}
});
row_as_string
};
println!(
"\n {}\n{} {}\n {}\n",
row_as_string(false),
self.pits[13].to_string(),
self.pits[6].to_string(),
row_as_string(true)
);
}
}
pub fn player_prompt(message: &str) -> usize {
loop {
let mut input = String::new();
println!("{}", message);
if let Ok(_) = std::io::stdin().read_line(&mut input) {
match input.trim().parse::<usize>() {
Ok(n) => {
if (1..=6).contains(&n) {
return n;
} else {
println!("Enter a number between 1 and 6")
}
}
Err(e) => {
println!("{}", e);
}
}
}
}
}
+21
View File
@@ -0,0 +1,21 @@
using System.Text;
using BugGame.Parts;
using BugGame.Resources;
namespace BugGame;
internal class Bug
{
private readonly Body _body = new();
public bool IsComplete => _body.IsComplete;
public bool TryAdd(IPart part, out Message message) => _body.TryAdd(part, out message);
public string ToString(string pronoun, char feelerCharacter)
{
var builder = new StringBuilder($"*****{pronoun} Bug*****").AppendLine().AppendLine().AppendLine();
_body.AppendTo(builder, feelerCharacter);
return builder.ToString();
}
}
+9
View File
@@ -6,4 +6,13 @@
<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>
+86
View File
@@ -0,0 +1,86 @@
using BugGame.Parts;
using BugGame.Resources;
using Games.Common.IO;
using Games.Common.Randomness;
using static System.StringComparison;
namespace BugGame;
internal class Game
{
private readonly IReadWrite _io;
private readonly IRandom _random;
public Game(IReadWrite io, IRandom random)
{
_io = io;
_random = random;
}
public void Play()
{
_io.Write(Resource.Streams.Introduction);
if (!_io.ReadString("Do you want instructions").Equals("no", InvariantCultureIgnoreCase))
{
_io.Write(Resource.Streams.Instructions);
}
BuildBugs();
_io.Write(Resource.Streams.PlayAgain);
}
private void BuildBugs()
{
var yourBug = new Bug();
var myBug = new Bug();
while (true)
{
var partAdded = TryBuild(yourBug, m => m.You);
Thread.Sleep(500);
_io.WriteLine();
partAdded |= TryBuild(myBug, m => m.I);
if (partAdded)
{
if (yourBug.IsComplete) { _io.WriteLine("Your bug is finished."); }
if (myBug.IsComplete) { _io.WriteLine("My bug is finished."); }
if (!_io.ReadString("Do you want the picture").Equals("no", InvariantCultureIgnoreCase))
{
_io.Write(yourBug.ToString("Your", 'A'));
_io.WriteLine();
_io.WriteLine();
_io.WriteLine();
_io.WriteLine();
_io.Write(myBug.ToString("My", 'F'));
}
}
if (yourBug.IsComplete || myBug.IsComplete) { break; }
}
}
private bool TryBuild(Bug bug, Func<Message, string> messageTransform)
{
var roll = _random.Next(6) + 1;
_io.WriteLine(messageTransform(Message.Rolled.ForValue(roll)));
IPart part = roll switch
{
1 => new Body(),
2 => new Neck(),
3 => new Head(),
4 => new Feeler(),
5 => new Tail(),
6 => new Leg(),
_ => throw new Exception("Unexpected roll value")
};
_io.WriteLine($"{roll}={part.GetType().Name}");
var partAdded = bug.TryAdd(part, out var message);
_io.WriteLine(messageTransform.Invoke(message));
return partAdded;
}
}
+44
View File
@@ -0,0 +1,44 @@
using System.Text;
using BugGame.Resources;
namespace BugGame.Parts;
internal class Body : ParentPart
{
private readonly Neck _neck = new();
private readonly Tail _tail = new();
private readonly Legs _legs = new();
public Body()
: base(Message.BodyAdded, Message.BodyNotNeeded)
{
}
public override bool IsComplete => _neck.IsComplete && _tail.IsComplete && _legs.IsComplete;
protected override bool TryAddCore(IPart part, out Message message)
=> part switch
{
Neck => _neck.TryAdd(out message),
Head or Feeler => _neck.TryAdd(part, out message),
Tail => _tail.TryAdd(out message),
Leg => _legs.TryAddOne(out message),
_ => throw new NotSupportedException($"Can't add a {part.Name} to a {Name}.")
};
public void AppendTo(StringBuilder builder, char feelerCharacter)
{
if (IsPresent)
{
_neck.AppendTo(builder, feelerCharacter);
builder
.AppendLine(" BBBBBBBBBBBB")
.AppendLine(" B B")
.AppendLine(" B B");
_tail.AppendTo(builder);
builder
.AppendLine(" BBBBBBBBBBBB");
_legs.AppendTo(builder);
}
}
}
+6
View File
@@ -0,0 +1,6 @@
namespace BugGame.Parts;
internal class Feeler : IPart
{
public string Name => nameof(Feeler);
}
+14
View File
@@ -0,0 +1,14 @@
using System.Text;
using BugGame.Resources;
namespace BugGame.Parts;
internal class Feelers : PartCollection
{
public Feelers()
: base(2, Message.FeelerAdded, Message.FeelersFull)
{
}
public void AppendTo(StringBuilder builder, char character) => AppendTo(builder, 10, 4, character);
}
+38
View File
@@ -0,0 +1,38 @@
using System.Text;
using BugGame.Resources;
namespace BugGame.Parts;
internal class Head : ParentPart
{
private Feelers _feelers = new();
public Head()
: base(Message.HeadAdded, Message.HeadNotNeeded)
{
}
public override bool IsComplete => _feelers.IsComplete;
protected override bool TryAddCore(IPart part, out Message message)
=> part switch
{
Feeler => _feelers.TryAddOne(out message),
_ => throw new NotSupportedException($"Can't add a {part.Name} to a {Name}.")
};
public void AppendTo(StringBuilder builder, char feelerCharacter)
{
if (IsPresent)
{
_feelers.AppendTo(builder, feelerCharacter);
builder
.AppendLine(" HHHHHHH")
.AppendLine(" H H")
.AppendLine(" H O O H")
.AppendLine(" H H")
.AppendLine(" H V H")
.AppendLine(" HHHHHHH");
}
}
}
+6
View File
@@ -0,0 +1,6 @@
namespace BugGame.Parts;
internal interface IPart
{
string Name { get; }
}
+6
View File
@@ -0,0 +1,6 @@
namespace BugGame.Parts;
internal class Leg : IPart
{
public string Name => nameof(Leg);
}
+14
View File
@@ -0,0 +1,14 @@
using System.Text;
using BugGame.Resources;
namespace BugGame.Parts;
internal class Legs : PartCollection
{
public Legs()
: base(6, Message.LegAdded, Message.LegsFull)
{
}
public void AppendTo(StringBuilder builder) => AppendTo(builder, 6, 2, 'L');
}
+33
View File
@@ -0,0 +1,33 @@
using System.Text;
using BugGame.Resources;
namespace BugGame.Parts;
internal class Neck : ParentPart
{
private Head _head = new();
public Neck()
: base(Message.NeckAdded, Message.NeckNotNeeded)
{
}
public override bool IsComplete => _head.IsComplete;
protected override bool TryAddCore(IPart part, out Message message)
=> part switch
{
Head => _head.TryAdd(out message),
Feeler => _head.TryAdd(part, out message),
_ => throw new NotSupportedException($"Can't add a {part.Name} to a {Name}.")
};
public void AppendTo(StringBuilder builder, char feelerCharacter)
{
if (IsPresent)
{
_head.AppendTo(builder, feelerCharacter);
builder.AppendLine(" N N").AppendLine(" N N");
}
}
}
+27
View File
@@ -0,0 +1,27 @@
using BugGame.Resources;
namespace BugGame.Parts;
internal abstract class ParentPart : Part
{
public ParentPart(Message addedMessage, Message duplicateMessage)
: base(addedMessage, duplicateMessage)
{
}
public bool TryAdd(IPart part, out Message message)
=> (part.GetType() == GetType(), IsPresent) switch
{
(true, _) => TryAdd(out message),
(false, false) => ReportDoNotHave(out message),
_ => TryAddCore(part, out message)
};
protected abstract bool TryAddCore(IPart part, out Message message);
private bool ReportDoNotHave(out Message message)
{
message = Message.DoNotHaveA(this);
return false;
}
}
+34
View File
@@ -0,0 +1,34 @@
using BugGame.Resources;
namespace BugGame.Parts;
internal class Part : IPart
{
private readonly Message _addedMessage;
private readonly Message _duplicateMessage;
public Part(Message addedMessage, Message duplicateMessage)
{
_addedMessage = addedMessage;
_duplicateMessage = duplicateMessage;
}
public virtual bool IsComplete => IsPresent;
protected bool IsPresent { get; private set; }
public string Name => GetType().Name;
public bool TryAdd(out Message message)
{
if (IsPresent)
{
message = _duplicateMessage;
return false;
}
message = _addedMessage;
IsPresent = true;
return true;
}
}
+50
View File
@@ -0,0 +1,50 @@
using System.Text;
using BugGame.Resources;
namespace BugGame.Parts;
internal class PartCollection
{
private readonly int _maxCount;
private readonly Message _addedMessage;
private readonly Message _fullMessage;
private int _count;
public PartCollection(int maxCount, Message addedMessage, Message fullMessage)
{
_maxCount = maxCount;
_addedMessage = addedMessage;
_fullMessage = fullMessage;
}
public bool IsComplete => _count == _maxCount;
public bool TryAddOne(out Message message)
{
if (_count < _maxCount)
{
_count++;
message = _addedMessage.ForValue(_count);
return true;
}
message = _fullMessage;
return false;
}
protected void AppendTo(StringBuilder builder, int offset, int length, char character)
{
if (_count == 0) { return; }
for (var i = 0; i < length; i++)
{
builder.Append(' ', offset);
for (var j = 0; j < _count; j++)
{
builder.Append(character).Append(' ');
}
builder.AppendLine();
}
}
}
+20
View File
@@ -0,0 +1,20 @@
using System.Text;
using BugGame.Resources;
namespace BugGame.Parts;
internal class Tail : Part
{
public Tail()
: base(Message.TailAdded, Message.TailNotNeeded)
{
}
public void AppendTo(StringBuilder builder)
{
if (IsPresent)
{
builder.AppendLine("TTTTTB B");
}
}
}
+5
View File
@@ -0,0 +1,5 @@
using BugGame;
using Games.Common.IO;
using Games.Common.Randomness;
new Game(new ConsoleIO(), new RandomNumberGenerator()).Play();
+18
View File
@@ -0,0 +1,18 @@
The object of Bug is to finish your bug before I finish
mine. Each number stands for a part of the bug body.
I will roll the die for you, tell you what I rolled for you
what the number stands for, and if you can get the part.
If you can get the part I will give it to you.
The same will happen on my turn.
If there is a change in either bug I will give you the
option of seeing the pictures of the bugs.
The numbers stand for parts as follows:
Number Part Number of part needed
1 Body 1
2 Neck 1
3 Head 1
4 Feelers 2
5 Tail 1
6 Legs 6
+8
View File
@@ -0,0 +1,8 @@
Bug
Creative Computing Morristown, New Jersey
The Game Bug
I hope you enjoy this game.
+46
View File
@@ -0,0 +1,46 @@
using BugGame.Parts;
namespace BugGame.Resources;
internal class Message
{
public static Message Rolled = new("rolled a {0}");
public static Message BodyAdded = new("now have a body.");
public static Message BodyNotNeeded = new("do not need a body.");
public static Message NeckAdded = new("now have a neck.");
public static Message NeckNotNeeded = new("do not need a neck.");
public static Message HeadAdded = new("needed a head.");
public static Message HeadNotNeeded = new("I do not need a head.", "You have a head.");
public static Message TailAdded = new("I now have a tail.", "I now give you a tail.");
public static Message TailNotNeeded = new("I do not need a tail.", "You already have a tail.");
public static Message FeelerAdded = new("I get a feeler.", "I now give you a feeler");
public static Message FeelersFull = new("I have 2 feelers already.", "You have two feelers already");
public static Message LegAdded = new("now have {0} legs");
public static Message LegsFull = new("I have 6 feet.", "You have 6 feet already");
public static Message Complete = new("bug is finished.");
private Message(string common)
: this("I " + common, "You " + common)
{
}
private Message(string i, string you)
{
I = i;
You = you;
}
public string I { get; }
public string You { get; }
public static Message DoNotHaveA(Part part) => new($"do not have a {part.Name}");
public Message ForValue(int quantity) => new(string.Format(I, quantity), string.Format(You, quantity));
}
+1
View File
@@ -0,0 +1 @@
I hope you enjoyed the game, play it again soon!!
+19
View File
@@ -0,0 +1,19 @@
using System.Reflection;
using System.Runtime.CompilerServices;
namespace BugGame.Resources;
internal static class Resource
{
internal static class Streams
{
public static Stream Introduction => GetStream();
public static Stream Instructions => GetStream();
public static Stream PlayAgain => GetStream();
}
private static Stream GetStream([CallerMemberName] string? name = null) =>
Assembly.GetExecutingAssembly()
.GetManifestResourceStream($"Bug.Resources.{name}.txt")
?? throw new Exception($"Could not find embedded resource stream '{name}'.");
}
+8
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>
+56
View 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
View 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
View 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
View 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
View File
@@ -0,0 +1,5 @@
global using Games.Common.IO;
global using Chomp.Resources;
using Chomp;
new Game(new ConsoleIO()).Play();
@@ -0,0 +1 @@
Coordinates of Chomp (row, column)
+2
View File
@@ -0,0 +1,2 @@
Here we go...
@@ -0,0 +1 @@
How many columns
@@ -0,0 +1 @@
How many players
@@ -0,0 +1 @@
How many rows
@@ -0,0 +1,6 @@
Chomp
Creative Computing Morristown, New Jersey
This is the game of Chomp (Scientific American, Jan 1973)
+1
View File
@@ -0,0 +1 @@
No fair. You're trying to chomp on empty space!
+1
View File
@@ -0,0 +1 @@
Player{0}
+48
View 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
View 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.
@@ -0,0 +1 @@
Too many rows (9 is maximum). Now,
@@ -0,0 +1 @@
Too many rows (9 is maximum). Now,
+1
View File
@@ -0,0 +1 @@
You lose, player{0}
+1 -1
View File
@@ -72,7 +72,7 @@ The relation between the Historical and Standard nomenclatures is shown in the s
- If you dont zap a Klingon hard enough (relative to his shield strength) you wont damage him at all. Your sensors will tell the story.
- Damage control will let you know when out-of-commission devices have been completely repaired.
9. Your engines will automatically shit down if you should attempt to leave the galaxy, or if you should try to maneuver through a star, or Starbase, or—heaven help you—a Klingon warship.
9. Your engines will automatically shut down if you should attempt to leave the galaxy, or if you should try to maneuver through a star, or Starbase, or—heaven help you—a Klingon warship.
10. In a pinch, or if you should miscalculate slightly, some shield control energy will be automatically diverted to warp engine control (if your shield are operational!).