mirror of
https://github.com/coding-horror/basic-computer-games.git
synced 2025-12-22 23:26:40 -08:00
Merge pull request #531 from zspitz/main
Some dotnet script improvements; Animal (VB.NET)
This commit is contained in:
@@ -10,6 +10,11 @@ public static class Extensions {
|
||||
src.Select(x => selector(x.Item1, x.Item2, x.Item3));
|
||||
public static IEnumerable<(T1, T2, int)> WithIndex<T1, T2>(this IEnumerable<(T1, T2)> src) => src.Select((x, index) => (x.Item1, x.Item2, index));
|
||||
|
||||
public static bool None<T>(this IEnumerable<T> src, Func<T, bool>? predicate = null) =>
|
||||
predicate is null ?
|
||||
!src.Any() :
|
||||
!src.Any(predicate);
|
||||
|
||||
public static bool IsNullOrWhitespace([NotNullWhen(false)] this string? s) => string.IsNullOrWhiteSpace(s);
|
||||
|
||||
[return: NotNullIfNotNull("path")]
|
||||
|
||||
@@ -17,6 +17,7 @@ var actions = new (Action action, string description)[] {
|
||||
(multipleProjs, "Output multiple project files"),
|
||||
(checkProjects, "Check .csproj/.vbproj files for target framework, nullability etc."),
|
||||
(checkExecutableProject, "Check that there is at least one executable project per port"),
|
||||
(noCodeFiles, "Output ports without any code files"),
|
||||
(printPortInfo, "Print info about a single port"),
|
||||
|
||||
(generateMissingSlns, "Generate solution files when missing"),
|
||||
@@ -87,7 +88,7 @@ void printInfos() {
|
||||
}
|
||||
|
||||
void missingSln() {
|
||||
var data = infos.Where(x => !x.Slns.Any()).ToArray();
|
||||
var data = infos.Where(x => x.Slns.None()).ToArray();
|
||||
foreach (var item in data) {
|
||||
WriteLine(item.LangPath);
|
||||
}
|
||||
@@ -98,7 +99,7 @@ void missingSln() {
|
||||
void unexpectedSlnName() {
|
||||
var counter = 0;
|
||||
foreach (var item in infos) {
|
||||
if (!item.Slns.Any()) { continue; }
|
||||
if (item.Slns.None()) { continue; }
|
||||
|
||||
var expectedSlnName = $"{item.GameName}.sln";
|
||||
if (item.Slns.Contains(Combine(item.LangPath, expectedSlnName), StringComparer.InvariantCultureIgnoreCase)) { continue; }
|
||||
@@ -125,7 +126,7 @@ void multipleSlns() {
|
||||
}
|
||||
|
||||
void missingProj() {
|
||||
var data = infos.Where(x => !x.Projs.Any()).ToArray();
|
||||
var data = infos.Where(x => x.Projs.None()).ToArray();
|
||||
foreach (var item in data) {
|
||||
WriteLine(item.LangPath);
|
||||
}
|
||||
@@ -136,7 +137,7 @@ void missingProj() {
|
||||
void unexpectedProjName() {
|
||||
var counter = 0;
|
||||
foreach (var item in infos) {
|
||||
if (!item.Projs.Any()) { continue; }
|
||||
if (item.Projs.None()) { continue; }
|
||||
|
||||
var expectedProjName = $"{item.GameName}.{item.ProjExt}";
|
||||
if (item.Projs.Contains(Combine(item.LangPath, expectedProjName))) { continue; }
|
||||
@@ -164,7 +165,7 @@ void multipleProjs() {
|
||||
}
|
||||
|
||||
void generateMissingSlns() {
|
||||
foreach (var item in infos.Where(x => !x.Slns.Any())) {
|
||||
foreach (var item in infos.Where(x => x.Slns.None())) {
|
||||
var result = RunProcess("dotnet", $"new sln -n {item.GameName} -o {item.LangPath}");
|
||||
WriteLine(result);
|
||||
|
||||
@@ -177,7 +178,7 @@ void generateMissingSlns() {
|
||||
}
|
||||
|
||||
void generateMissingProjs() {
|
||||
foreach (var item in infos.Where(x => !x.Projs.Any())) {
|
||||
foreach (var item in infos.Where(x => x.Projs.None())) {
|
||||
// We can't use the dotnet command to create a new project using the built-in console template, because part of that template
|
||||
// is a Program.cs / Program.vb file. If there already are code files, there's no need to add a new empty one; and
|
||||
// if there's already such a file, it might try to overwrite it.
|
||||
@@ -216,8 +217,8 @@ void generateMissingProjs() {
|
||||
|
||||
void checkProjects() {
|
||||
foreach (var info in infos) {
|
||||
WriteLine(info.LangPath);
|
||||
printProjectWarnings(info);
|
||||
WriteLine();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -231,13 +232,15 @@ void printProjectWarnings(PortInfo info) {
|
||||
nullable,
|
||||
implicitUsing,
|
||||
rootNamespace,
|
||||
langVersion
|
||||
langVersion,
|
||||
optionStrict
|
||||
) = (
|
||||
getValue(parent, "TargetFramework", "TargetFrameworks"),
|
||||
getValue(parent, "Nullable"),
|
||||
getValue(parent, "ImplicitUsings"),
|
||||
getValue(parent, "RootNamespace"),
|
||||
getValue(parent, "LangVersion")
|
||||
getValue(parent, "LangVersion"),
|
||||
getValue(parent, "OptionStrict")
|
||||
);
|
||||
|
||||
if (framework != "net6.0") {
|
||||
@@ -266,6 +269,9 @@ void printProjectWarnings(PortInfo info) {
|
||||
if (langVersion != "16.9") {
|
||||
warnings.Add($"LangVersion: {langVersion}");
|
||||
}
|
||||
if (optionStrict != "On") {
|
||||
warnings.Add($"OptionStrict: {optionStrict}");
|
||||
}
|
||||
}
|
||||
|
||||
if (warnings.Any()) {
|
||||
@@ -284,6 +290,15 @@ void checkExecutableProject() {
|
||||
}
|
||||
}
|
||||
|
||||
void noCodeFiles() {
|
||||
var qry = infos
|
||||
.Where(x => x.CodeFiles.None())
|
||||
.OrderBy(x => x.Lang);
|
||||
foreach (var item in qry) {
|
||||
WriteLine(item.LangPath);
|
||||
}
|
||||
}
|
||||
|
||||
void tryBuild() {
|
||||
// if has code files, try to build
|
||||
}
|
||||
|
||||
27
03_Animal/vbnet/Animal.Tests/Animal.Tests.vbproj
Normal file
27
03_Animal/vbnet/Animal.Tests/Animal.Tests.vbproj
Normal file
@@ -0,0 +1,27 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<RootNamespace>Animal.Tests</RootNamespace>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<IsPackable>false</IsPackable>
|
||||
<OptionStrict>On</OptionStrict>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.0.0" />
|
||||
<PackageReference Include="xunit" Version="2.4.1" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3">
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="coverlet.collector" Version="3.1.0">
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Animal\Animal.vbproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
65
03_Animal/vbnet/Animal.Tests/MockConsole.vb
Normal file
65
03_Animal/vbnet/Animal.Tests/MockConsole.vb
Normal file
@@ -0,0 +1,65 @@
|
||||
Imports System.IO
|
||||
|
||||
Public Class MockConsole
|
||||
Inherits ConsoleAdapterBase
|
||||
|
||||
Private inputs As Queue(Of String)
|
||||
Public ReadOnly Lines As New List(Of (line As String, centered As Boolean)) From {
|
||||
("", False)
|
||||
}
|
||||
|
||||
' TODO it's possible to clear all the lines, and we'd have to check once again in WriteString and WriteCenteredLine if there are any lines
|
||||
|
||||
Sub New(Inputs As IEnumerable(Of String))
|
||||
Me.inputs = New Queue(Of String)(Inputs)
|
||||
End Sub
|
||||
|
||||
Private Sub CheckLinesInitialized()
|
||||
If Lines.Count = 0 Then Lines.Add(("", False))
|
||||
End Sub
|
||||
|
||||
Private Sub WriteString(s As String, Optional centered As Boolean = False)
|
||||
If s Is Nothing Then Return
|
||||
CheckLinesInitialized()
|
||||
s.Split(Environment.NewLine).ForEach(Sub(line, index)
|
||||
If index = 0 Then
|
||||
Dim currentLast = Lines(Lines.Count - 1)
|
||||
' centered should never come from the current last line
|
||||
' if WriteCenteredLine is called, it immediately creates a new line
|
||||
Lines(Lines.Count - 1) = (currentLast.line + line, centered)
|
||||
Else
|
||||
Lines.Add((line, centered))
|
||||
End If
|
||||
End Sub)
|
||||
End Sub
|
||||
|
||||
Public Overrides Sub Write(value As Object)
|
||||
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)
|
||||
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.")
|
||||
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")
|
||||
|
||||
Dim nextInput = inputs.Dequeue.Trim
|
||||
WriteLine(nextInput)
|
||||
Return nextInput
|
||||
End Function
|
||||
End Class
|
||||
80
03_Animal/vbnet/Animal.Tests/TestContainer.vb
Normal file
80
03_Animal/vbnet/Animal.Tests/TestContainer.vb
Normal file
@@ -0,0 +1,80 @@
|
||||
Imports Xunit
|
||||
Imports Animal
|
||||
Imports System.IO
|
||||
|
||||
Public Class TestContainer
|
||||
Private Shared Function ResponseVariantExpander(src As IEnumerable(Of String)) As TheoryData(Of String)
|
||||
Dim theoryData = New TheoryData(Of String)
|
||||
src.
|
||||
SelectMany(Function(x) {x, x.Substring(0, 1)}).
|
||||
SelectMany(Function(x) {
|
||||
x,
|
||||
x.ToUpperInvariant,
|
||||
x.ToLowerInvariant,
|
||||
x.ToTitleCase,
|
||||
x.ToReverseCase
|
||||
}).
|
||||
Distinct.
|
||||
ForEach(Sub(x) theoryData.Add(x))
|
||||
Return theoryData
|
||||
End Function
|
||||
Private Shared YesVariantsThepryData As TheoryData(Of String) = ResponseVariantExpander({"yes", "true", "1"})
|
||||
Private Shared Function YesVariants() As TheoryData(Of String)
|
||||
Return YesVariantsThepryData
|
||||
End Function
|
||||
Private Shared NoVariantsThepryData As TheoryData(Of String) = ResponseVariantExpander({"no", "false", "0"})
|
||||
Private Shared Function NoVariants() As TheoryData(Of String)
|
||||
Return NoVariantsThepryData
|
||||
End Function
|
||||
|
||||
''' <summary>Test LIST variants</summary>
|
||||
<Theory>
|
||||
<InlineData("LIST")>
|
||||
<InlineData("list")>
|
||||
<InlineData("List")>
|
||||
<InlineData("lIST")>
|
||||
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.Equal(
|
||||
{
|
||||
"ANIMALS I ALREADY KNOW ARE:",
|
||||
"FISH BIRD "
|
||||
},
|
||||
console.Lines.Slice(-4, -2).Select(Function(x) x.line)
|
||||
)
|
||||
End Sub
|
||||
|
||||
'' <summary>Test YES variants</summary>
|
||||
<Theory>
|
||||
<MemberData(NameOf(YesVariants))>
|
||||
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.Equal(
|
||||
{
|
||||
$"ARE YOU THINKING OF AN ANIMAL? {yesVariant}",
|
||||
"DOES IT SWIM? "
|
||||
},
|
||||
console.Lines.Slice(-2, 0).Select(Function(x) x.line)
|
||||
)
|
||||
End Sub
|
||||
|
||||
'' <summary>Test NO variants</summary>
|
||||
<Theory>
|
||||
<MemberData(NameOf(NoVariants))>
|
||||
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.Equal(
|
||||
{
|
||||
$"DOES IT SWIM? {noVariant}",
|
||||
"IS IT A BIRD? "
|
||||
},
|
||||
console.Lines.Slice(-2, 0).Select(Function(x) x.line)
|
||||
)
|
||||
End Sub
|
||||
End Class
|
||||
@@ -1,22 +1,31 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 16
|
||||
VisualStudioVersion = 16.0.30114.105
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.0.32112.339
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "Animal", "Animal.vbproj", "{147D66D5-D817-4024-9447-9F5B9A6D2B7D}"
|
||||
Project("{778DAE3C-4631-46EA-AA77-85C1314464D9}") = "Animal", "Animal\Animal.vbproj", "{5517E4CE-BCF9-4D1F-9A17-B620C1B96B0D}"
|
||||
EndProject
|
||||
Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "Animal.Tests", "Animal.Tests\Animal.Tests.vbproj", "{3986C6A2-77D4-4F00-B3CF-F5736C623B1E}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{5517E4CE-BCF9-4D1F-9A17-B620C1B96B0D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{5517E4CE-BCF9-4D1F-9A17-B620C1B96B0D}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{5517E4CE-BCF9-4D1F-9A17-B620C1B96B0D}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{5517E4CE-BCF9-4D1F-9A17-B620C1B96B0D}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{3986C6A2-77D4-4F00-B3CF-F5736C623B1E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{3986C6A2-77D4-4F00-B3CF-F5736C623B1E}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{3986C6A2-77D4-4F00-B3CF-F5736C623B1E}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{3986C6A2-77D4-4F00-B3CF-F5736C623B1E}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{147D66D5-D817-4024-9447-9F5B9A6D2B7D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{147D66D5-D817-4024-9447-9F5B9A6D2B7D}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{147D66D5-D817-4024-9447-9F5B9A6D2B7D}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{147D66D5-D817-4024-9447-9F5B9A6D2B7D}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {88469A47-E30C-4763-A325-074101D16608}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
|
||||
@@ -4,5 +4,6 @@
|
||||
<RootNamespace>Animal</RootNamespace>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<LangVersion>16.9</LangVersion>
|
||||
<OptionStrict>On</OptionStrict>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
29
03_Animal/vbnet/Animal/Branch.vb
Normal file
29
03_Animal/vbnet/Animal/Branch.vb
Normal file
@@ -0,0 +1,29 @@
|
||||
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
|
||||
|
||||
' Allows walking all the descendants recursively
|
||||
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
|
||||
152
03_Animal/vbnet/Animal/Game.vb
Normal file
152
03_Animal/vbnet/Animal/Game.vb
Normal file
@@ -0,0 +1,152 @@
|
||||
Option Compare Text
|
||||
|
||||
Public Class Game
|
||||
' This Dictionary holds the corresponding value for each of the variants of "YES" and "NO" we accept
|
||||
' Note that the Dictionary is case-insensitive, meaning it maps "YES", "yes" and even "yEs" to True
|
||||
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
|
||||
|
||||
' The pre-initialized root branch
|
||||
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()
|
||||
' Print the program heading
|
||||
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
|
||||
' List all the stored animals
|
||||
console.WriteLine(
|
||||
"
|
||||
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)
|
||||
' We want to move to the next line after every four animals
|
||||
' But for the first animal, where the index is 0, 0 Mod 4 will also return 0
|
||||
' So we have to explicitly exclude the first animal
|
||||
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
|
||||
' Branches can either be questions, or end branches
|
||||
' We have to walk the questions, prompting each time for "yes" or "no"
|
||||
console.Write($"{currentBranch.Text} ")
|
||||
Do
|
||||
ynResponse = GetYesNo()
|
||||
Loop While ynResponse Is Nothing
|
||||
|
||||
' Depending on the answer, we'll follow either the branch at "Yes" or "No"
|
||||
currentBranch = If(
|
||||
ynResponse,
|
||||
currentBranch.Yes,
|
||||
currentBranch.No
|
||||
)
|
||||
Loop
|
||||
|
||||
' 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
|
||||
console.WriteLine("WHY NOT TRY ANOTHER ANIMAL?")
|
||||
Continue Do
|
||||
End If
|
||||
|
||||
' Get the new animal
|
||||
console.Write("THE ANIMAL YOU WERE THINKING OF WAS A ? ")
|
||||
Dim newAnimal = console.ReadLine
|
||||
|
||||
' 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
|
||||
|
||||
' 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 ? ")
|
||||
Do
|
||||
ynResponse = GetYesNo()
|
||||
Loop While ynResponse Is Nothing
|
||||
|
||||
' Create the new end branch for the new animal
|
||||
Dim newBranch = New Branch With {.Text = newAnimal}
|
||||
|
||||
' Copy over the current animal to another new end branch
|
||||
Dim currentBranchCopy = New Branch With {.Text = currentBranch.Text}
|
||||
|
||||
' Make the current branch into the distinguishing question
|
||||
currentBranch.Text = newQuestion
|
||||
|
||||
' Set the Yes and No branches of the current branch according to the answer
|
||||
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
|
||||
6
03_Animal/vbnet/Animal/Program.vb
Normal file
6
03_Animal/vbnet/Animal/Program.vb
Normal file
@@ -0,0 +1,6 @@
|
||||
Module Program
|
||||
Sub Main()
|
||||
Dim game As New Game(New ConsoleAdapter)
|
||||
game.BeginLoop()
|
||||
End Sub
|
||||
End Module
|
||||
29
03_Animal/vbnet/Animal/Shared/ConsoleAdapter.vb
Normal file
29
03_Animal/vbnet/Animal/Shared/ConsoleAdapter.vb
Normal file
@@ -0,0 +1,29 @@
|
||||
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)
|
||||
If Console.CursorLeft <> 0 Then Throw New InvalidOperationException("Can only write centered line if cursor is at start of line.")
|
||||
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
|
||||
9
03_Animal/vbnet/Animal/Shared/ConsoleAdapterBase.vb
Normal file
9
03_Animal/vbnet/Animal/Shared/ConsoleAdapterBase.vb
Normal 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
|
||||
50
03_Animal/vbnet/Animal/Shared/Extensions.vb
Normal file
50
03_Animal/vbnet/Animal/Shared/Extensions.vb
Normal file
@@ -0,0 +1,50 @@
|
||||
Imports System.Runtime.CompilerServices
|
||||
|
||||
Public Module Extensions
|
||||
<Extension> Public Sub ForEach(Of T)(src As IEnumerable(Of T), action As Action(Of T))
|
||||
For Each x In src
|
||||
action(x)
|
||||
Next
|
||||
End Sub
|
||||
<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
|
||||
|
||||
<Extension> Public Function ToTitleCase(s As String) As String
|
||||
If s Is Nothing Then Return Nothing
|
||||
Return Char.ToUpperInvariant(s(0)) + s.Substring(1).ToUpperInvariant
|
||||
End Function
|
||||
|
||||
' https://stackoverflow.com/a/3681580/111794
|
||||
<Extension> Public Function ToReverseCase(s As String) As String
|
||||
If s Is Nothing Then Return Nothing
|
||||
Return New String(s.Select(Function(c) If(
|
||||
Not Char.IsLetter(c),
|
||||
c,
|
||||
If(
|
||||
Char.IsUpper(c), Char.ToLowerInvariant(c), Char.ToUpperInvariant(c)
|
||||
)
|
||||
)).ToArray)
|
||||
End Function
|
||||
|
||||
' https://stackoverflow.com/a/58132204/111794
|
||||
<Extension> Public Function Slice(Of T)(lst As IList(Of T), start As Integer, [end] As Integer) As T()
|
||||
start = If(start >= 0, start, lst.Count + start)
|
||||
[end] = If([end] > 0, [end], lst.Count + [end])
|
||||
Return lst.Skip(start).Take([end] - start).ToArray
|
||||
End Function
|
||||
End Module
|
||||
@@ -3,7 +3,6 @@
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net5.0</TargetFramework>
|
||||
<RootNamespace>BasicComputerGames.Bagels</RootNamespace>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -2,8 +2,9 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<RootNamespace>banner</RootNamespace>
|
||||
<RootNamespace>Banner</RootNamespace>
|
||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||
<LangVersion>16.9</LangVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -2,8 +2,9 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<RootNamespace>batnum</RootNamespace>
|
||||
<RootNamespace>Batnum</RootNamespace>
|
||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||
<LangVersion>16.9</LangVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net5.0</TargetFramework>
|
||||
<RootNamespace>_21_calendar</RootNamespace>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -2,8 +2,9 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<RootNamespace>word</RootNamespace>
|
||||
<RootNamespace>Word</RootNamespace>
|
||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||
<LangVersion>16.9</LangVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
Reference in New Issue
Block a user