Chapter 8. Working with Text Files

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

Project Preview: The Tic Tac Toe Game

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.

Player X starts each game by clicking on one of the available game board squares.

Figure 8.1. Player X starts each game by clicking on one of the available game board squares.

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.

PlayerXand Player O alternate turns when selecting squares.

Figure 8.2. PlayerXand Player O alternate turns when selecting squares.

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.

Any attempt to click on a square that has already been selected will result in an error.

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.

At the conclusion of each round of play a popup dialog is displayed announcing the result.

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.

Player X has won by lining up three squares diagonally.

Figure 8.5. Player X has won by lining up three squares diagonally.

Working with Files and Folders

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.

Hint

Hint

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.

Retrieving Drive Information

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.

Retrieving Information about the Current Working Directory

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

Collecting Data about Files and Folders

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

Hint

Hint

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.

Trick

Trick

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.

Ensuring that Files Exist

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.

Specifying Absolute File and Path Names

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.

Specifying Relative Path Names

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.

Using the filedialog Window to Allow the User to Select a File

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.

Using the filedialog command to display a popup dialog that allows the userto selecta file.

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$.

Working with Files

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.

Hint

Hint

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.

Opening Files

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.

Hint

Hint

Remember, it is always a good idea to first check to see if a file exists before trying to open it.

Closing Files

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.

Errors result when a program is stopped while files remain open.

Figure 8.7. Errors result when a program is stopped while files remain open.

Reading from Files

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

Hint

Hint

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.

An example of a small text file.

Figure 8.8. An example of a small text file.

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.

Using a text file to populate the contents of a texteditor control.

Figure 8.9. Using a text file to populate the contents of a texteditor control.

Hint

Hint

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 Files

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.

Creating and Writing to New Files

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.

Appending Text to the End of an Existing File

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.

File and Folder Administration

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.

Renaming Files

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"

Trap

Trap

Be careful not to assign the file a name that is the same as another file. Otherwise, you’ll get an error.

Deleting Files

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

trap

trap

Be careful when working with the kill command. Once executed, you cannot reverse its effects. The file is permanently deleted from the computer. You will not find a copy of it in the Recycle Bin.

Creating New Folders

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.

Deleting Folders

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.

Trap

Trap

Be careful when working with the rmdir() function. Once executed, you cannot reverse its effects. The folder is permanently deleted from the computer. You will not find a copy of it in the Recycle Bin.

Back to the Tic Tac Toe Game

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.

Copies of the three bitmap images used to represent available game board squares as well as squares selected by Player X and Player O.

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.

Designing the Game

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:

  1. Create a new BASIC file and add initial comment statements.

  2. Define global variables and start the game play.

  3. Create the ManageGamePlay subroutine.

  4. Create the ProcessMove subroutine.

  5. Create the SwitchPlayerTurn subroutine.

  6. Create the LookForEndOfGame subroutine.

  7. Create the ResetGameBoard subroutine.

  8. Create the ClosePlay subroutine.

Creating a Just BASIC File Script

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.

Defining Global Variables and Initiating Game Play

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.

Displaying the Game Board

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.

Processing Player Moves

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").

Hint

Hint

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.

Hint

Hint

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.

Controlling Player Turns

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.

Looking for the End of the Game

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.

Preparing for a New Game

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

Terminating Game Play

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

The Final Result

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.

Summary

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.

 

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

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