mirror of
https://github.com/coding-horror/basic-computer-games.git
synced 2025-12-25 20:34:32 -08:00
Update AceyDucey.vb
- reduce nested do/loop and if/else statements by utilizing subs and functions.
This commit is contained in:
242
01_Acey_Ducey/vbnet/AceyDucey.vb
Normal file
242
01_Acey_Ducey/vbnet/AceyDucey.vb
Normal 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
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user