It is time to turn your attention to this book's final game project, the Tic-Tac-Toe game. You will create the Tic-Tac-Toe game by following the same five basic development steps that you have followed for all preceding game projects.
The Tic-Tac-Toe game is played on a single window and is made up of one form and the 18 controls listed in Table 11.1.
Control Type | Control Name | Description |
---|---|---|
Panel1 | pnlLeft | Used to represent the left vertical bar on the game board |
Panel2 | pnlRight | Used to represent the right vertical bar on the game board |
Panel3 | pnlTop | Used to represent the top horizontal bar on the game board |
Panel4 | pnlBottom | Used to represent the bottom horizontal bar on the game board |
PictureBox1 | pbxA1 | The left PictureBox control on the first row of th board game |
PictureBox2 | pbxA2 | The middle PictureBox control on the first row of the game board |
PictureBox3 | pbxA3 | The right PictureBox control on the first row of the game board |
PictureBox4 | pbxB1 | The left PictureBox control on the second row of the game board |
PictureBox5 | pbxB2 | The middle PictureBox control on the second row of the game board |
PictureBox6 | pbxb3 | The right PictureBox control on the second row of the game board |
PictureBox7 | pbxC1 | The left PictureBox control on the third row of the game board |
PictureBox8 | pbxC2 | The middle PictureBox control on the third row of the game board |
PictureBox9 | pbxC3 | The right PictureBox control on the third row of the game board |
Button1 | btnPlay | Used to initiate game play |
Button2 | btnExit | Used to terminate the game |
Label1 | lbloutput | Identifies the TextBox control that is used to display status messages |
TextBox1 | txtOutput | Used to display status messages |
ImageList1 | imlsquares | Stores an indexed collection of graphics used to represent player moves |
The first step in creating the Tic-Tac-Toe game is to open up Visual Basic and create a new project, as outlined below.
If you have not already done so, start up Visual Basic 2005 Express Edition and then click on File and select New Project. The New Project dialog will appear.
Select the Windows Application template.
Type Tic-Tac-Toe as the name of your new application in the Name field located at the bottom of the New Project window.
Click on OK to close the New Project dialog.
Visual Basic will create a new project for you and display a form, which you will then use to design your new game's user interface.
The first step in laying out the user interface is to add controls to the form and to move and resize them to the appropriate locations. As you go through each step, make sure that you reference Figure 11.13 so that you'll know where each control needs to be placed and what size it needs to be.
Begin by setting the Size property of the form to 605, 591.
Next, create the grid lines that organize the game board by adding four Panel controls and resizing them as shown in Figure 11.13.
Add PictureBox controls inside each of the game board's nine cells and resize them until they take up almost all of the available space.
Add two Button controls to the bottom left hand corner.
Add a TextBox control to the bottum right-hand side of the game board and resize it as shown in Figure 11.13.
Add a Label control and place it just over the upper left-hand corner of the TextBox control.
Finally, add an ImageList control.
Figure 11.13: Completing the interface design for the Tic-Tac-Toe game.
At this point, the overall layout of the Tic-Tac-Toe game's user interface is now complete. You can start making changes to the form and control properties.
Let's begin by making the required changes to properties belonging to the form object, as listed in Table 11.2.
Property | Value |
---|---|
Name | frmMain |
ControlBox | False |
Cursor | Hand |
FormBorderStyle | Fixed3D |
StartPosition | CenterScreen |
Text | Tic-Tac-Toe |
Make the property changes shown in Table 11.3 to the Panel controls.
Control | Property | Value |
---|---|---|
Panel1 | Name | pnlLeft |
BackColor | Black | |
Panel2 | Name | pnlRight |
BackColor | Black | |
Panel3 | Name | pnlTop |
BackColor | Black | |
Panel4 | Name | pnlBottom |
BackColor | Black |
Make the property changes shown in Table 11.4 to the PictureBox controls.
Control | Property | Value |
---|---|---|
PictureBox1 | Name | pbxA1 |
Enabled | False | |
Size | 164, 134 | |
SizeMode | StretchImage | |
PictureBox2 | Name | pbxA2 |
Enabled | False | |
Size | 164, 134 | |
SizeMode | StretchImage | |
PictureBox3 | Name | pbxA2 |
Enabled | False | |
Size | 164, 134 | |
SizeMode | StretchImage | |
PictureBox4 | Name | pbxB1 |
Enabled | False | |
Size | 164, 134 | |
SizeMode | StretchImage | |
PictureBox5 | Name | pbxB2 |
Enabled | False | |
Size | 164, 134 | |
SizeMode | StretchImage | |
PictureBox6 | Name | pbxB3 |
Enabled | False | |
Size | 164, 134 | |
SizeMode | StretchImage | |
PictureBox7 | Name | pbxC1 |
Enabled | False | |
Size | 164, 134 | |
SizeMode | StretchImage | |
PictureBox8 | Name | pbxC2 |
Enabled | False | |
Size | 164, 134 | |
SizeMode | StretchImage | |
PictureBox9 | Name | pbxC3 |
Enabled | False | |
Size | 164, 134 | |
SizeMode | StretchImage |
Make the property changes shown in Table 11.5 to the Button controls.
Control | Property | Value |
---|---|---|
Button1 | Name | btnPlay |
Text | Play | |
Button2 | Name | btnExit |
Text | Exit |
Make the property changes shown in Table 11.6 to the Label control.
Control | Property | Value |
---|---|---|
Label1 | Name | lblOutput |
Font.Bold | True | |
Text | Status |
Make the property changes shown in Table 11.7 to the TextBox control.
Control | Property | Value |
---|---|---|
TextBoxl | Name | txtOutput |
Font.Bold | True | |
Readonly | True | |
TabStop | False |
Finally, change the name of the Name property for the ImageList control to imlSquares. Change the ImageList control's ImageSize property to 164, 134 and then add the following bitmap images to its Images property (collection) as shown in Table 11.8. You'll find copies of the Bitmap images required to complete this project, along with the source code, on this book's companion web site.
Property | File | Index No. |
---|---|---|
Name | X.bmp | 0 |
Name | O.bmp | 1 |
Name | Blank.bmp | 2 |
That's it. The user interface for this game is now complete. Let's move on to the next step and begin adding program code.
The first task in putting together the program code for the Tic-Tac-Toe game is to define class-level variables, as shown below. These variables represent values used by two or more procedures within the application.
Public Class frmMain Private strPlayer As String = "" 'Used to track whose turn it is 'Declare variables representing game board cells Private strpbxA1 As String = "Open" Private strpbxA2 As String = "Open" Private strpbxA3 As String = "Open" Private strpbxB1 As String = "Open" Private strpbxB2 As String = "Open" Private strpbxB3 As String = "Open" Private strpbxC1 As String = "Open" Private strpbxC2 As String = "Open" Private strpbxC3 As String = "Open" End Class
The first statement defines a variable named strPlayer, which is used to keep track of each player's turn. The remaining statements are used to keep track of when each of the game board cells have been selected by a player.
The form's Load event procedure, shown below, is responsible for preparing the game for initial play. This is accomplished by calling on two custom procedures.
'This procedure executes procedures required to set up the game Private Sub frmMain_Load(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles MyBase.Load SetGameDefaults() 'Call procedure that sets default assignments ClearBoard() 'Call procedure that clears the game board End Sub
The first procedure called by the form's Load event procedure is the SetGameDefaults procedure, shown below. When called, this procedure displays instructions in the TextBox control and sets Player X as the first player.
'This procedure sets default assignments Private Sub SetGameDefaults() txtOutput.Text = "Click on Play to begin" 'Display opening message strPlayer = "Player X" 'Set Player X to go first End Sub
The second procedure called by the form's Load event procedure is the ClearBoard procedure, shown below. This procedure is responsible for clearing off the game board by displaying blank squares in each of the game's PictureBox controls. The procedure also sets the values assigned to the variables used to track the status of each game board cell to Open.
'This procedure clears out the game board Private Sub ClearBoard() 'Load a blank image into each game board cell pbxAl.Image = imlSquares.Images(2) pbxA2.Image = imlSquares.Images(2) pbxA3.Image = imlSquares.Images(2) pbxB1.Image = imlSquares.Images(2) pbxB2.Image = imlSquares.Images(2) pbxB3.Image = imlSquares.Images(2) pbxC1.Image = imlSquares.Images(2) pbxC2.Image = imlSquares.Images(2) pbxC3.Image = imlSquares.Images(2) 'Mark each game board cell as open and available for selection strpbxA1 = "Open" strpbxA2 = "Open" strpbxA3 = "Open" strpbxB1 = "Open" strpbxB2 = "Open" strpbxB3 = "Open" strpbxC1 = "Open" strpbxC2 = "Open" strpbxC3 = "Open" End Sub
Game play cannot begin until the first player (Player x) clicks on the Button control labeled Play, at which time the btnPlay procedure, shown below, is executed. This procedure is responsible for executing two custom procedures. The first procedure that is called is the ClearBoard procedure, which we have already examined. The second procedure called is the PlayGame procedure.
'This procedure executes when the button labeled Play is clicked Private Sub btnPlay_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles btnPlay.Click ClearBoard() 'Call the procedure that clears out the game board PlayGame() 'Call the procedure that begins game play End Sub
The PlayGame procedure, shown below, displays a text message in the game's TextBox control in order to identify whose turn it is. In addition, the procedure enables each of the PictureBox controls on the game board. This allows the first player to make the first move. Finally, this procedure disables the btnPlay Button control.
'This procedure begins game play Private Sub P1ayGame() 'Post message that identifies whose turn it is txtOutput.Text = strPlayer & "'s turn." 'Enable all game board cells pbxA1.Enabled = True pbxA2.Enabled = True pbxA3.Enabled = True pbxB1.Enabled = True pbxB2.Enabled = True pbxB3.Enabled = True pbxC1.Enabled = True pbxC2.Enabled = True pbxC3.Enabled = True btnPlay.Enabled = False 'Disable access to the button labeled Play End Sub
Players can end the game at any time by clicking on the Button control labeled Exit, in which case the btnExit procedure, shown below, is executed.
'This procedure executes when the button labeled Exit is clicked Private Sub btnExit_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles btnExit.Click Application.Exit() End Sub
Each cell on the game board is made up of a PictureBox control. The following statements make up the Click event procedure for the game's first PictureBox control.
'This procedure executes when a player clicks on the first cell 'in the first row Private Sub pbxA1_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles pbxA1.Click Dim strGameOver As String = "" 'Used to track game status 'Notify the player if the cell has already been selected If strpbxA1 <> "Open" Then txtOutput.Text = "The square has already been taken." & _ ControlChars.CrLf & strPlayer & "'s turn." Return 'Leave the Sub procedure End If If strPlayer = "Player X" Then pbxA1.Image = imlSquares.Images(0) strpbxA1 = "Player X" Else pbxA1.Image = imlSquares.Images(1) strpbxA1 = "Player O" End If 'Call the procedure that checks to see if the game has been won strGameOver = CheckForWinner() 'Call the procedure that switched player turns or displays a 'message declaring a winner DetermineGameStatus(strGameOver) End Sub
This procedure declares a local variable named strGameOver. Next, an If…Then code block is used to check whether the cell has already been selected by examining the value assigned to the corresponding strpbxA1 variable. If the cell is available, an If…Then…Else code block displays a graphic representing the current player in the call. A call is then made to the CheckForWinner function procedure. If the game has been won, the CheckForWinner Function procedure will return a string of either Player X or Player O, which is then passed on to the DetermineGameStatus procedure.
The code for the second PictureBox control in the first row is shown below. As you can see, except for the changes in references that reflect the second cell instead of the first cell, the code is identical to that of the pbxA1_Click procedure.
'This procedure executes when a player clicks on the second cell 'in the first row Private Sub pbxA2_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles pbxA2.Click Dim strGameOver As String = "" 'Used to track game status 'Notify the player if the cell has already been selected If strpbxA2 <> "Open" Then txtOutput.Text = "The square has already been taken." &_ ControlChars.CrLf & strPlayer & "'s turn." Return 'Leave the Sub procedure End If If strPlayer = "Player X" Then pbxA2.Image = imlSquares.Images(0) strpbxA2 = "Player X" Else pbxA2.Image = imlSquares.Images(1) strpbxA2 = "Player O" End If 'Call the procedure that checks to see if the game has been won strGameOver = CheckForWinner() 'Call the procedure that switched player turns or displays a 'message declaring a winner DetermineGameStatus(strGameOver) End Sub
The code for the third PictureBox control on the first row is shown below.
'This procedure executes when a player clicks on the third cell 'in the first row Private Sub pbxA3_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles pbxA3.Click Dim strGameOver As String = "" 'Used to track game status 'Notify the player if the cell has already been selected If strpbxA3 <> "Open" Then txtOutput.Text = "The square has already been taken." &_ ControlChars.CrLf & strPlayer & "'s turn." Return 'Leave the Sub procedure End If If strPlayer = "Player X" Then pbxA3.Image = imlSquares.Images(0) strpbxA3 = "Player X" Else pbxA3.Image = imlSquares.Images(1) strpbxA3 = "Player O" End If 'Call the procedure that checks to see if the game has been won strGameOver = CheckForWinner() 'Call the procedure that switched player turns or displays a 'message declaring a winner DetermineGameStatus(strGameOver) End Sub
The code for the first PictureBox control on the second row is shown below.
'This procedure executes when a player clicks on the first cell 'in the second row Private Sub pbxB1_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles pbxB1.Click Dim strGameOver As String = "" 'Used to track game status 'Notify the player if the cell has already been selected If strpbxB1 <> "Open" Then txtOutput.Text = "The square has already been taken." &_ ControlChars.CrLf & strPlayer & "'s turn." Return 'Leave the Sub procedure End If If strPlayer = "Player X" Then pbxB1.Image = imlSquares.Images(0) strpbxB1 = "Player X" Else pbxB1.Image = imlSquares.Images(1) strpbxB1 = "Player O" End If 'Call the procedure that checks to see if the game has been won strGameOver = CheckForWinner() 'Call the procedure that switched player turns or displays a 'message declaring a winner DetermineGameStatus(strGameOver) End Sub
The code for the second PictureBox control on the second row is shown below.
'This procedure executes when a player clicks on the second cell 'in the second row Private Sub pbxB2_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles pbxB2.Click Dim strGameOver As String = "" 'Used to track game status 'Notify the player if the cell has already been selected If strpbxB2 <> "Open" Then txtOutput.Text = "The square has already been taken." &_ ControlChars.CrLf & strPlayer & "'s turn." Return 'Leave the Sub procedure End If If strPlayer = "Player X" Then pbxB2.Image = imlSquares.Images(0) strpbxB2 = "Player X" Else pbxB2.Image = imlSquares.Images(1) strpbxB2 = "Player O" End If 'Call the procedure that checks to see if the game has been won strGameOver = CheckForWinner() 'Call the procedure that switched player turns or displays a 'message declaring a winner DetermineGameStatus(strGameOver) End Sub
The code for the third PictureBox control on the second row is shown below.
'This procedure executes when a player clicks on the third cell 'in the second row Private Sub pbxB3_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles pbxB3.Click Dim strGameOver As String = "" 'Used to track game status 'Notify the player if the cell has already been selected If strpbxB3 <> "Open" Then txtOutput.Text = "The square has already been taken." & _ ControlChars.CrLf & strPlayer & "'s turn." Return 'Leave the Sub procedure End If If strPlayer = "Player X" Then pbxB3.Image = imlSquares.Images (0) strpbxB3 = "Player X" Else pbxB3.Image = imlSquares.Images(1) strpbxB3 = "Player O" End If 'Call the procedure that checks to see if the game has been won strGameOver = CheckForWinner() 'Call the procedure that switched player turns or displays a 'message declaring a winner DetermineGameStatus(strGameOver) End Sub
The code for the first PictureBox control on the third row is shown below.
'This procedure executes when a player clicks on the first cell 'in the third row Private Sub pbxC1_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles pbxC1.C1ick Dim strGameOver As String = "" 'Used to track game status 'Notify the player if the cell has already been selected If strpbxC1 <> "Open" Then txtOutput.Text = "The square has already been taken." & _ ControlChars.CrLf & strPlayer & "'s turn." Return 'Leave the Sub procedure End If If strPlayer = "Player X" Then pbxC1.Image = imlSquares.Images(0) strpbxC1 = "Player X" Else pbxC1.Image = imlSquares.Images(1) strpbxC1 = "Player O" End If 'Call the procedure that checks to see if the game has been won strGameOver = CheckForWinner() 'Call the procedure that switched player turns or displays a 'message declaring a winner DetermineGameStatus(strGameOver) End Sub
The code for the second PictureBox control on the third row is shown below.
'This procedure executes when a player clicks on the second cell 'in the third row Private Sub pbxC2_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles pbxC2.Click Dim strGameOver As String = "" 'Used to track game status 'Notify the player if the cell has already been selected If strpbxC2 <> "Open" Then txtOutput.Text = "The square has already been taken." & _ ControlChars.CrLf & strPlayer & "'s turn." Return 'Leave the Sub procedure End If If strPlayer = "Player X" Then pbxC2.Image = imlSquares.Images(0) strpbxC2 = "Player X" Else pbxC2.Image = imlSquares.Images(1) strpbxC2 = "Player O" End If 'Call the procedure that checks to see if the game has been won strGameOver = CheckForWinner() 'Call the procedure that switched player turns or displays a 'message declaring a winner DetermineGameStatus(strGameOver) End Sub
The code for the third PictureBox control on the third row is shown below.
'This procedure executes when a player clicks on the third cell 'in the third row Private Sub pbxC3_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles pbxC3.Click Dim strGameOver As String = "" 'Used to track game status 'Notify the player if the cell has already been selected If strpbxC3 <> "Open" Then txtOutput.Text = "The square has already been taken." & _ ControlChars.CrLf & strPlayer & "'s turn." Return 'Leave the Sub procedure End If If strPlayer = "Player X" Then pbxC3.Image = imlSquares.Images(0) strpbxC3 = "Player X" Else pbxC3.Image = imlSquares.Images(1) strpbxC3 = "Player O" End If 'Call the procedure that checks to see if the game has been won strGameOver = CheckForWinner() 'Call the procedure that switched player turns or displays a 'message declaring a winner DetermineGameStatus(strGameOver) End Sub
The CheckForWinner Function procedure, shown below, is responsible for executing a collection of If…Then statements to see if the current player has won the game. This procedure checks each row and column, as well as diagonally. In addition, the procedure checks to see if the two players have tied, which occurs when all nine cells have been selected without a winner being declared.
'This procedure determines whether the game has been won and by whom Function CheckForWinner() As String 'Check the first row for a winner If strpbxA1 = strPlayer Then If strpbxA2 = strPlayer Then If strpbxA3 = strPlayer Then Return strPlayer End If End If End If 'Check the second row for a winner If strpbxB1 = strPlayer Then If strpbxB2 = strPlayer Then If strpbxB3 = strPlayer Then Return strPlayer End If End If End If 'Check the third row for a winner If strpbxC1 = strPlayer Then If strpbxC2 = strPlayer Then If strpbxC3 = strPlayer Then Return strPlayer End If End If End If 'Check the first column for a winner If strpbxA1 = strPlayer Then If strpbxB1 = strPlayer Then If strpbxC1 = strPlayer Then Return strPlayer End If End If End If 'Check the second column for a winner If strpbxA2 = strPlayer Then If strpbxB2 = strPlayer Then If strpbxC2 = strPlayer Then Return strPlayer End If End If End If 'Check the third column for a winner If strpbxA3 = strPlayer Then If strpbxB3 = strPlayer Then If strpbxC3 = strPlayer Then Return strPlayer End If End If End If 'Check diagonally from top-left to bottom-right for a winner If strpbxA1 = strPlayer Then If strpbxB2 = strPlayer Then If strpbxC3 = strPlayer Then Return strPlayer End If End If End If 'Check diagonally from top-right to bottom-left for a winner If strpbxA3 = strPlayer Then If strpbxB2 = strPlayer Then If strpbxC1 = strPlayer Then Return strPlayer End If End If End If 'Check to see if the game has resulted in a tie Select Case "Open" 'Check each cell to see if it has been assigned to a player Case strpbxA1 Case strpbxB1 Case strpbxB1 Case strpbxB1 Case strpbxB2 Case strpbxB3 Case strpbxC1 Case strpbxC2 Case strpbxC3 Case Else 'This option executes if all cells have been assigned Return "Tie" End Select Return "" End Function
The DetermineGameStatus procedure, shown below, processes a single argument that identifies whether the game has been won, lost, or is still in progress. If the game is still in progress, the SwitchPlayers procedure is called in order to ready the game for the next player's turn. If the game has been won, the procedure enables the Button control labeled Play, calls the DisableSquares procedure, and displays a text message in the game's TextBox control identifying the winner of the game. Finally, if the game has ended in a tie, the btnPlay control is enabled, the DisableSquares procedure is called, and a text message is displayed in the game's TextBox control indicating that a tie has occurred.
'This procedure determines whether or not the game is over Private Sub DetermineGameStatus(ByVal strGameOver As String) If strGameOver = "" Then 'The game is not over yet SwitchPlayers() 'Call procedure that switches player turns 'Post message stating that it is time for players to switch turns txtOutput.Text = strPlayer & "'s turn." Else If strGameOver <> "Tie" Then 'There is a winner btnPlay.Enabled = True 'Enable the button labeled Play 'Call procedure that disables game board cells DisableSquares() 'Display game over message txtOutput.Text = "Game over. " & strGameOver & " has won." Else 'The game has resulted in a tie btnPlay.Enabled = True 'Enable the button labeled Play 'Call procedure that disables game board cells DisableSquares() 'Display game over message txtOutput.Text = "Game over. There was no winner." End If End If End Sub
The SwitchPlayers procedure, shown below, is very straightforward. When called, it changes the value assigned to the strPlayer variable to reflect the next player.
'This procedure is responsible for toggling between player turns Private Sub SwitchPlayers() If strPlayer = "Player X" Then strPlayer = "Player O" Else strPlayer = "Player X" End If End Sub
The DisableSquares procedure, shown below, is the last procedure in the application. Its job is to disable each of the game board PictureBox controls in order to keep players from trying to take another turn once the game has ended.
'This procedure disables all game board cells Private Sub DisableSquares() pbxA1.Enabled = False pbxA2.Enabled = False pbxA3.Enabled = False pbxB1.Enabled = False pbxB2.Enabled = False pbxB3.Enabled = False pbxC1.Enabled = False pbxC2.Enabled = False pbxC3.Enabled = False End Sub
OK. That's it. The Tic-Tac-Toe game is now ready to run. Go ahead and run the game by pressing F5 and make sure that everything works like it is supposed to. If you run into any problems, try using the troubleshooting tips presented in this chapter to track down any errors. Once things are in order, pass it around to a few of your friends and ask them what they think.