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.