diff --git a/03_Animal/vbnet/Animal.vbproj b/03_Animal/vbnet/Animal.vbproj index f54e4e3d..f01d7b62 100644 --- a/03_Animal/vbnet/Animal.vbproj +++ b/03_Animal/vbnet/Animal.vbproj @@ -4,5 +4,6 @@ Animal net6.0 16.9 + On diff --git a/03_Animal/vbnet/Branch.vb b/03_Animal/vbnet/Branch.vb new file mode 100644 index 00000000..6734e39d --- /dev/null +++ b/03_Animal/vbnet/Branch.vb @@ -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 diff --git a/03_Animal/vbnet/Game.vb b/03_Animal/vbnet/Game.vb new file mode 100644 index 00000000..3bc2b2f6 --- /dev/null +++ b/03_Animal/vbnet/Game.vb @@ -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"} + } + + ''' Reduces a string or console input to True, False or Nothing. Case-insensitive. + ''' Optional String to reduce via the same logic. If not passed in, will use console.ReadLine + ''' + ''' Returns True for a "yes" response (yes, y, true, t, 1) and False for a "no" response (no, n, false, f, 0).
+ ''' Returns Nothing if the response doesn't match any of these. + '''
+ 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 diff --git a/03_Animal/vbnet/Program.vb b/03_Animal/vbnet/Program.vb new file mode 100644 index 00000000..adb34932 --- /dev/null +++ b/03_Animal/vbnet/Program.vb @@ -0,0 +1,6 @@ +Module Program + Sub Main() + Dim game As New Game(New ConsoleAdapter) + game.BeginLoop() + End Sub +End Module diff --git a/03_Animal/vbnet/Shared/ConsoleAdapter.vb b/03_Animal/vbnet/Shared/ConsoleAdapter.vb new file mode 100644 index 00000000..132f1968 --- /dev/null +++ b/03_Animal/vbnet/Shared/ConsoleAdapter.vb @@ -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 diff --git a/03_Animal/vbnet/Shared/ConsoleAdapterBase.vb b/03_Animal/vbnet/Shared/ConsoleAdapterBase.vb new file mode 100644 index 00000000..4c9166bf --- /dev/null +++ b/03_Animal/vbnet/Shared/ConsoleAdapterBase.vb @@ -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) + + ''' Implementations should always return a String without leading or trailing whitespace, never Nothng + Public MustOverride Function ReadLine() As String +End Class diff --git a/03_Animal/vbnet/Shared/Extensions.vb b/03_Animal/vbnet/Shared/Extensions.vb new file mode 100644 index 00000000..83d1914b --- /dev/null +++ b/03_Animal/vbnet/Shared/Extensions.vb @@ -0,0 +1,21 @@ +Imports System.Runtime.CompilerServices + +Public Module Extensions + 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 + + 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 + + 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