diff --git a/16_Bug/csharp/Bug.cs b/16_Bug/csharp/Bug.cs
new file mode 100644
index 00000000..42e686d4
--- /dev/null
+++ b/16_Bug/csharp/Bug.cs
@@ -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();
+ }
+}
\ No newline at end of file
diff --git a/16_Bug/csharp/Bug.csproj b/16_Bug/csharp/Bug.csproj
index d3fe4757..91e759c0 100644
--- a/16_Bug/csharp/Bug.csproj
+++ b/16_Bug/csharp/Bug.csproj
@@ -6,4 +6,13 @@
enable
enable
+
+
+
+
+
+
+
+
+
diff --git a/16_Bug/csharp/Game.cs b/16_Bug/csharp/Game.cs
new file mode 100644
index 00000000..a8989a4c
--- /dev/null
+++ b/16_Bug/csharp/Game.cs
@@ -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 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;
+ }
+}
\ No newline at end of file
diff --git a/16_Bug/csharp/Parts/Body.cs b/16_Bug/csharp/Parts/Body.cs
new file mode 100644
index 00000000..58f80405
--- /dev/null
+++ b/16_Bug/csharp/Parts/Body.cs
@@ -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);
+ }
+ }
+}
diff --git a/16_Bug/csharp/Parts/Feeler.cs b/16_Bug/csharp/Parts/Feeler.cs
new file mode 100644
index 00000000..9508d541
--- /dev/null
+++ b/16_Bug/csharp/Parts/Feeler.cs
@@ -0,0 +1,6 @@
+namespace BugGame.Parts;
+
+internal class Feeler : IPart
+{
+ public string Name => nameof(Feeler);
+}
diff --git a/16_Bug/csharp/Parts/Feelers.cs b/16_Bug/csharp/Parts/Feelers.cs
new file mode 100644
index 00000000..165a073d
--- /dev/null
+++ b/16_Bug/csharp/Parts/Feelers.cs
@@ -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);
+}
diff --git a/16_Bug/csharp/Parts/Head.cs b/16_Bug/csharp/Parts/Head.cs
new file mode 100644
index 00000000..d7135575
--- /dev/null
+++ b/16_Bug/csharp/Parts/Head.cs
@@ -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");
+ }
+ }
+}
diff --git a/16_Bug/csharp/Parts/IPart.cs b/16_Bug/csharp/Parts/IPart.cs
new file mode 100644
index 00000000..e325a7c1
--- /dev/null
+++ b/16_Bug/csharp/Parts/IPart.cs
@@ -0,0 +1,6 @@
+namespace BugGame.Parts;
+
+internal interface IPart
+{
+ string Name { get; }
+}
diff --git a/16_Bug/csharp/Parts/Leg.cs b/16_Bug/csharp/Parts/Leg.cs
new file mode 100644
index 00000000..c2d4aaaf
--- /dev/null
+++ b/16_Bug/csharp/Parts/Leg.cs
@@ -0,0 +1,6 @@
+namespace BugGame.Parts;
+
+internal class Leg : IPart
+{
+ public string Name => nameof(Leg);
+}
diff --git a/16_Bug/csharp/Parts/Legs.cs b/16_Bug/csharp/Parts/Legs.cs
new file mode 100644
index 00000000..0ed8d8fc
--- /dev/null
+++ b/16_Bug/csharp/Parts/Legs.cs
@@ -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');
+}
diff --git a/16_Bug/csharp/Parts/Neck.cs b/16_Bug/csharp/Parts/Neck.cs
new file mode 100644
index 00000000..23dacfb7
--- /dev/null
+++ b/16_Bug/csharp/Parts/Neck.cs
@@ -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");
+ }
+ }
+}
diff --git a/16_Bug/csharp/Parts/ParentPart.cs b/16_Bug/csharp/Parts/ParentPart.cs
new file mode 100644
index 00000000..1ca6682f
--- /dev/null
+++ b/16_Bug/csharp/Parts/ParentPart.cs
@@ -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;
+ }
+}
diff --git a/16_Bug/csharp/Parts/Part.cs b/16_Bug/csharp/Parts/Part.cs
new file mode 100644
index 00000000..f29fbd81
--- /dev/null
+++ b/16_Bug/csharp/Parts/Part.cs
@@ -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;
+ }
+}
diff --git a/16_Bug/csharp/Parts/PartCollection.cs b/16_Bug/csharp/Parts/PartCollection.cs
new file mode 100644
index 00000000..9a0fd2ee
--- /dev/null
+++ b/16_Bug/csharp/Parts/PartCollection.cs
@@ -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();
+ }
+ }
+}
diff --git a/16_Bug/csharp/Parts/Tail.cs b/16_Bug/csharp/Parts/Tail.cs
new file mode 100644
index 00000000..ebf4b28f
--- /dev/null
+++ b/16_Bug/csharp/Parts/Tail.cs
@@ -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");
+ }
+ }
+}
\ No newline at end of file
diff --git a/16_Bug/csharp/Program.cs b/16_Bug/csharp/Program.cs
new file mode 100644
index 00000000..883e62d1
--- /dev/null
+++ b/16_Bug/csharp/Program.cs
@@ -0,0 +1,5 @@
+using BugGame;
+using Games.Common.IO;
+using Games.Common.Randomness;
+
+new Game(new ConsoleIO(), new RandomNumberGenerator()).Play();
diff --git a/16_Bug/csharp/Resources/Instructions.txt b/16_Bug/csharp/Resources/Instructions.txt
new file mode 100644
index 00000000..bdbdadd3
--- /dev/null
+++ b/16_Bug/csharp/Resources/Instructions.txt
@@ -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
+
+
diff --git a/16_Bug/csharp/Resources/Introduction.txt b/16_Bug/csharp/Resources/Introduction.txt
new file mode 100644
index 00000000..e74a833e
--- /dev/null
+++ b/16_Bug/csharp/Resources/Introduction.txt
@@ -0,0 +1,8 @@
+ Bug
+ Creative Computing Morristown, New Jersey
+
+
+
+The Game Bug
+I hope you enjoy this game.
+
diff --git a/16_Bug/csharp/Resources/Message.cs b/16_Bug/csharp/Resources/Message.cs
new file mode 100644
index 00000000..20a59d9a
--- /dev/null
+++ b/16_Bug/csharp/Resources/Message.cs
@@ -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));
+}
\ No newline at end of file
diff --git a/16_Bug/csharp/Resources/PlayAgain.txt b/16_Bug/csharp/Resources/PlayAgain.txt
new file mode 100644
index 00000000..2380e130
--- /dev/null
+++ b/16_Bug/csharp/Resources/PlayAgain.txt
@@ -0,0 +1 @@
+I hope you enjoyed the game, play it again soon!!
diff --git a/16_Bug/csharp/Resources/Resource.cs b/16_Bug/csharp/Resources/Resource.cs
new file mode 100644
index 00000000..2b34a4e6
--- /dev/null
+++ b/16_Bug/csharp/Resources/Resource.cs
@@ -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}'.");
+}
\ No newline at end of file