From 961085be483541e6c23a1679e6d08c893d8f1fa5 Mon Sep 17 00:00:00 2001 From: Andrew Cooper Date: Sat, 23 Jul 2022 10:00:57 +1000 Subject: [PATCH 1/6] Add text resources --- 30_Cube/csharp/Cube.csproj | 8 ++++ 30_Cube/csharp/Resources/Balance.txt | 1 + 30_Cube/csharp/Resources/Bang.txt | 4 ++ 30_Cube/csharp/Resources/BetAgain.txt | 1 + 30_Cube/csharp/Resources/Bust.txt | 1 + 30_Cube/csharp/Resources/Congratulations.txt | 1 + 30_Cube/csharp/Resources/Goodbye.txt | 3 ++ 30_Cube/csharp/Resources/HowMuch.txt | 1 + 30_Cube/csharp/Resources/IllegalMove.txt | 2 + 30_Cube/csharp/Resources/Instructions.txt | 24 +++++++++++ 30_Cube/csharp/Resources/Introduction.txt | 6 +++ 30_Cube/csharp/Resources/NextMove.txt | 1 + 30_Cube/csharp/Resources/Resource.cs | 44 ++++++++++++++++++++ 30_Cube/csharp/Resources/TryAgain.txt | 1 + 30_Cube/csharp/Resources/Wager.txt | 1 + 30_Cube/csharp/Resources/YourMove.txt | 2 + 16 files changed, 101 insertions(+) create mode 100644 30_Cube/csharp/Resources/Balance.txt create mode 100644 30_Cube/csharp/Resources/Bang.txt create mode 100644 30_Cube/csharp/Resources/BetAgain.txt create mode 100644 30_Cube/csharp/Resources/Bust.txt create mode 100644 30_Cube/csharp/Resources/Congratulations.txt create mode 100644 30_Cube/csharp/Resources/Goodbye.txt create mode 100644 30_Cube/csharp/Resources/HowMuch.txt create mode 100644 30_Cube/csharp/Resources/IllegalMove.txt create mode 100644 30_Cube/csharp/Resources/Instructions.txt create mode 100644 30_Cube/csharp/Resources/Introduction.txt create mode 100644 30_Cube/csharp/Resources/NextMove.txt create mode 100644 30_Cube/csharp/Resources/Resource.cs create mode 100644 30_Cube/csharp/Resources/TryAgain.txt create mode 100644 30_Cube/csharp/Resources/Wager.txt create mode 100644 30_Cube/csharp/Resources/YourMove.txt diff --git a/30_Cube/csharp/Cube.csproj b/30_Cube/csharp/Cube.csproj index d3fe4757..3870320c 100644 --- a/30_Cube/csharp/Cube.csproj +++ b/30_Cube/csharp/Cube.csproj @@ -6,4 +6,12 @@ enable enable + + + + + + + + diff --git a/30_Cube/csharp/Resources/Balance.txt b/30_Cube/csharp/Resources/Balance.txt new file mode 100644 index 00000000..1f6adffd --- /dev/null +++ b/30_Cube/csharp/Resources/Balance.txt @@ -0,0 +1 @@ +You now have {0} dollars. \ No newline at end of file diff --git a/30_Cube/csharp/Resources/Bang.txt b/30_Cube/csharp/Resources/Bang.txt new file mode 100644 index 00000000..1d924788 --- /dev/null +++ b/30_Cube/csharp/Resources/Bang.txt @@ -0,0 +1,4 @@ +******BANG****** +You lose! + + diff --git a/30_Cube/csharp/Resources/BetAgain.txt b/30_Cube/csharp/Resources/BetAgain.txt new file mode 100644 index 00000000..47c9fb8c --- /dev/null +++ b/30_Cube/csharp/Resources/BetAgain.txt @@ -0,0 +1 @@ +Tried to fool me; bet again \ No newline at end of file diff --git a/30_Cube/csharp/Resources/Bust.txt b/30_Cube/csharp/Resources/Bust.txt new file mode 100644 index 00000000..cd753d98 --- /dev/null +++ b/30_Cube/csharp/Resources/Bust.txt @@ -0,0 +1 @@ +You bust. diff --git a/30_Cube/csharp/Resources/Congratulations.txt b/30_Cube/csharp/Resources/Congratulations.txt new file mode 100644 index 00000000..3319c833 --- /dev/null +++ b/30_Cube/csharp/Resources/Congratulations.txt @@ -0,0 +1 @@ +Congratulations! diff --git a/30_Cube/csharp/Resources/Goodbye.txt b/30_Cube/csharp/Resources/Goodbye.txt new file mode 100644 index 00000000..0aa64192 --- /dev/null +++ b/30_Cube/csharp/Resources/Goodbye.txt @@ -0,0 +1,3 @@ +Tough luck! + +Goodbye. diff --git a/30_Cube/csharp/Resources/HowMuch.txt b/30_Cube/csharp/Resources/HowMuch.txt new file mode 100644 index 00000000..ff2bea20 --- /dev/null +++ b/30_Cube/csharp/Resources/HowMuch.txt @@ -0,0 +1 @@ +How much \ No newline at end of file diff --git a/30_Cube/csharp/Resources/IllegalMove.txt b/30_Cube/csharp/Resources/IllegalMove.txt new file mode 100644 index 00000000..ca8f96ba --- /dev/null +++ b/30_Cube/csharp/Resources/IllegalMove.txt @@ -0,0 +1,2 @@ + +Illegal move. You lose. diff --git a/30_Cube/csharp/Resources/Instructions.txt b/30_Cube/csharp/Resources/Instructions.txt new file mode 100644 index 00000000..e82ae51e --- /dev/null +++ b/30_Cube/csharp/Resources/Instructions.txt @@ -0,0 +1,24 @@ +This is a game in which you will be playing against the +random decision od the computer. The field of play is a +cube of side 3. Any of the 27 locations can be designated +by inputing three numbers such as 2,3,1. At the start, +you are automatically at location 1,1,1. The object of +the game is to get to location 3,3,3. One minor detail: +the computer will pick, at random, 5 locations at which +it will play land mines. If you hit one of these locations +you lose. One other details: you may move only one space +in one direction each move. For example: from 1,1,2 you +may move to 2,1,2 or 1,1,3. You may not change +two of the numbers on the same move. If you make an illegal +move, you lose and the computer takes the money you may +have bet on that round. + + +All Yes or No questions will be answered by a 1 for Yes +or a 0 (zero) for no. + +When stating the amount of a wager, print only the number +of dollars (example: 250) You are automatically started with +500 dollars in your account. + +Good luck! diff --git a/30_Cube/csharp/Resources/Introduction.txt b/30_Cube/csharp/Resources/Introduction.txt new file mode 100644 index 00000000..6299d19b --- /dev/null +++ b/30_Cube/csharp/Resources/Introduction.txt @@ -0,0 +1,6 @@ + Cube + Creative Computing Morristown, New Jersey + + + +Do you want to see the instructions? (Yes--1,No--0) diff --git a/30_Cube/csharp/Resources/NextMove.txt b/30_Cube/csharp/Resources/NextMove.txt new file mode 100644 index 00000000..4cbe5496 --- /dev/null +++ b/30_Cube/csharp/Resources/NextMove.txt @@ -0,0 +1 @@ +Next move: \ No newline at end of file diff --git a/30_Cube/csharp/Resources/Resource.cs b/30_Cube/csharp/Resources/Resource.cs new file mode 100644 index 00000000..c3ed10c7 --- /dev/null +++ b/30_Cube/csharp/Resources/Resource.cs @@ -0,0 +1,44 @@ +using System.Reflection; +using System.Runtime.CompilerServices; + +namespace Cube.Resources; + +internal static class Resource +{ + internal static class Streams + { + public static Stream Introduction => GetStream(); + public static Stream Instructions => GetStream(); + public static Stream Wager => GetStream(); + public static Stream IllegalMove => GetStream(); + public static Stream Bang => GetStream(); + public static Stream Bust => GetStream(); + public static Stream Congratulations => GetStream(); + public static Stream Goodbye => GetStream(); + } + + internal static class Prompts + { + public static string HowMuch => GetString(); + public static string BetAgain => GetString(); + public static string YourMove => GetString(); + public static string NextMove => GetString(); + public static string TryAgain => GetString(); + } + + internal static class Formats + { + public static string Balance => 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/30_Cube/csharp/Resources/TryAgain.txt b/30_Cube/csharp/Resources/TryAgain.txt new file mode 100644 index 00000000..9ccf358a --- /dev/null +++ b/30_Cube/csharp/Resources/TryAgain.txt @@ -0,0 +1 @@ +Do you want to try again \ No newline at end of file diff --git a/30_Cube/csharp/Resources/Wager.txt b/30_Cube/csharp/Resources/Wager.txt new file mode 100644 index 00000000..04720a7a --- /dev/null +++ b/30_Cube/csharp/Resources/Wager.txt @@ -0,0 +1 @@ +Want to make a wager diff --git a/30_Cube/csharp/Resources/YourMove.txt b/30_Cube/csharp/Resources/YourMove.txt new file mode 100644 index 00000000..5ea0c544 --- /dev/null +++ b/30_Cube/csharp/Resources/YourMove.txt @@ -0,0 +1,2 @@ + +It's your move: \ No newline at end of file From dbeeae81545afb9dbd2d4a27e94445617f8381ee Mon Sep 17 00:00:00 2001 From: Andrew Cooper Date: Sat, 23 Jul 2022 18:31:51 +1000 Subject: [PATCH 2/6] Update readmes --- 30_Cube/README.md | 24 ++++++++++++++++++++++++ 30_Cube/csharp/README.md | 9 +++++++++ 2 files changed, 33 insertions(+) diff --git a/30_Cube/README.md b/30_Cube/README.md index f99366a2..c220c8d4 100644 --- a/30_Cube/README.md +++ b/30_Cube/README.md @@ -16,3 +16,27 @@ http://www.vintage-basic.net/games.html #### Porting Notes (please note any difficulties or challenges in porting here) + +##### Randomization Logic + +The BASIC code uses an interesting technique for choosing the random coordinates for the mines. The first coordinate is +chosen like this: + +```basic +380 LET A=INT(3*(RND(X))) +390 IF A<>0 THEN 410 +400 LET A=3 +``` + +where line 410 is the start of a similar block of code for the next coordinate. The behaviour of `RND(X)` depends on the +value of `X`. If `X` is greater than zero then it returns a random value between 0 and 1. If `X` is zero it returns the +last random value generated, or 0 if no value has yet been generated. + +If `X` is 1, therefore, the first line above set `A` to 0, 1, or 2. The next 2 lines replace a 0 with a 3. The +replacement values varies for the different coordinates with the result that the random selection is biased towards a +specific set of points. If `X` is 0, the `RND` calls all return 0, so the coordinates are the known. It appears that +this technique was probably used to allow testing the game with a well-known set of locations for the mines. However, in +the code as it comes to us, the value of `X` is never set and is thus 0, so the mine locations are never randomized. + +The C# port implements the biased randomized mine locations, as seems to be the original intent, but includes a +command-line switch to enable the deterministic execution as well. diff --git a/30_Cube/csharp/README.md b/30_Cube/csharp/README.md index 4daabb5c..9100e6f8 100644 --- a/30_Cube/csharp/README.md +++ b/30_Cube/csharp/README.md @@ -1,3 +1,12 @@ Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html) Conversion to [Microsoft C#](https://docs.microsoft.com/en-us/dotnet/csharp/) + +#### Execution + +As noted in the main Readme file, the randomization code in the BASIC program has a switch (the variable `X`) that +allows the game to be run in a deterministic (non-random) mode. + +Running the C# port without command-line parameters will play the game with random mine locations. + +Running the port with a `-d` command-line switch will run the game with non-random mine locations. From d1cf340e9fe626e67ad939e9d8ddb95abc42d285 Mon Sep 17 00:00:00 2001 From: Andrew Cooper Date: Sat, 23 Jul 2022 18:42:03 +1000 Subject: [PATCH 3/6] Add Program and Game class --- 30_Cube/csharp/Game.cs | 18 ++++++++++++++++++ 30_Cube/csharp/Program.cs | 8 ++++++++ 2 files changed, 26 insertions(+) create mode 100644 30_Cube/csharp/Game.cs create mode 100644 30_Cube/csharp/Program.cs diff --git a/30_Cube/csharp/Game.cs b/30_Cube/csharp/Game.cs new file mode 100644 index 00000000..f02fa07e --- /dev/null +++ b/30_Cube/csharp/Game.cs @@ -0,0 +1,18 @@ +namespace Cube; + +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(Streams.Introduction); + } +} \ No newline at end of file diff --git a/30_Cube/csharp/Program.cs b/30_Cube/csharp/Program.cs new file mode 100644 index 00000000..f22208ac --- /dev/null +++ b/30_Cube/csharp/Program.cs @@ -0,0 +1,8 @@ +global using Games.Common.IO; +global using Games.Common.Randomness; + +global using static Cube.Resources.Resource; + +using Cube; + +new Game(new ConsoleIO(), new RandomNumberGenerator()).Play(); From 06386a6d5f5dc30d25a07b02b2f35bf18681221c Mon Sep 17 00:00:00 2001 From: Andrew Cooper Date: Sun, 24 Jul 2022 16:36:48 +1000 Subject: [PATCH 4/6] Add command-line switch --- 30_Cube/csharp/Program.cs | 4 +++- 30_Cube/csharp/README.md | 2 +- 30_Cube/csharp/ZerosGenerator.cs | 10 ++++++++++ 3 files changed, 14 insertions(+), 2 deletions(-) create mode 100644 30_Cube/csharp/ZerosGenerator.cs diff --git a/30_Cube/csharp/Program.cs b/30_Cube/csharp/Program.cs index f22208ac..803a569a 100644 --- a/30_Cube/csharp/Program.cs +++ b/30_Cube/csharp/Program.cs @@ -5,4 +5,6 @@ global using static Cube.Resources.Resource; using Cube; -new Game(new ConsoleIO(), new RandomNumberGenerator()).Play(); +IRandom random = args.Contains("--non-random") ? new ZerosGenerator() : new RandomNumberGenerator(); + +new Game(new ConsoleIO(), random).Play(); diff --git a/30_Cube/csharp/README.md b/30_Cube/csharp/README.md index 9100e6f8..7bea3d88 100644 --- a/30_Cube/csharp/README.md +++ b/30_Cube/csharp/README.md @@ -9,4 +9,4 @@ allows the game to be run in a deterministic (non-random) mode. Running the C# port without command-line parameters will play the game with random mine locations. -Running the port with a `-d` command-line switch will run the game with non-random mine locations. +Running the port with a `--non-random` command-line switch will run the game with non-random mine locations. diff --git a/30_Cube/csharp/ZerosGenerator.cs b/30_Cube/csharp/ZerosGenerator.cs new file mode 100644 index 00000000..4490f606 --- /dev/null +++ b/30_Cube/csharp/ZerosGenerator.cs @@ -0,0 +1,10 @@ +namespace Cube; + +internal class ZerosGenerator : IRandom +{ + public float NextFloat() => 0; + + public float PreviousFloat() => 0; + + public void Reseed(int seed) { } +} \ No newline at end of file From e6d03771069fa0fb55241ca5cca4ececca226782 Mon Sep 17 00:00:00 2001 From: Andrew Cooper Date: Sun, 24 Jul 2022 17:29:26 +1000 Subject: [PATCH 5/6] Add game loops and betting --- 30_Cube/csharp/Game.cs | 40 ++++++++++++++++++++++++++- 30_Cube/csharp/IOExtensions.cs | 20 ++++++++++++++ 30_Cube/csharp/Resources/NextMove.txt | 2 +- 3 files changed, 60 insertions(+), 2 deletions(-) create mode 100644 30_Cube/csharp/IOExtensions.cs diff --git a/30_Cube/csharp/Game.cs b/30_Cube/csharp/Game.cs index f02fa07e..70ada599 100644 --- a/30_Cube/csharp/Game.cs +++ b/30_Cube/csharp/Game.cs @@ -2,6 +2,7 @@ namespace Cube; internal class Game { + private const int _initialBalance = 500; private readonly IReadWrite _io; private readonly IRandom _random; @@ -14,5 +15,42 @@ internal class Game public void Play() { _io.Write(Streams.Introduction); + + if (_io.ReadNumber("") != 0) + { + _io.Write(Streams.Instructions); + } + + PlaySeries(_initialBalance); + + _io.Write(Streams.Goodbye); } -} \ No newline at end of file + + private void PlaySeries(float balance) + { + while (true) + { + var wager = _io.ReadWager(balance); + + var gameWon = PlayGame(); + + if (wager.HasValue) + { + balance = gameWon ? (balance + wager.Value) : (balance - wager.Value); + if (balance <= 0) + { + _io.Write(Streams.Bust); + return; + } + _io.WriteLine(Formats.Balance, balance); + } + + if (_io.ReadNumber(Prompts.TryAgain) != 1) { return; } + } + } + + private bool PlayGame() + { + return true; + } +} diff --git a/30_Cube/csharp/IOExtensions.cs b/30_Cube/csharp/IOExtensions.cs new file mode 100644 index 00000000..a221ca5d --- /dev/null +++ b/30_Cube/csharp/IOExtensions.cs @@ -0,0 +1,20 @@ +namespace Cube; + +internal static class IOExtensions +{ + internal static float? ReadWager(this IReadWrite io, float balance) + { + io.Write(Streams.Wager); + if (io.ReadNumber("") == 0) { return null; } + + var prompt = Prompts.HowMuch; + + while(true) + { + var wager = io.ReadNumber(prompt); + if (wager <= balance) { return wager; } + + prompt = Prompts.BetAgain; + } + } +} \ No newline at end of file diff --git a/30_Cube/csharp/Resources/NextMove.txt b/30_Cube/csharp/Resources/NextMove.txt index 4cbe5496..2c3c0611 100644 --- a/30_Cube/csharp/Resources/NextMove.txt +++ b/30_Cube/csharp/Resources/NextMove.txt @@ -1 +1 @@ -Next move: \ No newline at end of file +Next move: \ No newline at end of file From 7ffa89da08957fabdd96fd0ff6557295dca9a31e Mon Sep 17 00:00:00 2001 From: Andrew Cooper Date: Sun, 24 Jul 2022 18:17:04 +1000 Subject: [PATCH 6/6] Add main game logic --- 30_Cube/csharp/Game.cs | 48 +++++++++++++++++++++++++++ 30_Cube/csharp/IOExtensions.cs | 2 +- 30_Cube/csharp/RandomExtensions.cs | 14 ++++++++ 30_Cube/csharp/Resources/NextMove.txt | 2 +- 4 files changed, 64 insertions(+), 2 deletions(-) create mode 100644 30_Cube/csharp/RandomExtensions.cs diff --git a/30_Cube/csharp/Game.cs b/30_Cube/csharp/Game.cs index 70ada599..a06ec565 100644 --- a/30_Cube/csharp/Game.cs +++ b/30_Cube/csharp/Game.cs @@ -3,6 +3,13 @@ namespace Cube; internal class Game { private const int _initialBalance = 500; + private readonly IEnumerable<(int, int, int)> _seeds = new List<(int, int, int)> + { + (3, 2, 3), (1, 3, 3), (3, 3, 2), (3, 2, 3), (3, 1, 3) + }; + private readonly (float, float, float) _startLocation = (1, 1, 1); + private readonly (float, float, float) _goalLocation = (3, 3, 3); + private readonly IReadWrite _io; private readonly IRandom _random; @@ -51,6 +58,47 @@ internal class Game private bool PlayGame() { + var mineLocations = _seeds.Select(seed => _random.NextLocation(seed)).ToHashSet(); + var currentLocation = _startLocation; + var prompt = Prompts.YourMove; + + while (true) + { + var newLocation = _io.Read3Numbers(prompt); + + if (!MoveIsLegal(currentLocation, newLocation)) { return Lose(Streams.IllegalMove); } + + currentLocation = newLocation; + + if (currentLocation == _goalLocation) { return Win(Streams.Congratulations); } + + if (mineLocations.Contains(currentLocation)) { return Lose(Streams.Bang); } + + prompt = Prompts.NextMove; + } + } + + private bool Lose(Stream text) + { + _io.Write(text); + return false; + } + + private bool Win(Stream text) + { + _io.Write(text); return true; } + + private bool MoveIsLegal((float, float, float) from, (float, float, float) to) + => (to.Item1 - from.Item1, to.Item2 - from.Item2, to.Item3 - from.Item3) switch + { + ( > 1, _, _) => false, + (_, > 1, _) => false, + (_, _, > 1) => false, + (1, 1, _) => false, + (1, _, 1) => false, + (_, 1, 1) => false, + _ => true + }; } diff --git a/30_Cube/csharp/IOExtensions.cs b/30_Cube/csharp/IOExtensions.cs index a221ca5d..14f2a85e 100644 --- a/30_Cube/csharp/IOExtensions.cs +++ b/30_Cube/csharp/IOExtensions.cs @@ -17,4 +17,4 @@ internal static class IOExtensions prompt = Prompts.BetAgain; } } -} \ No newline at end of file +} diff --git a/30_Cube/csharp/RandomExtensions.cs b/30_Cube/csharp/RandomExtensions.cs new file mode 100644 index 00000000..ac05108e --- /dev/null +++ b/30_Cube/csharp/RandomExtensions.cs @@ -0,0 +1,14 @@ +namespace Cube; + +internal static class RandomExtensions +{ + internal static (float, float, float) NextLocation(this IRandom random, (int, int, int) bias) + => (random.NextCoordinate(bias.Item1), random.NextCoordinate(bias.Item2), random.NextCoordinate(bias.Item3)); + + private static float NextCoordinate(this IRandom random, int bias) + { + var value = random.Next(3); + if (value == 0) { value = bias; } + return value; + } +} \ No newline at end of file diff --git a/30_Cube/csharp/Resources/NextMove.txt b/30_Cube/csharp/Resources/NextMove.txt index 2c3c0611..4cbe5496 100644 --- a/30_Cube/csharp/Resources/NextMove.txt +++ b/30_Cube/csharp/Resources/NextMove.txt @@ -1 +1 @@ -Next move: \ No newline at end of file +Next move: \ No newline at end of file