The larger your applications become, the more complicated your program code becomes and the more difficult things are to maintain. One effective way of making your programs easier to create and maintain is to break them up into small parts, which you can then use as building blocks to create a larger program. One way of accomplishing this is through procedures. You’ve used procedures in the development of most of the game applications you’ve already created in this book. In this chapter you will learn how to work with two types of procedures—subroutines and functions. You will learn how to pass data to your subroutines and functions for processing. You will also learn how to return data back from functions. You will also get the opportunity to put your newfound understanding of subroutines and functions to use in this chapter’s game project, the BASIC BlackJack game.
Specifically, you will learn how to
This chapter’s game project is the BASIC BlackJack game. This game is a light version of the popular BlackJack casino game that pits the player against the dealer (computer) in a virtual card game in which the object of the game is to come as close as possible to 21 without going over. This game, like many applications, begins by displaying a splash screen, as shown in Figure 7.1.
After a four-second delay, the splash screen closes and the game’s main window is displayed. An initial hand is automatically dealt for both the player and the dealer, as shown in Figure 7.2.
The object of the game is to get the highest possible hand without going over 21, which would cause the player to bust and automatically make the dealer a winner. The player is required to always play out her hand first. To get additional cards, the player must click on the Hit Me! button. The value of the player’s hand is automatically updated each time the Hit Me! button is clicked, as demonstrated in Figure 7.3.
The player may ask for as many cards as she wants, provided the value of her hand does not exceed 21. If the player’s hand goes over 21, she automatically busts and loses that hand. The player may stop adding cards to her hand at any time and allow the dealer to play out its hand by clicking on the Stay! button. The dealer’s method of play is very simple. It will continue to ask for a card as long as its hand is less than 17 or until it busts. Once its hand exceeds 17, assuming it has not busted, it stops adding new cards to its hand and holds.
If neither the player nor the dealer has busted, the game compares both hands to determine the results of the game. Figure 7.4 shows an example of a hand in which the player and the dealer have tied.
The player can start a new hand at the end of each round of play by clicking on the Play Again button. Again, play continues until the player or dealer busts or stays. Figure 7.5 shows an example of a hand in which the dealer has gone bust.
As your applications become larger, they become more complicated and difficult to maintain. One way of making your programs easier to create and maintain is to organize them into small parts, which can then be used as building blocks in the creation of a larger program. This is where procedures come into play.
A procedure is a collection of programming statements that can be called upon to execute from different locations within an application. Most programming languages support two types of procedures: subroutines and functions. A subroutine is a collection of one or more code statements that can be called upon to execute. A function is similar to a subroutine except that a function also has the ability to return a value back to the statement that called upon it. For example, subroutines and functions help to make application development easier by allowing you to break an application down into manageable sections, which you can develop one at a time.
In Chapter 3, “Creating Graphical User Interfaces,” you were introduced to the use of labels as a means of identifying locations within program code. By specifying a label as the event handler for a user interface control or by using the goto
or gosub
commands, you learned how to group and execute collections of code statements. However, as was stated in that chapter, this method of programming is frowned upon. Instead, professional programmers rely on subroutines and functions to group and execute related code statements.
Procedures provide programmers with several advantages. For starters, they make program development easier by letting programmers build programs in small chunks, each of which is designed to accomplish a specific task. Secondly, procedures allow programmers to reduce the amount of code required to develop applications by providing the ability to group commonly executed statements into a named collection, which can then be called upon to run over and over again as necessary. Organizing programming logic into separate procedures also helps to streamline programming logic and create modular code. This can also reduce errors.
Suppose, for example, you needed to create a program for a retail store that performs a number of different tasks, including the calculation of sales tax for each sale made in the store, and later you found the need to modify the program because the sales tax rate had changed. If you did not use procedures when initially developing the program, your job will be more difficult, especially if you are working on a large program. By organizing the program into procedures, you simplify its maintenance by grouping related code statements together. To change the part of the program that calculates the sales tax, all that you would have to do is to locate the procedure where the sales tax calculation is performed and make the appropriate modification. Thus, by placing related code statements into subroutines and functions, programmers can isolate different logical processes and reduce the chances that a change made in one part of a program might affect another part.
Subroutines begin with a declaration statement (sub
) and end with an end sub
statement. When called, subroutines execute, and, when complete, they return control back to the statement that called upon them. To define a subroutine within a Just BASIC application, you must use the following syntax.
sub SubroutineName Parameter1,... ParameterN statements end sub
SubroutineName
represents the name of the subroutine being defined. Subroutines can accept and process any number of arguments passed to them when called, as represented by Parameter1 ... ParameterN
(i.e., Parameter1 “through” ParameterN). statements
represents the statements that will be executed each time the subroutine is called.
Procedure names for both subroutines and functions must be unique throughout an application. You cannot assign the same name to more than one subroutine or function. Nor can you assign a subroutine or function name to a variable.
In many programming languages, including C++, REALbasic, and Just BASIC, programmers are required to declare the data type of any parameters defined in a procedure. Since Just BASIC only supports two data types, numeric and string, data type is identified by the absence or presence of the $
character at the end of the parameter name.
The following statements define a small subroutine that accepts two arguments and uses them in the formulation of a string, which is then displayed in a popup window.
sub HappyBirthday name$, age notice "Happy birthday, " + name$ + "! Today you are " + str$(age) _ + " years old." end sub
As you can see, the name assigned to the subroutine is HappyBirthday
. The first parameter defined by the subroutine is a string variable named name$
. The second parameter is a numerical variable named age
.
Within Just BASIC, subroutines can be called in either of two ways. The first way of calling a subroutine is as an event handler. The second is to explicitly call the subroutine using the call
command. Both of these options are explained in the sections that follow.
You have used procedures in most of the game projects that you have worked on so far. For example, in most of the chapter game projects, you have set up subroutines as event handlers that execute whenever the player clicks on user interface controls.
If you are defining a subroutine that will be used as the event handler for an interface control, you must always define at least one parameter. The reason for this is that when called in this manner, the subroutine is passed an argument containing a string representing the name of the control that called the subroutine. If you forget to add a parameter representing this argument, you’ll get an error. For example, in the Guess My Number game that you created in Chapter 6, you created a subroutine called ClosePlay
, as shown next. As you can see, this subroutine includes a parameter definition named handle$
. handle$
is really just a string variable that receives the name of the user interface control whose event triggered the execution of the subroutine.
sub ClosePlay handle$ 'Get confirmation before displaying a hit confirm "Are you sure you want quit?"; answer$ if answer$ = "yes" then 'The player clicked on Yes close #play 'Close the #play window 'See if the #help window is open and close it if it is if helpOpen$ = "True" then call CloseHelpWindow "X" 'Close the #help window end if end 'Terminate the game end if end sub
If you did not include the required parameter as part of this subroutine’s definition, your program would crash and generate an error similar to the one shown in Figure 7.6.
Figure 7.6. Just BASIC has generated an error because a subroutine was called and passed an argument for which it did not have a corresponding parameter defined.
In addition to setting up procedures as event handlers for window and user interface controls, you can also execute them using the call
command. The syntax for the call
command is outlined here:
call ProcedureName Parameter1, ... ParameterN
ProcedureName
represents the procedure to be executed and Parameter1...ParameterN
represent one or more optional arguments that may be passed to the procedure. For example, the following statement could be used to call on a procedure named HappyBirthday
and pass it two arguments (a string and a number), as demonstrated here:
An argument is a value, literal or variable, that is passed to a subroutine or function for processing. A parameter is a variable defined within a subroutine or function that maps up to an argument that the subroutine or function is called on to execute.
call HappyBirthday "William", 9
When executed, the HappyBirthday
subroutine maps each argument that is passed to it to one of its parameters. The data type of each argument that is passed must match the data type of each parameter defined by the subroutine or function; otherwise, an error will occur.
sub HappyBirthday name$, age notice "Happy birthday " + name$ + "! Today you are " + str$(age) _ + " years old." end sub
In many programming languages, including Visual Basic, you can call on a procedure by simply keying in its name, as demonstrated here:
HappyAnniversary()
Here, a function named HappyAnniversary()
has been called upon to execute. In Visual Basic, the call
command is regarded as a legacy statement that is seldom used anymore.
If necessary, you can terminate the execution of a subroutine at any time using the following statement.
exit sub
For example, the following subroutine, taken from the Guess My Number game, used the exit sub
command in a number of different places to terminate the execution of the AnalyzeGuess
subroutine when validating and processing the player’s guess.
sub AnalyzeGuess handle$ print #play.textbox1, "!contents? playerGuess" if playerGuess < 1 or playerGuess > 100 then print #play.textbox4, "Your guess must be between 1 and 100. Try again." print #play.textbox1, "" print #play.textbox1, "!setfocus"; exit sub end if if playerGuess < secretNumber then guessCount = guessCount + 1 print #play.textbox4, "Your guess was too low. Try again." print #play.textbox1, "" print #play.textbox1, "!setfocus"; exit sub end if if playerGuess > secretNumber then guessCount = guessCount + 1 print #play.textbox4, "Your guess was too high. Try again." print #play.textbox1, "" print #play.textbox1, "!setfocus"; exit sub end if if playerGuess = secretNumber then notice "Guess My Number" + chr$(13) + "Game over, you win!" guessCount = guessCount + 1 noOfGamesPlayed = noOfGamesPlayed + 1 avgNoGuesses = guessCount / noOfGamesPlayed print #play.textbox3, avgNoGuesses print #play.textbox2, noOfGamesPlayed print #play.textbox1, "" print #play.button3, "!enable" print #play.button3, "!setfocus"; print #play.textbox1, "!disable" print #play.button1, "!disable" print #play.textbox4, "" exit sub end if end sub
As you can see, the exit sub
command was used four times in four separate if...then
statements to prematurely terminate the subroutine’s execution as soon as any of the tested conditions evaluated as being true.
As has already been stated, a function is almost exactly the same thing as a subroutine, except that functions also provide programmers with the ability to return a value back to the statement that called upon them to execute. To define a function within a Just BASIC application, you must use the following syntax.
function FunctionName(Parameter1,... ParameterN) statements end sub
FunctionName
represents the name of the function being defined. A function can accept and process any number of arguments passed to it, as represented by Parameter1...ParameterN. statements
represent the statements that will be executed each time the subroutine is executed.
You can pass as many arguments to your subroutines and functions as you want, provided that you have the same number of corresponding parameters defined inside the subroutine or function.
The following code provides an example of a function that generates a random number in the range of 1 to 10 and then returns the number that is generated back to the statement that called upon the function to execute.
function GetRandomNumber()
RandomNumber = int(rnd(1)*10) + 1
GetRandomNumber = RandomNumber
end function
As the previous example demonstrates, to return a value from a function, you must create a variable within the function that has the same name as the function and assign it the value that you want returned.
Different programming languages have different ways of returning values from functions. For example, in Visual Basic you can return a value from a function using the same approach as Just BASIC (e.g., creating a variable with the same name as the function and assigning the value to be returned to it). REALbasic, on the other hand, requires that you use the return
command to explicitly specify the value that is to be returned. Therefore, in REALbasic you would return a value by specifying the return
keyword followed by the literal or variable value to be returned, as demonstrated here:
return RandomNumber
You can call upon the GetRandomNumber
function shown in the previous example from anywhere inside the application that contains it using the following statement.
gameNumber = GetRandomNumber()
Of course, instead of returning values to and from functions, you could use global variables, accessing them whenever necessary from within your subroutines and functions. However, it is a much better programming practice to limit the scope of your variables as much as possible and to pass arguments to your functions and return any required result back to calling statements. Limiting variable scope in this manner helps to prevent the unintentional modification of variable values and helps make your program code easier to maintain.
Like subroutines, you can terminate the execution of a function at any time using the following statement.
exit function
As an example of how to use the exit function
statement, consider the following example.
prompt "Give me a number and I'll double it!"; x notice DoubleNumber(x) function DoubleNumber(x) DoubleNumber = x + x if DoubleNumber > 100 then notice "Sorry, I don't like large numbers! How about 2 + 2 instead?" DoubleNumber = 4 exit function end if end function
Here, in this somewhat silly example, a function named DoubleNumber
has been defined. It accepts a single argument in the form of a numeric value, which is mapped to a parameter named x
. Next, the function adds the value of x
to itself, thus creating a new numeric value that is twice the value of the argument that was passed to the function. By assigning this new value to a variable with the same name as the function, this value will be passed back to the statement that called upon the function as long as the if...then
statement that follows proves false. The if...then
statement executes only in the event that the value being returned is greater than 100. If this is the case, then the notice
command is used to inform the user that it does not appreciate being asked to calculate a large number and the value of DoubleNumber
is instead reassigned an arbitrary value of 4, which is then returned to the statement that called upon the function.
Variables defined within a subroutine or function are local in scope, meaning that they cannot be accessed outside of the subroutine or function where they are defined. However, you can share access to data throughout your Just BASIC applications by declaring your variables as global. Alternatively, you can pass variables as arguments to subroutines and functions where they can then be acted upon. You can also pass arguments by reference, in which case your subroutine or function can modify the value assigned to any argument that is passed to it.
By default, any argument passed to a subroutine or function is passed by value, meaning that the subroutine or function may do whatever it wants to the value of the argument within itself but any modification of the value is not visible outside of the procedure. To get a better feeling for how this works, consider the following example.
age = 10 call ChangeValue age notice "You are " + str$(age) + " years old." sub ChangeValue x x = x + 5 end sub
Here, a numeric variable named age
is defined and assigned a value of 10
. Next, the call
command is used to execute a subroutine named ChangeValue
to which age
is passed as an argument. Within the subroutine, the value of age
is mapped to a variable named x
. The value of x
is then increased by 5
. However, since the argument passed to the subroutine was passed by value, the modification of x
’s value had no effect on the value of age
, as proven when the subroutine ends and the notice
statement is executed, showing that the value assigned to age is still 5
.
Arguments passed by reference affect the value of the argument passed, both within the procedure and outside of it. To see how this works, take a look at the following example.
age = 10
call ChangeValue age
notice "You are " + str$(age) + " years old."
sub ChangeValue byref x
x = x + 5
end sub
As you can see, in this example the subroutine definition includes the byref
keyword. As a result, any change made to the value of x
inside the subroutine is also reflected outside of the subroutine in the value of age
, which is proved when the notice
statement executes and age
is shown to have a value of 10 and not 5.
Although you can certainly create your own custom functions to perform all kinds of operations, you will find that most programming languages include their own collection of predefined functions that you can call upon to perform common calculations or tasks. By taking advantage of these built-in functions, you reduce the size and complexity of your programs. You also reduce the amount of time required to create your applications in the first place.
You have already seen a number of Just BASIC’s functions in action. For example, you have used the rnd()
function to generate random numbers, the int()
function to convert a floatingpoint number to an integer, and the str$()
function to convert a numeric value to a string value. Table 7.1 shows a list of functions provided by Just BASIC. You’ll see a number of these functions in use throughout this book. For any function that is not covered, you can learn more about it by referencing Just BASIC’s Help.
Table 7.1. Just BASIC’s Built-in Functions
Function Name | Function Name | Function Name |
---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
All right, that is enough about subroutines and functions for now. Let’s turn your attention back to the development of this chapter’s game project, the BASIC BlackJack game. This game will consist of two windows—one will serve as a splash screen and the other will serve as the main window. In order to create the splash screen, you will need to download copies of the two bitmap image files shown in Figure 7.7 from this book’s companion website.
Figure 7.7. Reviewing copies of the two bitmap images displayed on the game’s opening splash screen window.
You can download copies of these two bitmap images along with the source code for this application from this book’s companion website at www.courseptr.com/downloads.
The design of the BASIC BlackJack game relies heavily on the use of subroutines and functions. You will follow the same pattern already established through the development of previous game projects. In total, you will create the BASIC BlackJack game in 12 steps, as outlined here:
Create and document a new BASIC file.
Define global variables and execute the DisplaySplash
subroutine.
Create the DisplaySplash
subroutine.
Create the CloseSplashWindow
subroutine.
Create the ManageGamePlay
subroutine.
Create the DealCard
subroutine.
Create the DealerTurn
subroutine.
Create the RestartGame
subroutine.
Create the DealOpeningHand
subroutine.
Create the GetRandomNumber
function.
Create the ResetGame
subroutine.
Create the ClosePlay
subroutine.
Following the same pattern that has been established with the creation of all Just BASIC game applications that you have worked on in this book, let’s begin by adding a few comment statements to the beginning of the program file.
' ************************************************************************* ' ' Script Name: BlackJack.bas (The BASIC BlackJack Game) ' Version: 1.0 ' Author: Jerry Lee Ford, Jr. ' Date: March 1, 2007 ' ' Description: This game is a Just BASIC implementation of the BlackJack ' casino card game which pits the player against the ' computer (dealer). ' ' *************************************************************************
Next, since the application will not make use of its default text window, add the following statement to the end of the program file telling Just BASIC to suppress its display at runtime.
nomainwin 'Suppress the display of the default text window
The next step in the development of the BASIC BlackJack game is to define a pair of global variables. The dealerCard
variable will be used to keep track of the numeric value of the dealer’s hand during game play. Likewise, the playerCard
variable is used to keep track of the value of the player’s hand. Next, the call
command is used to execute the DisplaySplash
subroutine. This subroutine is responsible for displaying the game’s splash screen and then initializing game play.
global dealerCard, playerCard 'Assign default values to global variables call DisplaySplash 'Call on the subroutine that displays the game's 'splash screen wait 'Pause the application and wait for the player's instruction
The code statements that make up the DisplaySplash
subroutine are shown next and should be added to the end of the program file.
'This subroutine displays a splash screen at game start-up sub DisplaySplash loadbmp "AceImage", "C:imagesAce.bmp" 'Load the specified bitmap 'file into memory loadbmp "JackImage", "C:imagesJack.bmp" 'Load the specified bitmap 'file into memory 'Define the format of statictext controls displayed on the window statictext #splash.statictext1, "B A S I C B L A C K J A C K", _ 30, 30, 440, 20 statictext #splash.statictext2, "Copyright 2007", 210, 60, 80, 20 'Add two graphicbox controls to the #splash window graphicbox #splash.gboxAce, 65, 120, 77, 116 graphicbox #splash.gboxJack, 175, 120, 77, 116 'Open the window with no frame and a handle of #splash open "BASIC BlackJack" for window_nf as #splash 'Set the font type, size, and attributes print #splash.statictext1, "!font Arial 14 bold" 'Use the flush command to prevent the contents of the graphicbox control 'from being overwritten when the window is first generated print #splash.gboxAce, "flush" print #splash.gboxJack, "flush" 'Display the pre-loaded bitmap images in the graphicbox control print #splash.gboxAce, "drawbmp AceImage 1 1" print #splash.gboxJack, "drawbmp JackImage 1 1" 'Use the flush command to prevent the contents of the graphicbox controls 'from begin overwritten if the user opens or moves another window on top 'of the #splash window print #splash.gboxAce, "flush" print #splash.gboxJack, "flush" 'Wait 4 thousand milliseconds (4 seconds) and then call upon the 'subroutine that closes the game's splash screen timer 4000, CloseSplashWindow end sub
The DisplaySplash
subroutine begins by using the loadbmp
command to pre-load graphic bitmap files into memory. Statictext and graphicbox controls are then added to the window. The window is then displayed using the open
command and assigned a handle of #splash
. The rest of the subroutine consists of statements that perform the following set of actions:
Set the window’s font type, size, and attributes.
Use the flush
command to prevent bitmap files from being overwritten.
Use the drawbmp
command to display the bitmap images.
Use the timer
command to pause the subroutine’s execution for four seconds before calling on the CloseSplashWindow
subroutine.
The timer
command uses the computer’s internal hardware timer to provide programmers with the ability to implement programming logic that relies on the passage of time. For example, you might use the timer
command to drive the execution of programming statements that control the display of animation in a computer game.
The syntax of the timer
command is shown here:
timer delay, EventHandler
delay is a placeholder that represents the amount of time, in milliseconds, that the timer command will wait before executing the specified EventHandler (e.g., call to a subroutine, function, or label). One second is equivalent to 1,000 milliseconds. So, to tell the timer to wait five seconds, you would specify a value of 5,000 for delay.
The CloseSplashWindow
subroutine, shown next, is responsible for closing the game’s splash screen window.
'This subroutine closes the game's splash screen window sub CloseSplashWindow timer 0 'Turn the timer off close #splash 'Close the #splash window call ManageGamePlay 'Call on the subroutine that manages game play end sub
The first statement in the subroutine disables the execution of the timer
command by passing a value of zero. Next, the close
command is used to close the #splash
window. Finally, the call
command is used to execute the ManageGamePlay
subroutine, which is responsible for initializing game play.
The code statements that make up the ManageGamePlay
subroutine are shown next and should be added to the end of the program file. This subroutine will display the game’s main window and facilitate game play.
'This subroutine displays the game board and controls interaction with the 'player sub ManageGamePlay WindowWidth = 400 'Set the width of the window to 500 pixels WindowHeight = 400 'Set the height of the window to 500 pixels 'Define the format of statictext controls displayed on the window statictext #play.statictext1, "B A S I C B L A C K J A C K", _ 30, 20, 440, 30 statictext #play.statictext2, "Copyright 2007", 285, 50, 80, 20 statictext #play.statictext3, "Player's Hand", 50, 110, 70, 20 statictext #play.statictext4, "Dealer's Hand", 270, 110, 70, 20 'Define textbox control that will be used to display data textbox #play.textbox1, 50, 140, 70, 50 textbox #play.textbox2, 270, 140, 70, 50 textbox #play.textbox3, 50, 280, 290, 50 'Add button controls to the window button #play.button1 "Hit Me!", DealCard, UL, 30, 210, 50, 30 button #play.button2 "Stay!", DealerTurn, UL, 90, 210, 50, 30 button #play.button3 "Play Again", RestartGame, UL, 270, 210, 70, 30 'Open the window with no frame and a handle of #play open "BASIC BlackJack" for window_nf as #play 'Set up the trapclose event for the window print #play, "trapclose ClosePlay" Call DealOpeningHand 'Call the subroutine that deals the player's and 'dealer's initial hands 'Set the font type, size, and attributes print #play.statictext1, "!font Arial 18 bold" print #play.textbox1, "!font Arial 24 bold" print #play.textbox2, "!font Arial 24 bold" print #play.textbox3, "!font Arial 24 bold" print #play.button1, "!setfocus"; 'Set focus to the Hit Me! button print #play.button3, "!disable" 'Disable the Play Again button 'Pause the application and wait for the player's instruction wait end sub
The first two statements specify the window’s dimensions. Then a series of statictext, textbox, and button controls are added to the window. Each of the button controls calls on a different subroutine as its event handler. The Hit Me! button calls on the DealCard
subroutine, which adds another card to the player’s hand when executed. The Stay! button calls on the DealerTurn
subroutine, which is responsible for managing the dealer’s hand. The Play Again button calls on the RestartGame
subroutine, which resets the game board and resets the game’s variables, readying the game to play a new hand.
The open
command is used next to display the window, which is assigned a handle of #play
. Next, the window’s trapclose
event handler is set up to call on the ClosePlay
subroutine whenever the player attempts to close the #play window. The DealOpeningHand
subroutine is then called. This subroutine sets font type, size, and attributes for various interface controls. Lastly, the Hit Me! button is assigned focus and the Play Again button is disabled and will stay that way until the end of the current hand.
The code statements that make up the DealCard
subroutine are provided next and should be added to the end of the program file. This subroutine is responsible for adding a new card to the player’s hand when called. This is accomplished by calling on the GetRandomNumbe
function and adding the number that is returned to the current value assigned to the playerCard
variable.
'This subroutine is called when the player clicks on the Hit Me! button sub DealCard handle$ 'Add another card to the player's hand playerCard = playerCard + GetRandomNumber() 'The player busts if his hand exceeds 21 if playerCard > 21 then print #play.textbox1, playerCard 'Display the player's card print #play.textbox3, "You have busted!" 'Display a summary of the 'results print #play.button1, "!disable" 'Disable the Hit Me! button print #play.button2, "!disable" 'Disable the Stay! button print #play.button3, "!enable" 'Disable the New Game button else print #play.textbox1, playerCard 'Display the player's card end if end sub
Next, an if...then...else
statement is set up to analyze the value of the player’s hand. If it is over 21, then the player has gone bust, losing the game. If this is the case, the value of the player’s hand is updated, a message is displayed explaining what has happened, and the Hit Me! and Stay! buttons are disabled while the New Game button is enabled. This prevents the player from doing anything else at this point other than starting a new game. If, however, the player’s hand is less than 21, all that happens is that the display of the player’s score is updated.
The code statements that make up the DealerTurn
subroutine are shown next and should be added to the end of the program file.
'This subroutine manages the dealer's turn sub DealerTurn handle$ print #play.button1, "!disable" 'Disable the Hit Me! button print #play.button2, "!disable" 'Disable the Stay! button print #play.button3, "!enable" 'Disable the New Game button do 'Add another card to the dealer's hand dealerCard = dealerCard + GetRandomNumber() print #play.textbox2, dealerCard 'Display the player's card 'The dealer busts if its hand exceeds 21 if dealerCard > 21 then print #play.textbox3, "The dealer busts!" 'Display a summary of the 'results exit sub 'Exit out of the subroutine end if loop while dealerCard < 17 'Keep looping as long as the dealer's hand 'is less than 17 'Analyze the results of the game once both the player and dealer have 'rested if playerCard = dealerCard then 'Check for a tie print #play.textbox3, "Tie!" end if if playerCard < dealerCard then 'See if the dealer has won print #play.textbox3, "You lose!" end if if playerCard > dealerCard then 'See if the player has won print #play.textbox3, "You win!" end if end sub
This subroutine begins by disabling the Hit Me! and Stay! buttons and enabling the New Game button. Next, a do...while
loop is set up. This loop is responsible for adding new cards to the dealer’s hand, stopping only when the value of the dealer’s hand (e.g., the value assigned to the dealerCard
variable) exceeds 17. New cards are added to the dealer’s hand by calling on the GetRandomNumber()
function and adding the value that is returned to dealerCard
. The display of the dealer’s hand is then updated. Each time the loop repeats, the value of dealerCard
is checked to see if it exceeds 21, in which case the dealer has busted. Note that if the dealer has busted, the exit sub
command is executed, immediately terminating the execution of the DealerTurn
subroutine. If the do...while
loop ends without the dealer going bust, a series of three if...then
statements are executed. These statements compare the player’s and the dealer’s hands to determine the result of the game.
The RestartGame
subroutine is shown next. When called, this subroutine calls on the ResetGame
subroutine and enables the Hit Me! and Stay! Buttons, allowing the player to begin adding new cards to her hand or to elect to hold on to her current hand. The last statement in this subroutine calls on the DealOpeningHand
subroutine, which deals an initial card for the player and the dealer.
'This subroutine gets the game ready to play a new hand sub RestartGame handle$ call ResetGame 'call on the subroutine that resets the game print #play.button1, "!enable" 'Disable the Hit Me! button print #play.button2, "!enable" 'Disable the Stay! button print #play.button3, "!disable" 'Disable the New Game button call DealOpeningHand 'Call on the subroutine that deals the player's 'and dealer's opening hand end sub
The code statements that make up the DealOpeningHand
subroutine are shown next and should be added to the end of the program file.
'This subroutine deals the player's and dealer's opening hand sub DealOpeningHand playerCard = GetRandomNumber() 'Retrieve the player's first card dealerCard = GetRandomNumber() 'Retrieve the dealer's first card print #play.textbox1, playerCard 'Display the player's card print #play.textbox2, dealerCard 'Display the dealer's card end sub
The first statement adds a card to the player’s and computer’s hand by assigning the value returned by the GetRandomNumber()
function. Two print
statements then update the display of the player’s and computer’s hands.
The GetRandomNumber()
function, shown next, is responsible for generating a numeric value and returning it to the statement that called upon the function.
'This function generates and returns cards function GetRandomNumber() RandomNumber = int(rnd(1)*13) + 1 'Retrieve a number between 1 and 13 if RandomNumber = 1 then RandomNumber = 11 'A 1 is converted to 11 (ace) if RandomNumber > 10 then RandomNumber = 10 '11, 12, and 13 are equivalent 'to jacks, queens, and kings 'which have a value of 10 GetRandomNumber = RandomNumber 'Return the selected card end function
As you can see, random numbers are generated in the range of 1 to 13. If a value of 1 is generated, it is considered to be an ace and a value of 11 is returned in its place. Any number greater than 10 represents a face card (jack, queen, or king) and a value of 10 is returned. Any other value (2–10) is returned without modification or replacement.
The code statements that make up the ResetGame
subroutine are shown next and should be added to the end of the program file. When called, this subroutine resets the value of dealerCard
and playerCard
back to zero and clears out any numbers or text displayed in the game’s textbox controls, readying the game to play a new hand.
'This subroutine resets the player's and dealer's hand and clears out 'the game board sub ResetGame dealerCard = 0 'Reset the dealer's hand to zero playerCard = 0 'Reset the player's hand to zero 'Clear out the textbox controls on the game board print #play.textbox1, "" print #play.textbox2, "" print #play.textbox3, "" end sub
The last procedure to be added to the BASIC BlackJack game is the ClosePlay
subroutine, whose code statements are shown next. This subroutine is called when the #play window’s trapclose
event handler is executed.
'This subroutine is called when the player closes the #play window 'and is responsible for ending the game sub ClosePlay handle$ 'Get confirmation before terminating program execution confirm "Are you sure you want quit?"; answer$ if answer$ = "yes" then 'The player clicked on Yes close #play 'Close the #play window end 'Terminate the game end if end sub
When called, this subroutine uses the confirm
command to display a popup window that requires the player to reconfirm her intention to terminate the game. If the player responds by clicking on the Yes button, the close
command is executed, followed by the end
command.
Okay, you have seen all the steps involved in the creation of the BASIC BlackJack game. Assuming that you didn’t make any typos or miss keying in any statements, the game should be ready to run. So, put it through its paces and see how well you fare when playing against the computer.
In this chapter you learned how to work with subroutines and functions. This included learning how to define them and to set them up to process arguments. You learned how to create functions that could return a result. You also learned about variable scope within subroutines and functions. Using this information you will be able to develop applications whose source code is better organized and more manageable.
Before moving on to Chapter 8, “Working with Text Files,” why don’t you take a little extra time to work on the BASIC BlackJack game by addressing the following list of challenges.