mirror of
https://github.com/coding-horror/basic-computer-games.git
synced 2025-12-22 07:10:42 -08:00
Test for added animal; minor fixes
This commit is contained in:
7
03_Animal/vbnet/Animal.Tests/EndOfInputsException.vb
Normal file
7
03_Animal/vbnet/Animal.Tests/EndOfInputsException.vb
Normal file
@@ -0,0 +1,7 @@
|
||||
''' <summary>
|
||||
''' <para>Indicates that there are no more inputs in the MockConsole.</para>
|
||||
''' We need this while testing, because otherwise the game loop will continue forever, waiting for a nonexistent input.
|
||||
''' </summary>
|
||||
Public Class EndOfInputsException
|
||||
Inherits Exception
|
||||
End Class
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
''' <summary>Test adding a new animal and using the new animal in the game</summary>
|
||||
<Fact>
|
||||
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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
''' <summary>Implementations should always return a String without leading or trailing whitespace, never Nothng</summary>
|
||||
Public MustOverride Function ReadLine() As String
|
||||
|
||||
Public Sub WriteLine(value As Object)
|
||||
Write(value)
|
||||
WriteLine()
|
||||
End Sub
|
||||
End Class
|
||||
|
||||
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user