From 4fa74a10e4ae87ce58890a1265c3c5674b76a22e Mon Sep 17 00:00:00 2001 From: Andrew Cooper Date: Wed, 9 Mar 2022 21:46:43 +1100 Subject: [PATCH] Add .Net usage README --- 00_Common/README.md | 3 + 00_Common/dotnet/README.md | 181 +++++++++++++++++++++++++++++++++++++ 2 files changed, 184 insertions(+) create mode 100644 00_Common/dotnet/README.md diff --git a/00_Common/README.md b/00_Common/README.md index 81a0b8e0..5b207e05 100644 --- a/00_Common/README.md +++ b/00_Common/README.md @@ -83,6 +83,9 @@ I propose to ignore this last behaviour - the interpreter crash - and instead tr of a quoted value. There are some additional behaviours to those shown above which can be seen in the unit tests for the C# implementation of the library. +Note also that BASIC numeric variables store a single-precision floating point value, so numeric input functions should +return a value of that type. + ### Implementation Notes The usage of the `INPUT` command in the BASIC code of the games was analysed, with the variables used designated as diff --git a/00_Common/dotnet/README.md b/00_Common/dotnet/README.md new file mode 100644 index 00000000..ac0b1bcc --- /dev/null +++ b/00_Common/dotnet/README.md @@ -0,0 +1,181 @@ +# Games.Common + +This is the common library for C# and VB.Net ports of the games. + +## Overview + +### Game Input/Output + +* `TextIO` is the main class which manages text input and output for a game. It take a `TextReader` and a `TextWriter` in +its constructor so it can be wired up in unit tests to test gameplay scenarios. +* `ConsoleIO` derives from `TextIO` and binds it to `System.Console.In` and `System.Console.Out`. +* `IReadWrite` is an interface implemented by `TextIO` which may be useful in some test scenarios. + +```csharp +public interface IReadWrite +{ + // Reads a float value from input. + float ReadNumber(string prompt); + + // Reads 2 float values from input. + (float, float) Read2Numbers(string prompt); + + // Reads 3 float values from input. + (float, float, float) Read3Numbers(string prompt); + + // Reads 4 float values from input. + (float, float, float, float) Read4Numbers(string prompt); + + // Read numbers from input to fill an array. + void ReadNumbers(string prompt, float[] values); + + // Reads a string value from input. + string ReadString(string prompt); + + // Reads 2 string values from input. + (string, string) Read2Strings(string prompt); + + // Writes a string to output. + void Write(string message); + + // Writes a string to output, followed by a new-line. + void WriteLine(string message = ""); + + // Writes a float to output, formatted per the BASIC interpreter, with leading and trailing spaces. + void Write(float value); + + // Writes a float to output, formatted per the BASIC interpreter, with leading and trailing spaces, + // followed by a new-line. + void WriteLine(float value); + + // Writes the contents of a Stream to output. + void Write(Stream stream);} +``` + +### Random Number Generation + +* `IRandom` is an interface that provides basic methods that parallel the 3 uses of BASIC's `RND(float)` function. +* `RandomNumberGenerator` is an implementation of `IRandom` built around `System.Random`. +* `IRandomExtensions` provides convenience extension methods for obtaining random numbers as `int` and also within a + given range. + +```csharp +public interface IRandom +{ + // Like RND(1), gets a random float such that 0 <= n < 1. + float NextFloat(); + + // Like RND(0), Gets the float returned by the previous call to NextFloat. + float PreviousFloat(); + + // Like RND(-x), Reseeds the random number generator. + void Reseed(int seed); +} +``` + +Extension methods on `IRandom`: + +```csharp +// Gets a random float such that 0 <= n < exclusiveMaximum. +float NextFloat(this IRandom random, float exclusiveMaximum); + +// Gets a random float such that inclusiveMinimum <= n < exclusiveMaximum. +float NextFloat(this IRandom random, float inclusiveMinimum, float exclusiveMaximum); + +// Gets a random int such that 0 <= n < exclusiveMaximum. +int Next(this IRandom random, int exclusiveMaximum); + +// Gets a random int such that inclusiveMinimum <= n < exclusiveMaximum. +int Next(this IRandom random, int inclusiveMinimum, int exclusiveMaximum); + +// Gets the previous unscaled float (between 0 and 1) scaled to a new range: +// 0 <= x < exclusiveMaximum. +float PreviousFloat(this IRandom random, float exclusiveMaximum); + +// Gets the previous unscaled float (between 0 and 1) scaled to a new range: +// inclusiveMinimum <= n < exclusiveMaximum. +float PreviousFloat(this IRandom random, float inclusiveMinimum, float exclusiveMaximum); + +// Gets the previous unscaled float (between 0 and 1) scaled to an int in a new range: +// 0 <= n < exclusiveMaximum. +int Previous(this IRandom random, int exclusiveMaximum); + +// Gets the previous unscaled float (between 0 and 1) scaled to an int in a new range: +// inclusiveMinimum <= n < exclusiveMaximum. +int Previous(this IRandom random, int inclusiveMinimum, int exclusiveMaximum); +``` + +## C\# Usage + +### Add Project Reference + +Add the `Games.Common` project as a reference to the game project. For example, here's the reference from the C\# port +of `86_Tower`: + +```xml + + + +``` + +### C# Game Input/Output usage + +A game can be encapsulated in a class which takes a `TextIO` instance in it's constructor: + +```csharp +public class Game +{ + private readonly TextIO _io; + + public Game(TextIO io) => _io = io; + + public void Play() + { + var name = _io.ReadString("What is your name"); + var (cats, dogs) = _io.Read2Number($"Hello, {name}, how many pets do you have (cats, dogs)"); + _io.WriteLine($"So, {cats + dogs} pets in total, huh?"); + } +} +``` + +Then the entry point of the game program would look something like: + +```csharp +var game = new Game(new ConsoleIO()); +game.Play(); +``` + +### C# Random Number Generator usage + +```csharp +var io = new ConsoleIO(); +var rng = new RandomNumberGenerator(); +io.WriteLine(rng.NextFloat()); // 0.1234, for example +io.WriteLine(rng.NextFloat()); // 0.6, for example +io.WriteLine(rng.PreviousFloat()); // 0.6, repeats previous +io.WriteLine(rng.PreviousFloat(0, 10)); // 6, repeats previous value, but scaled to new range +``` + +### C# Unit Test usage + +`TextIO` can be initialised with a `StringReader` and `StringWriter` to enable testing. For example, given the `Game` +class above: + +```csharp +var reader = new StringReader("Joe Bloggs\r\n4\n\r5"); +var writer = new StringWriter(); +var game = new Game(new TextIO(reader, writer)) + +game.Play(); + +writer.ToString().Should().BeEquivalentTo( + "What is your name? Hello, Joe Bloggs, how many pets do you have (cats, dogs)? ?? So, 9 pets in total, huh?"); +``` + +Note the lack of line breaks in the expected output, because during game play the line breaks come from the text input. + +Of course, `IReadWrite` can also be mocked for simple test scenarios. + +## VB.Net Usage + +*To be provided*