Like most other programming languages, Just BASIC provides programmers with the ability to interact with the computer’s file system. Just BASIC lets programmers create and delete files and folders. It can also be used to build applications that can create and work with external files. As such, it gives you the ability to create reports, documents, and log files. In addition to teaching you the basics of working with files and folders, this chapter will also show you how to create your next application project, the Tic Tac Toe game.
Specifically, you will learn how to
Indicate absolute and relative path and filenames
Open and close files
Read from and write to text files
Perform file administration tasks
In this chapter, you will learn how to create a new computerized Tic Tac Toe game. Individual game board squares will be represented by bmpbutton controls. Bmpbutton controls are well suited to this task since they can display graphic images (representing Xs and Os when selected by the players) and initiate a click
event when selected. Using each bmpbutton control’s cl ick event, the game will be able to respond to player clicks and then display bitmap images (of Xs and Os) representing each player’s move.
In total, nine bmpbutton controls will be used and will be lined up in three consecutive rows. Figure 8.1 shows how these controls appear when the game is first started.
Initially a blank (all white) bitmap image is loaded onto each bmpbutton control in order to represent an unselected game board square. As Figure 8.2 demonstrates, as game play continues, bitmap images representing Xs and Os are displayed on bmpbutton controls as they are selected by Player X and Player O.
Players are only allowed to choose from unselected squares. If a player attempts to select a square that has already been chosen, the popup dialog shown in Figure 8.3 is displayed, informing the player of her mistake.
Figure 8.3. Any attempt to click on a square that has already been selected will result in an error.
Game play continues until one of the players wins or until all nine game board squares have been selected without either player having won. At the end of each game, a popup dialog is displayed that informs the players of the result. For example, Figure 8.4 shows the popup dialog that is displayed when Player X wins.
Figure 8.4. At the conclusion of each round of play a popup dialog is displayed announcing the result.
After dismissing the popup dialog showing the results of the game, the game board continues to display the results of the previous game, as demonstrated in Figure 8.5. At this point the players can click on the Start New Game button to play another round or they can click on the close button (X) located on the upper-right corner of the window to terminate the game.
Some programming languages such as JavaScript have limited execution environments. In the case of JavaScript this means that it can only be used to create scripts that run within web browsers. As a result these programming languages do not have the capability to interface with the Windows file system and thus cannot be used to create text files or reports. However, desktop programming languages like Visual Basic, C++, and Just BASIC do not have this restriction.
Today, many programming languages have been expanded to the point where they are capable of developing applications that can be run on many different platforms. One of the best examples of this is Visual Basic .NET, which can be used to create desktop applications as well as web-based applications. Visual Basic .NET also supports application development on a wide range of portable devices, including PDAs and cell phones.
In this chapter, you will learn how to create Just BASIC applications that are capable of interacting with the Windows file system. The programming techniques presented here, although specific to Just BASIC, can be generally applied to other programming languages and will provide you with a solid understanding of the basic steps involved in developing applications that interact with the Windows file system.
In order to work with files and folders, you must understand how to interact with the Windows file system, which consists of one or more disk drives represented by a letter followed by a colon. For example, C: is the standard designation for a computer’s primary disk drive. Many computers today have more than one drive. You can retrieve a string representing all of the drives available on a computer using the Drives$
special variable. For example, the following statement uses the print
command to display the value of Drives$
.
print Drives$
When executed, output similar to the following would be displayed.
c: d: e: f:
As you can see, the computer on which this example was executed contains four drives. Using the Drives$
special variable you could, as the following example demonstrates, populate a combobox (or a listbox) control with a list of all the drives available on a computer, and then allow the user to select one.
nomainwin global selection$ dim mydrives$(10) call GenerateDriveList call DisplayWindow wait sub GenerateDriveList i = 0 do mydrives$(i) = word$(Drives$, i + 1) i = i + 1 loop until word$(Drives$, i + 1) = "" end sub sub DisplayWindow combobox #main.combobox, mydrives$(), DisplaySelection, 100, 100, 100, 100 open "Select Drive" for window_nf as #main wait end sub sub DisplaySelection handle$ print #main.combobox, "contents? selection$" notice "You picked " + selection$ close #main end end sub
In this example, an array named mydrives$
is defined that is capable of storing up to 11 elements. Next, a subroutine called GenerateDriveList
is called. This subroutine uses a do...until
loop to iterate through the list of drives stored in the Drives$
special variable in order to populate the mydrives$()
array.
Once the GenerateDriveList
subroutine is done, the DisplayWindow
subroutine is called. This subroutine displays a window that contains a combobox control. The combobox control has been set up to display the contents of the mydrives$()
array In addition, the combobox control has also been set up to execute the program’s remaining subroutine, DisplaySelection
. When called, this subroutine retrieves the drive letter that the user has selected from the combobox control and displays it.
In Just BASIC, if you specify a filename without also providing any path information, Just BASIC looks to the special variable DefaultDir$
to determine which folder to look in to find the file. By default, DefaultDir$
contains a text string identifying the absolute path and name of the folder where the Just BASIC application being executed resides. For example, suppose you created a new Just BASIC program named text1.bas that contained the following statement.
print DefaultDir$
If you save this program in C:Program FilesJust BASIC v1.01 and then execute it, the following output would be displayed.
C:Program FilesJust BASIC v1.01
Programmers can use the files
command to retrieve information about files and folders. To use this statement, you must first create a two-dimensional array, which you will use to store the data returned by the files
command, as demonstrated here:
dim folderInfo$(3, 3) files "c: emp", folderInfo$()
Here, a two-dimensional array named folderInfo$()
has been defined that can store up to four pairs of entries. Next, the files
command is executed and passed two arguments. The first argument is the path of the folder for which information is to be retrieved. The second argument is the name of the array that will be used to store this information. Once executed, you can retrieve information about the specified folder by examining the information that has been stored in the array, as demonstrated here:
print "Files: " + folderInfo$(0, 0) 'Retrieves the number of files print "Folders: " + folderInfo$(0, 1) 'Retrieves the number of folders print "Drive: " + folderInfo$(0, 2) 'Retrieves the drive print "Path: " + folderInfo$(0, 3) 'Retrieves the path
As was discussed back in Chapter 4, “Working with Variables and Arrays,” a one-dimensional array is an indexed listed of values. A two-dimensional array is like a table or a spreadsheet, made up of rows and columns. When using the files
command to retrieve information about a folder, you retrieve the information from the first column of the array, which has an index value of 0. Since data is retrieved from a two-dimensional array by specifying a pair of numbers, when retrieving folder data you will always use 0 as the first number when specifying array coordinates. Thus the value representing the number of files found in a folder is located at (0,0), while the number of subfolders found in the folder is found at (0,1). File information, on the other hand, is stored in the second column of the array and thus is retrieved by specifying a value of 1 as the first coordinate. For example, the name of the file would be stored at coordinates (1,0), while the size of the file would be found at (1, 1).
When executed, these statements will generate output similar to the following.
Files: 2 Folders: 2 Drive: c: Path: temp
The files
command can just as easily be used to collect information about individual files, as demonstrated here:
dim fileInfo$(2, 2) files "c: emp", "Application.log", fileInfo$() print "Filename: " + fileInfo$(1, 0) 'Retrieves the file's name print "File size: " + fileInfo$(1, 1) 'Retrieves the file's size print "Date/time: " + fileInfo$(1, 2) 'Retrieves the file's date and time
Here, the files
command has been used to retrieve information about a filenamed Application.log
stored in C: emp
. The data retrieved from the command is stored in the fileInfo$()
array and then displayed using a series of print
commands.
You may also use wildcard characters when specifying filenames for the files
command. For example, the ? wildcard character can be used to substitute any single character, whereas the *
wildcard character can be used to substitute any number of characters. To better understand what all this means, take a look at the following example.
dim fileInfo$(2, 2) files "c: emp", "*.log", fileInfo$() for i = 1 to val(fileInfo$(0, 0)) print "Filename: " +fileInfo$(i, 0) next i
Here, the *
wildcard character has been used to tell the files
command to add information about every file found in the c: emp
folder that has a .log
file extension. A string showing the number of files found will be stored in fileInfo$(0, 0)
. This value can be converted to a numeric value using the val
() function. As such, it can be used to control the execution of a for_next
loop that iterates through all of the output stored in the fileInfo$()
array, displaying output similar to that shown here:
Filename: Application.log Filename: Error.log Filename: System.log
As you can see, three files with a .log file extension were found in c: emp.
If you write a program that needs to work with a specific file and that file is not present when the program attempts to access it, an error will occur. To avoid this problem, you can use the files
command to determine whether the file exists. If it does, your program can go ahead and open it. If the file does not exist, your program could create and then open it. To see how this might be done, take a look at the following example.
dim fileInfo$(0, 0) files "c: emp", "Application.log", fileInfo$ () if val(fileInfo$(0, 0)) = 0 then print "File not found!" end if
Here, an array named fileInfo$()
has been defined to hold a single pair of items. The files
command is then executed. Next, an if...then
statement has been set up to examine fileInfo$(0, 0)
. If a value of 0 is found then the file does not exist. At this point, you could insert additional programming statements to handle the situation as appropriate.
Like most programming languages, Just BASIC allows you to specify the location of files and folders using either absolute or relative paths. An absolute path is one that specifies the complete path to a file or folder, including the drive specification and any folders included in the path between the drive specification and the target file or folder. For example, the following statement specifies the absolute path of the temp folder (c: emp
).
files "c: emp", "Application.log", fileInfo$()
Use of absolute path when specifying file and folder names is convenient when you can count on the location of the files and folders being where you expect them to be, which would be the case on your own desktop computer. This might also be the case in a tightly controlled corporate environment where desktop computers are controlled and monitored by a central desktop support team. However, in many cases, you cannot count on knowing the exact location of a file of folder. For example, if you create a distribution package for one of your Just BASIC games and share it with your friends, there is no guaranteeing that they will install the game in the location you expect when they set it up on their computer. In these types of situations, using relative path names is usually the better way to go.
With relative paths, you identify the location of a file or folder relative to the location of the current working directory (folder). For example, if you have created an application that works with a text file that is located within the same location as the application itself, you could specify its name and location as demonstrated here:
open "CustomerData.txt" for input as #1
Since your Just BASIC program and the text file reside in the same folder, it is not necessary to specify the absolute path of the text. Instead, by simply specifying the name of the text file, Just BASIC knows to look in the current working directory.
Now let’s suppose that when your application is installed, your installation package automatically adds a number of files and folders to the computer. Using relative paths, you can navigate backwards and forwards within this file structure. For example, consider the following statement.
open "inventoryPartsList.txt" for input as #1
Here, the open
command has been instructed to look in a subfolder of the current working directory named inventory
and to open a filenamed PartsList.txt
. Using relative paths, you can also navigate your way backwards, as demonstrated here:
open "..SystemCodes.txt" for input as #1
Here, using the .. characters, the open
command has been instructed to look in the parent folder of the current working directory for a filenamed SystemCodes.txt
. If necessary, you can go backwards an additional level, as demonstrated here:
open "....Accounting.txt" for input as #1
Here, the open
command has been instructed to go back to the parent folder of the parent folder of the current working directory and open a filenamed Accounting.txt
. You can get as creative as necessary using relative paths to specify the location of a file.
open "......ooksISBNS.txt" for input as #1
Here, the open
command has been told to back up three levels from the current working directory and then to go forward one level into a folder named books
in order to find and open a filenamed ISBNS.txt
.
The filedialog
command can be used to display a dialog window that allows the user to select a file using a standard Windows Explorer dialog window. The dialog window will return a string representing the absolute path and filename of the file selected by the user. For example, take a look at the following statement.
filedialog "Open a File", "c: emp*.txt", selectedFile$
Here, a filedialog window is opened and configured to display a list of all text files located in the c: emp
folder, as demonstrated in Figure 8.6.
Figure 8.6. Using the filedialog
command to display a popup dialog that allows the userto selecta file.
In this example, a string representing the absolute file and path name of the file selected by the user is then captured and stored in a variable named selectedFile$
.
Just BASIC provides programmers with the capability of storing data in and reading it back from different types of files, including sequential, binary, and random access files. In this chapter, you will learn the basic steps involved in storing and retrieving data in sequential files.
A sequential file is a file that contains plain text. Sequential files are processed sequentially, from beginning to end. A binary file is a file that contains more than plain text. Binary files are capable of storing graphic files, sound files, etc. A random access file is a file that can be read from or written to at any location within the file. To learn more about how to work with binary and random access files, consult Just BASIC’s help system.
In order to work with a sequential file, you must first open it. In Just BASIC, files are opened using the open command, which has the following syntax.
open resource for purpose as #handle [len = n]
The open command is an extremely powerful and flexible command that can be used to perform a number of different actions, including opening files, windows, and serial communications. With respect to opening files, resource
identifies the file to be opened and the purpose
is input. The last parameter, [len = n]
, is used only when working with random access files.
As an example of how to use the open command, take a look at the following example.
open "c: empSample.txt" for input as #targetFile
Here, the open
command has been set up to open a filenamed Sample.txt
located in c: emp
. Once opened, the file can be referenced within the program as #targetFile
.
Any file that is opened should be closed when the program is done working with it. This is accomplished using the close
command, which has the following syntax.
close #reference
#reference
is the handle assigned to the file when it was opened using the open
command. To avoid errors, it is critical that you always remember to close any file previously opened by your application, as demonstrated in the following example.
open "c: empSample.txt" for input as #targetFile 'Add statements here that process the contents of the file close #targetFile
If you forget to close a previously opened file, an error will be generated when you try to terminate the execution of your program, as demonstrated in Figure 8.7.
Once you have opened a text file, you can read from it and write to it. Just BASIC processes text files sequentially, starting at the beginning of the file down to its end. You cannot open a text file and begin reading it anywhere other than at the beginning. To read from a text file, you can use the input
and line input
commands.
The input
command reads from the text file stopping at the first comma or carriage return that it finds. A subsequent input
command will resume reading at the point where the previous command stopped, terminating at the next comma or carriage return that it runs into. When used to read from a text file, the input
command has the following syntax.
input #handle, variableName
The input
command can be effectively used when reading from a text file that has been organized into records separated by commas.
#handle
is the reference previously set up for the file by the open
command and variableName
is the name of a variable into which the contents of the text file are to be copied. For example, the following statements open a filenamed Sample.txt
and read the first line from it (assuming that the first line does not have a comma in it).
open "c: empSample.txt" for input as #targetFile input #targetFile, variableName$ print variableName$ close #targetFile
Once the input
command has been used to read the first line and store its contents, the print command is used to display what was read in the default mainwin window.
Using the line input
command, you can read from a file a line at a time, ignoring any command that maybe found along the way. The syntax of the line input
command is shown here:
line input #handle, variablename
To get a better feel for how this command works, suppose you have a text filenamed Story.txt
that contains the text shown in Figure 8.8.
Using the line input
command within a loop, you can create a program that reads through and processes every line in the text file, as demonstrated here:
nomainwin WindowWidth = 360 WindowHeight = 300 texteditor #main.texteditor1, 10, 10, 335, 230 open "Story" for window_nf as #main call DisplayStory wait sub DisplayStory open "c: empStory.txt" for input as #targetFile while eof(#targetFile) = 0 line input #targetFile, variableName$ print #main.texteditor1, variableName$ wend close #targetFile end sub
As you can see, this example starts off by displaying a small application window that contains a texteditor control. The texteditor control has been sized so that it fills up most of the window. A call is then made to a subroutine named DisplayStory
, which then uses a while...wend
loop and the line input
command to copy the contents of the text file into the texteditor control. When executed, the contents of the text file will be visible to the user, as demonstrated in Figure 8.9.
To read a file a line at a time from beginning to end as demonstrated above, you will need to used the eof()
function. This function is used to determine when the end of the file that is being read has been reached. If the end of the file is not reached the function returns a value of –1. When the end of the file has been reached the function returns a value of 0.
Writing to a text file is just about as easy as reading from one. Just BASIC gives you two options for doing so, as outlined here:
output
—. Opens a new file and writes to the beginning of it.
append
—. Opens an existing file and writes to the end of it.
To create a new file and write to it, you need to open the file for output, as demonstrated here:
open "c: empStory.txt" for output as #targetFile
When opened in this manner, any text written to the file is written starting at the beginning of the file. Once the file has been opened in for output
mode, text is written to the file using print
commands. If a file of the same name already exists, it is replaced by a new empty file to which text is then written. It is a good idea to first check to see if the file that you want to write to already exists. If this is the case and you want to preserve any text that may have already been written to the file, you will want to instead open the file for append
, as discussed in the next section.
To get a good feel for how to write output to a file, take a look at the following example.
open "c: empStory.txt" for output as #x print #x, "Once upon a time in a far away land, there lived a little boy" print "#x, named Alexander the Great. One hot summer day an evil monster" print #x, "emerged in the small town where Alexander lived and began to" print #x, "terrorize everyone who lived there. No one dared to stand up" print #x, "and try to fight the monster. No one, that is, except for" print #x, "Alexander." close #x
As with the reading of files, any file opened for writing must be closed in order to prevent an error from occurring. As each line of output is written to the file, Just BASIC automatically adds a carriage return to the end of the line. When executed, this example will create the Story.txt
file that you worked with earlier in the chapter when learning how to read from text files.
As was alluded to in the previous section, you can open a file in for append
mode in order to preserve its current contents. Once opened in this manner, any print
statements that write output to the file are added to the end of the file instead of to the beginning of it, as demonstrated in the following example.
open "c: empStory.txt" for append as #x print #x, "" print #x, "The End" close #x
When executed, this example will open the Story.txt
file thatyou created previously and add a blank line followed by a second line that reads The End to the end of the text file.
You have already learned how to create and write to new files. Just BASIC also provides you with a number of commands that you can use to interact with and control the Windows file system. Using these commands, you can rename and delete files. You can also create and delete folders.
To rename a file, use the name command, which has the following syntax.
name CurrentName as NewName
CurrentName
is the name of the file that you want to rename. New Name
is the new filename. For example, the following statement could be used to rename a filenamed mini-parts.txt
located in the current working directory to small-parts.txt
.
name "mini-parts.txt" as "small-parts.txt"
Provided that you have the appropriate security permissions, you can use the kill
command to delete a file from the computer. The kill
command has the following syntax.
kill FileName
FileName
is the name of the file to be removed. For example, the following statement could be used to delete a filenamed small-parts.txt
from the current working directory.
kill small-parts.txt
In addition to creating new files, Just BASIC also lets you programmatically create new folders. This is achieved through the mkdir()
function, which has the following syntax.
mkdir(" FolderName")
Folder Name is a text string that specifies the name and path of the folder to be created. For example, the following statement creates a new folder named WorkPapers
in the C:Temp
folder.
result = mkdir("c:TempWorkPapers") if result <> 0 then notice "Something went wrong. c: empWorkpapers was not created." end if
When executed, the mkdir()
function returns a numeric value indicating the success or failure of its operation. A value of zero indicates that the folder was successfully created. A non-zero value indicates that an error occurred and the folder was not created, which would be the case if a folder of the same name already existed at the specified location.
Just BASIC also provides you with the ability to delete empty folders using the rmdir()
function. If the delete operation is successful, the function returns a value of zero. The rmdir()
function has the following syntax.
rmdir(" FolderName")
Using this command you can programmatically delete any folder from the computer over which you have the appropriate set of security permissions.
result = rmdir("c: empWorkPapers") if result <> 0 then notice "Something went wrong. c: empWorkpapers was not deleted." end if
In this example, the WorkPapers
folder is deleted from the c: emp
folder if it is found and if it is empty. If the folder is not found or if the folder is not empty, an error occurs.
Okay, it is time to turn your attention back to the development of this chapter’s game project. In developing this application, you will learn how to work with the bmpbutton control to display different bitmap images. Specifically, this application will display various combinations of the three graphic bitmap images shown in Figure 8.10 as the players play the game. You can download copies of all three of these files from this book’s companion website at www.courseptr.com/downloads.
Figure 8.10. Copies of the three bitmap images used to represent available game board squares as well as squares selected by Player X and Player O.
The design of the Tic Tac Toe game will rely on the use of subroutines. In total, you will create the Tic Tac Toe game in eight steps, as outlined here:
Create a new BASIC file and add initial comment statements.
Define global variables and start the game play.
Create the ProcessMove
subroutine.
Create the SwitchPlayerTurn
subroutine.
Create the LookForEndOfGame
subroutine.
Create the ResetGameBoard
subroutine.
Create the ClosePlay
subroutine.
The first step is to create a new BASIC file. After creating the program file, add the following statements to it.
' *************************************************************************************** ' ' Script Name: TicTacToe.bas (The Tic Tac Toe Game) ' Version: 1.0 ' Author: Jerry Lee Ford, Jr. ' Date: March 1, 2007 ' ' Description: This game is a Just BASIC implementation of the classic ' children's Tic Tac Toe game. This game pits two players ' against one another to see who can line up three ' consecutive characters in a row. ' ' *************************************************************************************** nomainwin 'Suppress the display of the default text window
The first 13 lines are introductory comment statements that help to document the program and its purpose. The last statement executes the nomainwin
command in order to prevent the mainwin window from being displayed when the application is started.
The next step in the development of the Tic Tac Toe game is to define the game’s global variables and to call upon the subroutine that displays the game board window. This is accomplished by adding the following statements to the end of the program file.
'Assign default values to global variables global currentPlayer$, noMoves global a1$, a2$, a3$, b1$, b2$, b3$, c1$, c2$, c3$ currentPlayer$ = "X" 'Player X always starts off each game call ManageGamePlay 'Call the subroutine responsible for managing game play wait 'Pause the application and wait for the player's instruction
In this version of Tic Tac Toe, Player X always starts each new game by going first. Players take turns making moves. The game keeps track of whose turn it is based on the value assigned to currentPlayer$
, which is set to “X” at the beginning of each new game.
The game board will consist of nine bmpbutton controls named A1, A2, A3, B1, B2, B3, C1, C2, and C3. As players take turns selecting game board squares, a graphic is displayed on each bmpbutton identifying when it has been selected and which player has selected it. The game keeps track of the status of each game board square (bmpbutton) by assigning a value of “X” or “O” to variables named a1$, a2$, a3$, b1$, b2$, b3$, c1$, c2$
, and c3$
. Part of knowing when a game has ended is knowing how many moves the players have made, which is tracked using the noMoves variable. Finally, game play is started by calling the ManageGamePlay
subroutine.
The Tic Tac Toe game consists of a single window, which displays the game board. The ManageGamePlay
subroutine, shown next, is responsible for creating and displaying the game’s main window (#play). These statements should be added to the end of the program file.
'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 = 500 'Set the height of the window to 500 pixels loadbmp "_", "C:images\_.bmp" 'Load the specified bitmap 'file into memory loadbmp "O", "C:imageso.bmp" 'Load the specified bitmap 'file into memory loadbmp "X", "C:imagesx.bmp" 'Load the specified bitmap 'file into memory 'Define the format of statictext controls displayed on the window statictext #play.statictext1, "T I C T A C T O E", 45, 20, 440, 30 statictext #play.statictext2, "Copyright 2007", 265, 55, 80, 20 'Add nine bmpbuttoncontrols representing the game board to the window 'First column bmpbutton #play.a1,"C:images\_.bmp", ProcessMove, UL, 45, 90 bmpbutton #play.a2,"C:images\_.bmp", ProcessMove, UL, 150, 90 bmpbutton #play.a3,"C:images\_.bmp", ProcessMove, UL, 255, 90 'Second column bmpbutton #play.b1,"C:images\_.bmp", ProcessMove,UL,45, 194 bmpbutton #play.b2,"C:images\_.bmp", ProcessMove,UL,150, 194 bmpbutton #play.b3,"C:images\_.bmp", ProcessMove,UL,255, 194 'Third column bmpbutton #play.c1,"C:images\_.bmp", ProcessMove,UL,45, 298 bmpbutton #play.c2,"C:images\_.bmp", ProcessMove,UL,150, 298 bmpbutton #play.c3,"C:images\_.bmp", ProcessMove,UL,255, 298 'Add the game's button control to the window button #play.button1 "Start New Game", ResetGameBoard, UL, _ 147, 420, 100, 30 'Open the window with no frame and a handle of #play open "Tic Tac Toe" for window_nf as #play 'Set up the trapclose event for the window print #play, "trapclose ClosePlay" 'Set the font type, size and attributes print #play.statictext1, "!font Arial 24 bold" print #play.button1, "!setfocus"; 'Set focus to the button control 'Pause the application and wait for the player's instruction wait end sub
The ManageGamePlay
subroutine begins by defining the height and width of the game’s window and preloading three images representing Player X, Player O, and an unselected game board square. Next, the game defines the controls that make up its user interface. These controls include two statictext controls used to display the game’s name and copyright information as well as nine bmpbutton controls representing each of the game’s nine game board squares. The last control to be added to the window is a button control, which allows the players to clear the game board and start a new game at any time during game play.
Next, the open
statement is used to display the window, and the window’s trapclose
event handler is defined. The last few statements set the font type, size, and attributes of the statictext control that displays the name of the game and sets focus to the Start New Game button. The wait
command is then executed, pausing the game to allow Player X to make the first move.
Players make their moves by clicking on one of the bmpbutton controls. Each bmpbutton control has the ProcessMove
subroutine set up as its event handler. The code statements that make up this subroutine are shown next and should be added to the end of the program file.
'This subroutine processes player moves, deciding when moves are 'valid and invalid and assigning game board squares accordingly sub ProcessMove handle$ 'Set up a select case code block to process player moves select case handle$ case "#play.a1" 'The player selects the 1st square on the 1st row if a1$ = "" then 'Let the player have the square if its available a1$ = currentPlayer$ 'Assign the square to the current player print #play.a1, "bitmap " + currentPlayer$ 'Display bitmap image else notice "Sorry, but this square is already assigned. Try again!" exit sub 'There is no need to keep going so exit the subroutine end if case "#play.a2" 'The player selects the 2nd square on the 1st row if a2$ = "" then 'Let the player have the square if its available a2$ = currentPlayer$ 'Assign the square to the current player print #play.a2, "bitmap " + currentPlayer$ 'Display bitmap image else notice "Sorry, but this square is already assigned. Try again!" exit sub 'There is no need to keep going so exit the subroutine end if case "#play.a3" 'The player selects the 3rd square on the 1st row if a3$ = "" then 'Let the player have the square if its available a3$ = currentPlayer$ 'Assign the square to the current player print #play.a3, "bitmap " + currentPlayer$ 'Display bitmap image else notice "Sorry, but this square is already assigned. Try again!" exit sub 'There is no need to keep going so exit the subroutine end if case "#play.b1" 'The player selects the 1st square on the 2nd row if b1$ = "" then 'Let the player have the square if its available b1$ = currentPlayer$ 'Assign the square to the current player print #play.b1, "bitmap " + currentPlayer$ 'Display bitmap image else notice "Sorry, but this square is already assigned. Try again!" exit sub 'There is no need to keep going so exit the subroutine end if case "#play.b2" 'The player selects the 2nd square on the 2nd row if b2$ = "" then 'Let the player have the square if its available b2$ = currentPlayer$ 'Assign the square to the current player print #play.b2, "bitmap " + currentPlayer$ 'Display bitmap image else notice "Sorry, but this square is already assigned. Try again!" exit sub 'There is no need to keep going so exit the subroutine end if case "#play.b3" 'The player selects the 3rd square on the 2nd row if b3$ = "" then 'Let the player have the square if its available b3$ = currentPlayer$ 'Assign the square to the current player print #play.b3, "bitmap " + currentPlayer$ 'Display bitmap image else notice "Sorry, but this square is already assigned. Try again!" exit sub 'There is no need to keep going so exit the subroutine end if case "#play.c1" 'The player selects the 1st square on the 3rd row if c1$ = "" then 'Let the player have the square if its available c1$ = currentPlayer$ 'Assign the square to the current player print #play.c1, "bitmap " + currentPlayer$ 'Display bitmap image else notice "Sorry, but this square is already assigned. Try again!" exit sub 'There is no need to keep going so exit the subroutine end if case "#play.c2" 'The player selects the 2nd square on the 3rd row if c2$ = "" then 'Let the player have the square if its available c2$ = currentPlayer$ 'Assign the square to the current player print #play.c2, "bitmap " + currentPlayer$ 'Display bitmap image else notice "Sorry, but this square is already assigned. Try again!" exit sub 'There is no need to keep going so exit the subroutine end if case "#play.c3" 'The player selects the 3rd square on the 3rd row if c3$ = "" then 'Let the player have the square if its available c3$ = currentPlayer$ 'Assign the square to the current player print #play.c3, "bitmap " + currentPlayer$ 'Display bitmap image else notice "Sorry, but this square is already assigned. Try again!" exit sub 'There is no need to keep going so exit the subroutine end if end select noMoves = noMoves + 1 'Increment the variable representing the number of 'moves made so far in this game 'Call the subroutine responsible for determining when the game is over call LookForEndOfGame 'Call the subroutine responsible for controlling whose turn it is call SwitchPlayerTurn end sub
As you can see, this subroutine mainly consists of a Select Case
statement that contains nine case statements, each of which sets up a code block that’s designed to process a specific game board square (bmpbutton). The first case statement is set up to execute when one of the players clicks on the bmpbutton control named #play.a1
(e.g., when the value of handle$
is equal to "play.a1"
).
Remember, a subroutine set up as an event handler is automatically passed an argument, which is a text string that identifies the name of the window or control whose event has triggered the subroutine’s execution.
The first thing that each case statement’s code block does when executed is to examine the value of its associated global variable, which in the case of #play.a1
is a1$
. If a1$
is equal to an empty string (""
), then it has not been chosen yet and the next statement assigns the value of currentPlayer$
(either Player "X"
or Player "0"
) to a1$
. Next, the bitmap image associated with the current player is displayed on the bmpbutton control.
The game’s three bitmap images were preloaded into memory earlier in the program file. To display them on a bitmap controlyou must use the print
command in order to pass a string to the control for processing. This string consists of two parts. The first partis the keyword bitmap
. The second part of the string is a reference to the file that was established earlier by the loadbmp
commands to preload each bitmap file (_represents a blank bitmap file, 0
represents a bitmap file that displays the letter O, and X represents a bitmap file that displays the letter X).
If the value of a1$
is not equal to an empty string, then the game board square has already been assigned to one of the players and it cannot be reassigned again. The player is then notified of the selection error via a popup dialog and the exit sub
statement is executed (since no further action needs to be taken when an invalid move is made).
The next eight case statement code blocks operate identically to the first case
statement code block except that each is set up to process the move associated with different game board squares. At the end of the subroutine are a few additional code statements, which are executed only in the event the player selected an available game board square when making a turn. The first of these statements increments the value of noMove
by 1, allowing the game to keep track of the number of valid moves made so far. Next, the LookForEndOfGame
subroutine is executed. The subroutine will scan the game board and determine when either player has won the game by managing to line up three squares in a row. The SwitchPlayerTurn
subroutine is then called. This subroutine is responsible for controlling when each player gets to take a turn.
The code statements that make up the SwitchPlayerTurn
subroutine are shown next and should be added to the end of the program file.
'This subroutine is responsible for switching between Player X and 'Player O's turns sub SwitchPlayerTurn 'If Player X just went then it is Player O's turn if currentPlayer$ = "X" then currentPlayer$ = "O" 'Make Player O the current player exit sub 'There is no need to keep going so exit the subroutine end if 'If Player O just went then it is Player X's turn if currentPlayer$ = "O" then currentPlayer$ = "X" 'Make Player X the current player exit sub 'There is no need to keep going so exit the subroutine end if end sub
As you can see, this subroutine consists of two if...then
code blocks. The first code block executes if Player X has just completed a turn, assigning a value of “O” to currentPlayer$
and then exiting the subroutine. If, however, Player O just moved, the first code block is skipped and the second if...then
code block is executed, assigning “X” to currentPlayer$
. Thus by assigning a string value of “X” or “O” to currentPlayer$
the game is able to keep track of player turns.
The code statements for the LookForEndOfGame
subroutine are shown next. This subroutine is called at the end of each player’s turn to determine if the current player has won the game.
'This subroutine is called at the end of each player's turn and is 'responsible for determining if the game is over sub LookForEndOfGame 'Look horizontally for a winner 'Check the first column if (a1$ = currentPlayer$) and (a2$ = currentPlayer$) and _ (a3$ = currentPlayer$) then notice "Player " + currentPlayer$ + " has won!" exit sub 'There is no need to keep going so exit the subroutine end if 'Check the second column if (b1$ = currentPlayer$) and (b2$ = currentPlayer$) and _ (b3$ = currentPlayer$) then notice "Player " + currentPlayer$ + " has won!" exit sub 'There is no need to keep going so exit the subroutine end if 'Check the third column if (c1$ = currentPlayer$) and (c2$ = currentPlayer$) and _ (c3$ = currentPlayer$) then notice "Player " + currentPlayer$ + " has won!" exit sub 'There is no need to keep going so exit the subroutine end if 'Look vertically for a winner 'Check the first row if (a1$ = currentPlayer$) and (b1$ = currentPlayer$) and _ (c1$ = currentPlayer$) then notice "Player " + currentPlayer$ + " has won!" exit sub 'There is no need to keep going so exit the subroutine end if 'Check the second row if (a2$ = currentPlayer$) and (b2$ = currentPlayer$) and _ (c2$ = currentPlayer$) then notice "Player " + currentPlayer$ + " has won!" exit sub 'There is no need to keep going so exit the subroutine end if 'Check the third row if (a3$ = currentPlayer$) and (b3$ = currentPlayer$) and _ (c3$ = currentPlayer$) then notice "Player " + currentPlayer$ + " has won!" exit sub 'There is no need to keep going so exit the subroutine end if 'Look diagonally for a winner 'Check from the top-left corner down to the lower-right corner if (a1$ = currentPlayer$) and (b2$ = currentPlayer$) and _ (c3$ = currentPlayer$) then notice "Player " + currentPlayer$ + " has won!" exit sub 'There is no need to keep going so exit the subroutine end if 'Check from the top-right corner down to the lower-left corner if (a3$ = currentPlayer$) and (b2$ = currentPlayer$) and _ (c1$ = currentPlayer$) then notice "Player " + currentPlayer$ + " has won!" exit sub 'There is no need to keep going so exit the subroutine end if 'If neither player has won and all squares have been chosen the game 'ends in a tie if noMoves = 9 then notice "Tie. There was no winner this time!" end if end sub
As you can see, this subroutine consists of if...then
statements that determine if the game is over. The first if...then
statement checks to see if the current player has won by selecting all three squares in the first column (a1, b1, c1). It does this by checking to see if a1$
, b1$
, and c1$
are all assigned to the current player. Note the use of the and operator, which simplifies the logic involved in making these comparisons by allowing one if...then
statement to be created instead of three if...then
statements. If the player has won, a message is displayed in a popup dialog notifying the players of the results of the game, after which the exit sub statement executes, terminating the execution of this subroutine.
The next two if...then
statements check the second and third columns. The three if...then
statements that follow check each row of the game board. The next two if...then
statements check diagonally in both directions for a winner. If no winner is found, the last if...then
statement executes, checking the value assigned to noMoves
to see if it is equal to 9. If it is the game has ended in a tie with neither player having lined up three consecutive squares and all squares having been chosen. If, on the other hand, the value of noMoves
is less than 9, the subroutine ends without taking action, allowing the game to continue.
The ResetGameBoard
subroutine, whose code statements are shown next, is responsible for readying the game for a new round of play. This is achieved by displaying the blank bitmap image file (_.bmp) on each bmpbutton control and then clearing out any assignments made to the game board squares by assigning an empty string to a1$
, a2$
, a3$
, b1$
, b2$
, b3$
, c1$
, c2$
, and c3$
. The subroutine concludes by resetting the value of noMoves
to 0 and making Player X the current player in the new game.
'This subroutine resets the game board and global variables in order to 'ready the game for a new round of play sub ResetGameBoard handle$ 'Display a blank bitmap image in each game board square print #play.a1, "bitmap _" print #play.a2, "bitmap _" print #play.a3, "bitmap _" print #play.b1, "bitmap _" print #play.b2, "bitmap _" print #play.b3, "bitmap _" print #play.c1, "bitmap _" print #play.c2, "bitmap _" print #play.c3, "bitmap _" 'Clear out any game board square assignments a1$ = "" a2$ ="" a3$ ="" b1$ ="" b2$ ="" b3$ ="" c1$ ="" c2$ ="" c3$ ="" noMoves = 0 'Reset the variable used to keep track of the total number 'of moves made per game to zero currentPlayer$ = "X" 'Set Player X as the current player end sub
The last subroutine in the Tic Tac Toe game is the ClosePlay
subroutine, which should look pretty familiar to you by now. This subroutine should be added to the end of the program file and is responsible for getting player confirmation before terminating the execution of the game.
'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
All right, that is all there is to the development of the Tic Tac Toe game. Assuming that you have not made any typing mistakes, everything should be ready for you to test. As you put your new program through its paces, keep an eye on the overall flow of the game, ensuring that the game manages the switching of player moves and the assignment of game board squares correctly.
In this chapter you learned how to programmatically interact with the Windows file system. This included learning how to access information about files and folders as well as how to specify the location of files using absolute and relative path and filenames. You also learned how to open, close, read from, and write to text files sequentially. You learned how to create and delete files and folders in order to perform basic file and folder administration tasks.
Before moving on to Chapter 9, “Working with Sound and Graphics,” why don’t you take a little extra time to work on the Tic Tac Toe game by tackling the following list of challenges.