diff --git a/01_Acey_Ducey/vbnet/AceyDucey.vb b/01_Acey_Ducey/vbnet/AceyDucey.vb new file mode 100644 index 00000000..8727e6b2 --- /dev/null +++ b/01_Acey_Ducey/vbnet/AceyDucey.vb @@ -0,0 +1,242 @@ +Public Class AceyDucey + ''' + ''' Create a single instance of the Random class to be used + ''' throughout the program. + ''' + Private ReadOnly Property Rnd As New Random() + + ''' + ''' Define a varaible to store the the player balance.
+ ''' Defaults to 0 + '''
+ ''' + ''' Since is a value type, and no value + ''' has been explicitly set, the default value of the type is used. + ''' + Private _balance As Integer + + Public Sub New() + DisplayIntroduction() + End Sub + + ''' + ''' Play multiple games of Acey Ducey until the player chooses to quit. + ''' + 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 + + ''' + ''' Play a game of Acey Ducey, which ends when the player balance reaches 0 + ''' + 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 + + ''' + ''' Play one turn of Acey Ducey + ''' + ''' + ''' A turn consists of displaying to cards, making a wager + ''' and determining the result (win/lose) + ''' + 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 + + ''' + ''' Get two cards in ascending order + ''' + ''' + ''' The original version generates two cards (A and B) + ''' If A is greater than or equal to B, both cards are regenerated. + '''

+ ''' 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) + '''
+ 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 + + ''' + ''' Get a random number (card) ranked 2 to 14 + ''' + Private Function GetCard() As Integer + Return Rnd.Next(2, 15) + End Function + + ''' + ''' Display the face value of the card + ''' + 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 + + ''' + ''' Prompt the user to make a bet + ''' + ''' + ''' The function will not return until a valid bet is made.
+ ''' is used to validate that the user input is a valid + '''
+ 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 + + ''' + ''' Prompt the user to try again + ''' + ''' + ''' This function will not return until a valid reponse is given + ''' + 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 + + ''' + ''' Display the opening title and instructions + ''' + ''' + ''' Refer to + ''' + ''' Interpolated Strings + ''' documentation for the use of $ and { } with strings + ''' + 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 diff --git a/01_Acey_Ducey/vbnet/Program.vb b/01_Acey_Ducey/vbnet/Program.vb index bfa518ca..2f82fd60 100644 --- a/01_Acey_Ducey/vbnet/Program.vb +++ b/01_Acey_Ducey/vbnet/Program.vb @@ -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. ''' 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