From 41fdd87047eeeb87943c2bf4d64ccdc9e8a106bf Mon Sep 17 00:00:00 2001 From: Zev Spitz Date: Sat, 22 Jan 2022 22:27:27 +0200 Subject: [PATCH] Test for added animal; minor fixes --- .../Animal.Tests/EndOfInputsException.vb | 7 +++ 03_Animal/vbnet/Animal.Tests/MockConsole.vb | 11 ++--- 03_Animal/vbnet/Animal.Tests/TestContainer.vb | 44 +++++++++++++++++-- 03_Animal/vbnet/Animal/Game.vb | 36 ++++++++------- .../vbnet/Animal/Shared/ConsoleAdapter.vb | 11 ++--- .../vbnet/Animal/Shared/ConsoleAdapterBase.vb | 8 +++- 03_Animal/vbnet/README.md | 6 +++ 7 files changed, 88 insertions(+), 35 deletions(-) create mode 100644 03_Animal/vbnet/Animal.Tests/EndOfInputsException.vb diff --git a/03_Animal/vbnet/Animal.Tests/EndOfInputsException.vb b/03_Animal/vbnet/Animal.Tests/EndOfInputsException.vb new file mode 100644 index 00000000..b584e451 --- /dev/null +++ b/03_Animal/vbnet/Animal.Tests/EndOfInputsException.vb @@ -0,0 +1,7 @@ +''' +''' Indicates that there are no more inputs in the MockConsole. +''' We need this while testing, because otherwise the game loop will continue forever, waiting for a nonexistent input. +''' +Public Class EndOfInputsException + Inherits Exception +End Class diff --git a/03_Animal/vbnet/Animal.Tests/MockConsole.vb b/03_Animal/vbnet/Animal.Tests/MockConsole.vb index a78dc50f..7b21dfe2 100644 --- a/03_Animal/vbnet/Animal.Tests/MockConsole.vb +++ b/03_Animal/vbnet/Animal.Tests/MockConsole.vb @@ -37,26 +37,21 @@ Public Class MockConsole WriteString(value?.ToString) End Sub - Public Overrides Sub WriteLine(value As Object) - WriteString(value?.ToString) - WriteLine() - End Sub - Public Overrides Sub WriteLine() Lines.Add(("", False)) End Sub - Public Overrides Sub WriteCenteredLine(value As Object) + Public Overrides Sub WriteCenteredLines(value As Object) If Lines.Count = 0 Then Lines.Add(("", False)) Dim currentLast = Lines(Lines.Count - 1).line - If currentLast.Length > 0 Then Throw New InvalidOperationException("Can only write centered line if cursor is at start of line.") + If currentLast.Length > 0 Then Lines.Add(("", False)) WriteString(value?.ToString, True) WriteLine() End Sub Public Overrides Function ReadLine() As String ' Indicates the end of a test run, for programs which loop endlessly - If inputs.Count = 0 Then Throw New EndOfStreamException("End of inputs") + If inputs.Count = 0 Then Throw New EndOfInputsException Dim nextInput = inputs.Dequeue.Trim WriteLine(nextInput) diff --git a/03_Animal/vbnet/Animal.Tests/TestContainer.vb b/03_Animal/vbnet/Animal.Tests/TestContainer.vb index 3aed862a..f3d0c494 100644 --- a/03_Animal/vbnet/Animal.Tests/TestContainer.vb +++ b/03_Animal/vbnet/Animal.Tests/TestContainer.vb @@ -36,7 +36,7 @@ Public Class TestContainer Sub List(listResponse As String) Dim console As New MockConsole({listResponse}) Dim game As New Game(console) - Assert.Throws(Of EndOfStreamException)(Sub() game.BeginLoop()) + Assert.Throws(Of EndOfInputsException)(Sub() game.BeginLoop()) Assert.Equal( { "ANIMALS I ALREADY KNOW ARE:", @@ -52,7 +52,7 @@ Public Class TestContainer Sub YesVariant(yesVariant As String) Dim console As New MockConsole({yesVariant}) Dim game As New Game(console) - Assert.Throws(Of EndOfStreamException)(Sub() game.BeginLoop()) + Assert.Throws(Of EndOfInputsException)(Sub() game.BeginLoop()) Assert.Equal( { $"ARE YOU THINKING OF AN ANIMAL? {yesVariant}", @@ -68,7 +68,7 @@ Public Class TestContainer Sub NoVariant(noVariant As String) Dim console As New MockConsole({"y", noVariant}) Dim game As New Game(console) - Assert.Throws(Of EndOfStreamException)(Sub() game.BeginLoop()) + Assert.Throws(Of EndOfInputsException)(Sub() game.BeginLoop()) Assert.Equal( { $"DOES IT SWIM? {noVariant}", @@ -77,4 +77,42 @@ Public Class TestContainer console.Lines.Slice(-2, 0).Select(Function(x) x.line) ) End Sub + + ''' Test adding a new animal and using the new animal in the game + + Sub TestAddedAnimal() + Dim console As New MockConsole({ + "y", + "y", + "n", + "whale", + "is it a mammal?", + "y", + "y", + "y", + "y", + "y" + }) + Dim game As New Game(console) + Assert.Throws(Of EndOfInputsException)(Sub() game.BeginLoop()) + Assert.Equal( + { + "ARE YOU THINKING OF AN ANIMAL? y", + "DOES IT SWIM? y", + "IS IT A FISH? n", + "THE ANIMAL YOU WERE THINKING OF WAS A ? whale", + "PLEASE TYPE IN A QUESTION THAT WOULD DISTINGUISH A", + "WHALE FROM A FISH", + "is it a mammal?", + "FOR A WHALE THE ANSWER WOULD BE? y", + "ARE YOU THINKING OF AN ANIMAL? y", + "DOES IT SWIM? y", + "IS IT A MAMMAL? y", + "IS IT A WHALE? y", + "WHY NOT TRY ANOTHER ANIMAL?", + "ARE YOU THINKING OF AN ANIMAL? " + }, + console.Lines.Slice(9, 100).Select(Function(x) x.line) + ) + End Sub End Class diff --git a/03_Animal/vbnet/Animal/Game.vb b/03_Animal/vbnet/Animal/Game.vb index bca72005..2da0bbb1 100644 --- a/03_Animal/vbnet/Animal/Game.vb +++ b/03_Animal/vbnet/Animal/Game.vb @@ -46,8 +46,11 @@ Public Class Game Sub BeginLoop() ' Print the program heading - console.WriteCenteredLine("ANIMAL") - console.WriteCenteredLine("CREATIVE COMPUTING MORRISTOWN, NEW JERSEY") + console.WriteCenteredLines( +"ANIMAL +CREATIVE COMPUTING MORRISTOWN, NEW JERSEY") + + ' Print the program description console.Write( " @@ -64,9 +67,10 @@ THINK OF AN ANIMAL AND THE COMPUTER WILL TRY TO GUESS IT. Dim response = console.ReadLine If response = "list" Then ' List all the stored animals - console.WriteLine( + console.Write( " -ANIMALS I ALREADY KNOW ARE:") +ANIMALS I ALREADY KNOW ARE: +") ' We're using a ForEach extension method instead of the regular For Each loop to provide the index alongside the text root.DescendantTexts.ForEach(Sub(text, index) @@ -76,8 +80,9 @@ ANIMALS I ALREADY KNOW ARE:") If index > 0 AndAlso index Mod 4 = 0 Then console.WriteLine() console.Write($"{text.MaxLength(15),-15}") End Sub) - console.WriteLine( + console.Write( " + ") Continue Do End If @@ -105,25 +110,26 @@ ANIMALS I ALREADY KNOW ARE:") ' Now we're at an end branch console.Write($"IS IT A {currentBranch.Text}? ") ynResponse = GetYesNo() - If ynResponse Then ' Only if ynResponse = True will we go into this If Then + If ynResponse Then ' Only if ynResponse = True will we go into this If Then block + ' The computer guessed the animal; we can go back to the beginning of the game console.WriteLine("WHY NOT TRY ANOTHER ANIMAL?") Continue Do End If - ' Get the new animal + ' Get the new animal from the user console.Write("THE ANIMAL YOU WERE THINKING OF WAS A ? ") - Dim newAnimal = console.ReadLine + Dim newAnimal = console.ReadLine.ToUpperInvariant ' Get the question used to distinguish the new animal from the current end branch - console.WriteLine( -$"PLEASE TYPE IN A QUESTION THAT WOULD DISTINGUISH A -{newAnimal} FROM A {currentBranch.Text}") - Dim newQuestion = console.ReadLine + console.Write( +$"PLEASE TYPE IN A QUESTION THAT WOULD DISTINGUISH A +{newAnimal} FROM A {currentBranch.Text} +") + Dim newQuestion = console.ReadLine.ToUpperInvariant ' Get the answer to that question, for the new animal - ' for the old animal, the answer would be the opposite - console.Write( -$"FOR A {newAnimal} THE ANSWER WOULD BE ? ") + ' for the old animal, the answer will be the opposite + console.Write($"FOR A {newAnimal} THE ANSWER WOULD BE? ") Do ynResponse = GetYesNo() Loop While ynResponse Is Nothing diff --git a/03_Animal/vbnet/Animal/Shared/ConsoleAdapter.vb b/03_Animal/vbnet/Animal/Shared/ConsoleAdapter.vb index f49394e0..c5c68f58 100644 --- a/03_Animal/vbnet/Animal/Shared/ConsoleAdapter.vb +++ b/03_Animal/vbnet/Animal/Shared/ConsoleAdapter.vb @@ -5,18 +5,15 @@ Console.Write(value) End Sub - Public Overrides Sub WriteLine(value As Object) - Console.WriteLine(value) - End Sub - Public Overrides Sub WriteLine() Console.WriteLine() End Sub - Public Overrides Sub WriteCenteredLine(value As Object) - If Console.CursorLeft <> 0 Then Throw New InvalidOperationException("Can only write centered line if cursor is at start of line.") + Public Overrides Sub WriteCenteredLines(value As Object) + If Console.CursorLeft <> 0 Then WriteLine() Dim toWrite = If(value?.ToString, "") - Console.WriteLine($"{Space((Console.WindowWidth - toWrite.Length) \ 2)}{toWrite}") + Write($"{Space((Console.WindowWidth - toWrite.Length) \ 2)}{toWrite}") + WriteLine() End Sub Public Overrides Function ReadLine() As String diff --git a/03_Animal/vbnet/Animal/Shared/ConsoleAdapterBase.vb b/03_Animal/vbnet/Animal/Shared/ConsoleAdapterBase.vb index 4c9166bf..b08a59ce 100644 --- a/03_Animal/vbnet/Animal/Shared/ConsoleAdapterBase.vb +++ b/03_Animal/vbnet/Animal/Shared/ConsoleAdapterBase.vb @@ -1,9 +1,13 @@ Public MustInherit Class ConsoleAdapterBase Public MustOverride Sub Write(value As Object) - Public MustOverride Sub WriteLine(value As Object) Public MustOverride Sub WriteLine() - Public MustOverride Sub WriteCenteredLine(value As Object) + Public MustOverride Sub WriteCenteredLines(value As Object) ''' Implementations should always return a String without leading or trailing whitespace, never Nothng Public MustOverride Function ReadLine() As String + + Public Sub WriteLine(value As Object) + Write(value) + WriteLine() + End Sub End Class diff --git a/03_Animal/vbnet/README.md b/03_Animal/vbnet/README.md index 98b702c7..9ac8afe5 100644 --- a/03_Animal/vbnet/README.md +++ b/03_Animal/vbnet/README.md @@ -1,3 +1,9 @@ Original BASIC source [downloaded from Vintage Basic](http://www.vintage-basic.net/games.html) Conversion to [Visual Basic .NET](https://en.wikipedia.org/wiki/Visual_Basic_.NET) + +This takes some inspiration from the [C# port of Animal](https://github.com/zspitz/basic-computer-games/tree/main/03_Animal/csharp). + +The `Game` class takes a console abstraction (`ConsoleAdapterBase`), which could also be used for different UIs, such as WinForms or a web page. +This solution also has an xUnit tests project. +Responses can be entered in any capitalization, but animals and the distinguishing question will be converted to uppercase.