Chapter 7. Improving Program Organization with Functions and Subroutines

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

  • Improve the overall organization and manageability of your applications through subroutines and functions

  • Streamline program code by placing reusable code within subroutines and functions

  • Pass data to subroutines and functions for processing

  • Return data from functions back to calling statements

Project Preview: The BASIC BlackJack Game

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.

The splash screen is displayed for four seconds and then automatically disappears.

Figure 7.1. The splash screen is displayed for four seconds and then automatically disappears.

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.

In this game the player must complete his hand before the dealer plays out its hand.

Figure 7.2. In this game the player must complete his hand before the dealer plays out its hand.

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’s turn ends when she clicks on the Stay! button or when she goes bust.

Figure 7.3. The player’s turn ends when she clicks on the Stay! button or when she goes bust.

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 results of each hand are displayed at the bottom of the window.

Figure 7.4. The results of each hand are displayed at the bottom of the window.

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.

Any hand over 21 results in an automatic loss.

Figure 7.5. Any hand over 21 results in an automatic loss.

Working with Subroutines and Functions

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.

Hint

Hint

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.

Defining Subroutines

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.

Trap

Trap

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.

Using a Subroutine as an Event Handler

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.

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.

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.

Programmatically Calling a Subroutine

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:

Hint

Hint

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

Hint

Hint

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.

Prematurely Terminating a Subroutine

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.

Defining Functions

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.

Hint

Hint

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

Executing Functions

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.

Prematurely Terminating a Function

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.

Different Ways to Pass Arguments to Procedures

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.

Passing Arguments by Value

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.

Passing Arguments by Reference

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.

Taking Advantage of Built-in Function Libraries

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

abs()

int()

rnd()

acs()

left()

sin()

acs()

len()

space()

asn()

lof()

sqr()

atn()

loc()

str$()

chr$()

log()

tab()

cos()

lower$()

tan()

date$()

mid$()

time$()

eof()

midipos()

trim$()

exp()

mkdir()

upper$()

input$()

right$()

using()

instr()

rmdir()

val()

Back to the BASIC BlackJack Game

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.

Reviewing copies of the two bitmap images displayed on the game’s opening splash screen window.

Figure 7.7. Reviewing copies of the two bitmap images displayed on the game’s opening splash screen window.

Hint

Hint

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.

Designing the Game

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:

  1. Create and document a new BASIC file.

  2. Define global variables and execute the DisplaySplash subroutine.

  3. Create the DisplaySplash subroutine.

  4. Create the CloseSplashWindow subroutine.

  5. Create the ManageGamePlay subroutine.

  6. Create the DealCard subroutine.

  7. Create the DealerTurn subroutine.

  8. Create the RestartGame subroutine.

  9. Create the DealOpeningHand subroutine.

  10. Create the GetRandomNumber function.

  11. Create the ResetGame subroutine.

  12. Create the ClosePlay subroutine.

Creating a Just BASIC File Script

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

Initializing the Game

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

Creating the DisplaySplash Subroutine

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.

Hint

Hint

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.

Creating the CloseSplashWindow Subroutine

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.

Creating the ManageGamePlay Subroutine

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.

Creating the DealCard Subroutine

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.

Creating the DealerTurn Subroutine

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.

Creating the RestartGame Subroutine

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

Creating the DealOpeningHand Subroutine

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.

Creating the GetRandomNumber Function

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.

Creating the ResetGame Subroutine

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

Creating the ClosePlay Subroutine

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.

The Final Result

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.

Summary

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.

 

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.14.144.216