Update AceyDucey.vb

- reduce nested do/loop and if/else statements by utilizing subs and functions.
This commit is contained in:
0phios
2022-01-21 14:19:16 -04:00
parent 0c5277815b
commit 961c762145
2 changed files with 248 additions and 163 deletions

View File

@@ -0,0 +1,242 @@
Public Class AceyDucey
''' <summary>
''' Create a single instance of the Random class to be used
''' throughout the program.
''' </summary>
Private ReadOnly Property Rnd As New Random()
''' <summary>
''' Define a varaible to store the the player balance. <br/>
''' Defaults to 0
''' </summary>
''' <remarks>
''' Since <see cref="Integer"/> is a value type, and no value
''' has been explicitly set, the default value of the type is used.
''' </remarks>
Private _balance As Integer
Public Sub New()
DisplayIntroduction()
End Sub
''' <summary>
''' Play multiple games of Acey Ducey until the player chooses to quit.
''' </summary>
Public Sub Play()
Do
PlayGame()
Loop While TryAgain() 'Loop (play again) based on the Boolean value returned by TryAgain
Console.WriteLine("O.K., HOPE YOU HAD FUN!")
End Sub
''' <summary>
''' Play a game of Acey Ducey, which ends when the player balance reaches 0
''' </summary>
Private Sub PlayGame()
_balance = 100 'At the start of the game, set the player balance to 100
Console.WriteLine()
Console.WriteLine($"YOU NOW HAVE {_balance} DOLLARS.")
Do
PlayTurn()
Loop While _balance > 0 'Continue playing while the user has a balance
Console.WriteLine()
Console.WriteLine("SORRY, FRIEND, BUT YOU BLEW YOUR WAD.")
End Sub
''' <summary>
''' Play one turn of Acey Ducey
''' </summary>
''' <remarks>
''' A turn consists of displaying to cards, making a wager
''' and determining the result (win/lose)
''' </remarks>
Private Sub PlayTurn()
Console.WriteLine()
Console.WriteLine("HERE ARE YOUR NEXT TWO CARDS: ")
Dim cards = GetOrderedCards()
For Each card In cards
DisplayCard(card)
Next
Dim wager As Integer = GetWager()
Dim finalCard As Integer = GetCard()
If wager = 0 Then
Console.WriteLine("CHICKEN!!")
Return
End If
DisplayCard(finalCard)
Console.WriteLine()
'''Check if the value of the final card is between the first and second cards.
'''
'''The use of AndAlso is used to short-circuit the evaluation of the IF condition.
'''Short-circuiting means that both sides of the condition do not need to be
'''evaluated. In this case, if the left criteria returns FALSE, the right criteria
'''is ignored and the evaluation result is returned as FALSE.
'''
'''This works because AndAlso requires both condition to return TRUE in order to be
'''evaluated as TRUE. If the first condition is FALSE we already know the evaluation result.
If finalCard >= cards.First() AndAlso finalCard <= cards.Last() Then
Console.WriteLine("YOU WIN!!!")
_balance += wager 'Condensed version of _balance = _balance + wager
Else
Console.WriteLine("SORRY, YOU LOSE.")
_balance -= wager 'Condensed version of _balance = _balance - wager
End If
End Sub
''' <summary>
''' Get two cards in ascending order
''' </summary>
''' <remarks>
''' The original version generates two cards (A and B)
''' If A is greater than or equal to B, both cards are regenerated.
''' <br/><br/>
''' This version generates the two cards, but only regenerates A
''' if A is equal to B. The cards are then returned is ascending order,
''' ensuring that A is less than B (maintaining the original end result)
''' </remarks>
Private Function GetOrderedCards() As Integer()
'''When declaring fixed size arrays in VB.NET you declare the MAX INDEX of the array
'''and NOT the SIZE (number of elements) of the array.
'''As such, card(1) gives you and array with index 0 and index 1, which means
'''the array stores two elements and not one
Dim cards(1) As Integer
cards(0) = GetCard()
cards(1) = GetCard()
'Perform this action as long as the first card is equal to the second card
While cards(0) = cards(1)
cards(0) = GetCard()
End While
Array.Sort(cards) 'Sort the values in ascending order
Return cards
End Function
''' <summary>
''' Get a random number (card) ranked 2 to 14
''' </summary>
Private Function GetCard() As Integer
Return Rnd.Next(2, 15)
End Function
''' <summary>
''' Display the face value of the card
''' </summary>
Private Sub DisplayCard(card As Integer)
Dim output As String
Select Case card
Case 2 To 10
output = card.ToString()
Case 11
output = "JACK"
Case 12
output = "QUEEN"
Case 13
output = "KING"
Case 14
output = "ACE"
Case Else
Throw New ArgumentOutOfRangeException(NameOf(card), "Value must be between 2 and 14")
End Select
Console.WriteLine(output)
End Sub
''' <summary>
''' Prompt the user to make a bet
''' </summary>
''' <remarks>
''' The function will not return until a valid bet is made. <br/>
''' <see cref="Int32.TryParse(String, ByRef Integer)"/> is used to validate that the user input is a valid <see cref="Integer"/>
''' </remarks>
Private Function GetWager() As Integer
Dim wager As Integer
Do
Console.WriteLine()
Console.Write("WHAT IS YOUR BET? ")
Dim input As String = Console.ReadLine()
'''Determine if the user input is an Integer
'''If it is an Integer, store the value in the variable wager
If Not Integer.TryParse(input, wager) Then
Console.WriteLine("SORRY, I DID'T QUITE GET THAT.")
Continue Do 'restart the loop
End If
'Prevent the user from betting more than their current balance
If _balance < wager Then
Console.WriteLine("SORRY, MY FRIEND, BUT YOU BET TOO MUCH.")
Console.WriteLine($"YOU HAVE ONLY {_balance} DOLLARS TO BET.")
Continue Do 'restart the loop
End If
'Prevent the user from betting negative values
If wager < 0 Then
Console.WriteLine("FUNNY GUY! YOU CANNOT MAKE A NEGATIVE BET.")
Continue Do 'restart the loop
End If
Exit Do 'If we get to this line, exit the loop as all above validations passed
Loop
Return wager
End Function
''' <summary>
''' Prompt the user to try again
''' </summary>
''' <remarks>
''' This function will not return until a valid reponse is given
''' </remarks>
Private Function TryAgain() As Boolean
Dim response As String
Do
Console.Write("TRY AGAIN (YES OR NO) ")
response = Console.ReadLine()
If response.Equals("YES", StringComparison.OrdinalIgnoreCase) Then Return True
If response.Equals("NO", StringComparison.OrdinalIgnoreCase) Then Return False
Console.WriteLine("SORRY, I DID'T QUITE GET THAT.")
Loop
End Function
''' <summary>
''' Display the opening title and instructions
''' </summary>
''' <remarks>
''' Refer to
''' <see href="https://docs.microsoft.com/en-us/dotnet/visual-basic/programming-guide/language-features/strings/interpolated-strings">
''' Interpolated Strings
''' </see> documentation for the use of $ and { } with strings
''' </remarks>
Private Sub DisplayIntroduction()
Console.WriteLine($"{Space((Console.WindowWidth \ 2) - 10)}ACEY DUCEY CARD GAME")
Console.WriteLine($"{Space((Console.WindowWidth \ 2) - 21)}CREATIVE COMPUTING MORRISTOWN, NEW JERSEY")
Console.WriteLine("")
Console.WriteLine("")
Console.WriteLine("ACEY-DUCEY IS PLAYED IN THE FOLLOWING MANNER")
Console.WriteLine("THE DEALER (COMPUTER) DEALS TWO CARDS FACE UP")
Console.WriteLine("YOU HAVE AN OPTION TO BET OR NOT BET DEPENDING")
Console.WriteLine("ON WHETHER OR NOT YOU FEEL THE CARD WILL HAVE")
Console.WriteLine("A VALUE BETWEEN THE FIRST TWO.")
Console.WriteLine("IF YOU DO NOT WANT TO BET, INPUT A 0")
Console.WriteLine("")
End Sub
End Class

View File

@@ -6,173 +6,16 @@ Imports System
''' The structural changes primarily consist of replacing the many GOTOs with
''' Do/Loop constructs to force the continual execution of the program.
'''
''' Because modern Basic allows multi-line If/Then blocks, many GOTO jumps were
''' able to be eliminated and the logic was able to be moved to more relevant areas,
''' For example, the increment/decrement of the player's balance could be in the same
''' area as the notification of win/loss.
''' Some modern improvements were added, primarily the inclusion of a multiple
''' subroutines and functions, which eliminates repeated logic and reduces
''' then need for nested loops.
'''
''' Some modern improvements were added, primarily the inclusion of a function, which
''' eliminated a thrice-repeated block of logic to display the card value. The archaic
''' RND function is greatly simplified with the .NET Framework's Random class.
''' The archaic RND function is greatly simplified with the .NET Framework's Random class.
'''
''' Elementary comments are provided for non-programmers or novices.
''' </summary>
Module Program
Sub Main(args As String())
' These are the variables that will hold values during the program's execution
Dim input As String
Dim rnd As New Random ' You can create a new instance of an object during declaration
Dim currentBalance As Integer = 100 ' You can set a initial value at declaration
Dim currentWager As Integer
Dim cardA, cardB, cardC As Integer ' You can specify multiple variables of the same type in one declaration statement
' Display the opening title and instructions
' Use a preceding $ to insert calculated values within the string using {}
Console.WriteLine($"{Space((Console.WindowWidth \ 2) - 10)}ACEY DUCEY CARD GAME")
Console.WriteLine($"{Space((Console.WindowWidth \ 2) - 21)}CREATIVE COMPUTING MORRISTOWN, NEW JERSEY")
Console.WriteLine("")
Console.WriteLine("")
Console.WriteLine("ACEY-DUCEY IS PLAYED IN THE FOLLOWING MANNER")
Console.WriteLine("THE DEALER (COMPUTER) DEALS TWO CARDS FACE UP")
Console.WriteLine("YOU HAVE AN OPTION TO BET OR NOT BET DEPENDING")
Console.WriteLine("ON WHETHER OR NOT YOU FEEL THE CARD WILL HAVE")
Console.WriteLine("A VALUE BETWEEN THE FIRST TWO.")
Console.WriteLine("IF YOU DO NOT WANT TO BET, INPUT A 0")
Do ' This loop continues as long as the player wants to keep playing
Do ' This loop continues as long as the player has money to play
Console.WriteLine("")
Console.WriteLine($"YOU NOW HAVE {currentBalance} DOLLARS.")
Console.WriteLine("")
Console.WriteLine("HERE ARE YOUR NEXT TWO CARDS:")
' We need to ensure that card B is a higher value for our later comparison,
' so we will loop until we have two cards that meet this criteria
Do
cardA = rnd.Next(2, 14)
cardB = rnd.Next(2, 14)
Loop While cardA > cardB
' We use a function to display the text value of the numeric card value
' because we do this 3 times and a function reduces repetition of code
Console.WriteLine(DisplayCard(cardA))
Console.WriteLine(DisplayCard(cardB))
Do ' This loop continues until the player provides a valid wager value
Console.WriteLine("")
Console.WriteLine("WHAT IS YOUR BET")
currentWager = 0
input = Console.ReadLine
' Any input from the console is a string, but we require a number.
' Test the input to make sure it is a numeric value.
If Integer.TryParse(input, currentWager) Then
' Test to ensure the player has not wagered more than their balance
If currentWager > currentBalance Then
Console.WriteLine("SORRY, MY FRIEND, BUT YOU BET TOO MUCH.")
Console.WriteLine($"YOU HAVE ONLY {currentBalance} DOLLARS TO BET.")
Else
' The player has provided a numeric value that is less/equal to their balance,
' exit the loop and continue play
Exit Do
End If ' check player balance
End If ' check numeric input
Loop ' wager loop
' If the player is wagering, draw the third card, otherwise, mock them.
If currentWager > 0 Then
cardC = rnd.Next(2, 14)
Console.WriteLine(DisplayCard(cardC))
' The effort we made to have two cards in numeric order earlier makes this check easier,
' otherwise we would have to have a second check in the opposite direction
If cardC < cardA OrElse cardC >= cardB Then
Console.WriteLine("SORRY, YOU LOSE")
currentBalance -= currentWager ' Shorthand code to decrement a number (currentBalance=currentBalance - currentWager)
Else
Console.WriteLine("YOU WIN!!!")
currentBalance += currentWager ' Shorthand code to increment a number (currentBalance=currentBalance + currentWager)
End If
Else
Console.WriteLine("CHICKEN!!")
Console.WriteLine("")
End If
Loop While currentBalance > 0 ' loop as long as the player has money
' At this point, the player has no money (currentBalance=0). Inform them of such.
Console.WriteLine("")
Console.WriteLine("SORRY, FRIEND, BUT YOU BLEW YOUR WAD.")
Console.WriteLine("")
Console.WriteLine("")
' We will loop to ensure the player provides some answer.
Do
Console.WriteLine("TRY AGAIN (YES OR NO)")
Console.WriteLine("")
input = Console.ReadLine
Loop While String.IsNullOrWhiteSpace(input)
' We will assume that the player wants to play again only if they answer yes.
' (yeah and ya are valid as well, because we only check the first letter)
If input.Substring(0, 1).Equals("y", StringComparison.CurrentCultureIgnoreCase) Then ' This allows upper and lower case to be entered.
currentBalance = 100 ' Reset the players balance before restarting
Else
' Exit the outer loop which will end the game.
Exit Do
End If
Loop ' The full game loop
Console.WriteLine("O.K., HOPE YOU HAD FUN!")
Sub Main()
Call New AceyDucey().Play()
End Sub
' This function is called for each of the 3 cards used in the game.
' The input and the output are both consistent, making it a good candidate for a function.
Private Function DisplayCard(value As Integer) As String
' We check the value of the input and run a block of code for whichever
' evaluation matches
Select Case value
Case 2 To 10 ' Case statements can be ranges of values, also multiple values (Case 2,3,4,5,6,7,8,9,10)
Return value.ToString
Case 11
Return "JACK"
Case 12
Return "QUEEN"
Case 13
Return "KING"
Case 14
Return "ACE"
End Select
' Although we have full knowledge of the program and never plan to send an invalid
' card value, it's important to provide a message for the next developer who won't
Throw New ArgumentOutOfRangeException("Card value must be between 2 and 14")
End Function
End Module