Animal (VB.NET) implementation

This commit is contained in:
Zev Spitz
2022-01-20 10:56:19 +02:00
parent dc64d34a3c
commit a8165eec0e
7 changed files with 219 additions and 0 deletions

View File

@@ -4,5 +4,6 @@
<RootNamespace>Animal</RootNamespace>
<TargetFramework>net6.0</TargetFramework>
<LangVersion>16.9</LangVersion>
<OptionStrict>On</OptionStrict>
</PropertyGroup>
</Project>

28
03_Animal/vbnet/Branch.vb Normal file
View File

@@ -0,0 +1,28 @@
Public Class Branch
Public Property Text As String
Public ReadOnly Property IsEnd As Boolean
Get
Return Yes Is Nothing AndAlso No Is Nothing
End Get
End Property
Public Property Yes As Branch
Public Property No As Branch
Public Iterator Function DescendantTexts() As IEnumerable(Of String)
If Yes IsNot Nothing Then
Yield Yes.Text
For Each childText In Yes.DescendantTexts
Yield childText
Next
End If
If No IsNot Nothing Then
Yield No.Text
For Each childText In No.DescendantTexts
Yield childText
Next
End If
End Function
End Class

126
03_Animal/vbnet/Game.vb Normal file
View File

@@ -0,0 +1,126 @@
Option Compare Text
Public Class Game
Private Shared ReadOnly YesNoResponses As New Dictionary(Of String, Boolean)(StringComparer.InvariantCultureIgnoreCase) From {
{"yes", True},
{"y", True},
{"true", True},
{"t", True},
{"1", True},
{"no", False},
{"n", False},
{"false", False},
{"f", False},
{"0", False}
}
ReadOnly console As ConsoleAdapterBase
ReadOnly root As New Branch With {
.Text = "DOES IT SWIM?",
.Yes = New Branch With {.Text = "FISH"},
.No = New Branch With {.Text = "BIRD"}
}
''' <summary>Reduces a string or console input to True, False or Nothing. Case-insensitive.</summary>
''' <param name="s">Optional String to reduce via the same logic. If not passed in, will use console.ReadLine</param>
''' <returns>
''' Returns True for a "yes" response (yes, y, true, t, 1) and False for a "no" response (no, n, false, f, 0).<br/>
''' Returns Nothing if the response doesn't match any of these.
''' </returns>
Private Function GetYesNo(Optional s As String = Nothing) As Boolean?
s = If(s, console.ReadLine)
Dim ret As Boolean
If YesNoResponses.TryGetValue(s, ret) Then Return ret
Return Nothing
End Function
Sub New(console As ConsoleAdapterBase)
If console Is Nothing Then Throw New ArgumentNullException(NameOf(console))
Me.console = console
End Sub
Sub BeginLoop()
console.WriteCenteredLine("ANIMAL")
console.WriteCenteredLine("CREATIVE COMPUTING MORRISTOWN, NEW JERSEY")
console.Write(
"
PLAY 'GUESS THE ANIMAL'
THINK OF AN ANIMAL AND THE COMPUTER WILL TRY TO GUESS IT.
")
Do
console.Write("ARE YOU THINKING OF AN ANIMAL? ")
Dim response = console.ReadLine
If response = "list" Then
console.WriteLine(
"
ANIMALS I ALREADY KNOW ARE:")
root.DescendantTexts.ForEach(Sub(text, index)
If index > 0 AndAlso index Mod 4 = 0 Then console.WriteLine()
console.Write($"{text.MaxLength(15),-15}")
End Sub)
console.WriteLine(
"
")
Continue Do
End If
Dim ynResponse = GetYesNo(response)
If ynResponse Is Nothing OrElse Not ynResponse Then Continue Do
Dim currentBranch = root
Do While Not currentBranch.IsEnd
console.Write($"{currentBranch.Text} ")
Do
ynResponse = GetYesNo()
Loop While ynResponse Is Nothing
currentBranch = If(
ynResponse,
currentBranch.Yes,
currentBranch.No
)
Loop
'at end
console.Write($"IS IT A {currentBranch.Text}? ")
ynResponse = GetYesNo()
If ynResponse Then ' No difference between False or Nothing
console.WriteLine("WHY NOT TRY ANOTHER ANIMAL?")
Continue Do
End If
console.Write("THE ANIMAL YOU WERE THINKING OF WAS A ? ")
Dim newAnimal = console.ReadLine
console.WriteLine(
$"PLEASE TYPE IN A QUESTION THAT WOULD DISTINGUISH A
{newAnimal} FROM A {currentBranch.Text}")
Dim newQuestion = console.ReadLine
console.Write(
$"FOR A {newAnimal} THE ANSWER WOULD BE ? ")
Do
ynResponse = GetYesNo()
Loop While ynResponse Is Nothing
Dim newBranch = New Branch With {.Text = newAnimal}
Dim currentBranchCopy = New Branch With {.Text = currentBranch.Text}
currentBranch.Text = newQuestion
If ynResponse Then
currentBranch.Yes = newBranch
currentBranch.No = currentBranchCopy
Else
currentBranch.No = newBranch
currentBranch.Yes = currentBranchCopy
End If
' TODO how do we exit?
Loop
End Sub
End Class

View File

@@ -0,0 +1,6 @@
Module Program
Sub Main()
Dim game As New Game(New ConsoleAdapter)
game.BeginLoop()
End Sub
End Module

View File

@@ -0,0 +1,28 @@
Public Class ConsoleAdapter
Inherits ConsoleAdapterBase
Public Overrides Sub Write(value As Object)
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)
Dim toWrite = If(value?.ToString, "")
Console.WriteLine($"{Space((Console.WindowWidth - toWrite.Length) \ 2)}{toWrite}")
End Sub
Public Overrides Function ReadLine() As String
Dim response As String
Do
response = Console.ReadLine
Loop While response Is Nothing
Return response.Trim
End Function
End Class

View File

@@ -0,0 +1,9 @@
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)
''' <summary>Implementations should always return a String without leading or trailing whitespace, never Nothng</summary>
Public MustOverride Function ReadLine() As String
End Class

View File

@@ -0,0 +1,21 @@
Imports System.Runtime.CompilerServices
Public Module Extensions
<Extension> Public Sub ForEach(Of T)(src As IEnumerable(Of T), action As Action(Of T, Integer))
Dim index As Integer
For Each x In src
action(x, index)
index += 1
Next
End Sub
<Extension> Public Function MaxLength(s As String, value As Integer) As String
If s Is Nothing Then Return Nothing
Return s.Substring(0, Math.Min(s.Length, value))
End Function
<Extension> Public Function ForceEndsWith(s As String, toAppend As String) As String
If Not s.EndsWith(toAppend, StringComparison.OrdinalIgnoreCase) Then s += toAppend
Return s
End Function
End Module