Visual Basic Gaming Quiz 2

This game is very different from what you are learning in the textbook but I hope you will be able to learn something new from this tic tac toe game as a quiz creation from the text was near impossible.  This is the first time this class has been taught so let me know if you find any errors in the quiz.

Create the Project

Create a new Visual Basic.NET project. I used the Windows Application template and called it "Tic Tac Toe"

To make things easier for me I renamed Form1.vb to frmTicTacToe.vb by right clicking the file in the solutions explorer. I then selected the form and changed the text to "Tic-Tac-Toe" and the name to "frmTicTacToe".

Building the Main Menu

I then went to the toolbox and dragged over a MainMenu object. I decided to add two headings: "Options", which would have all the features of the game, and "About" which would have an all people to see a little information on the program. Under Options I added New Game, Difficulty, and Quit. And then I added the three difficulty settings: Easy, Medium, and Hard.

To make my code easier to understand, I gave each of these design elements a more descriptive name. The Options heading was originally called MenuItem1, so it was renamed "menuOptions". I repeated this process for all of the recently created menus.

Building the Grid

Instead of making images for the X's and O's, I decided to keep things simple and use the letter X and O. To create the grid I used 9 labels laid out in a 3x3 square. To create the first label I used the Toolbox and dragged it out to 8x8 using the dots as my reference measurement. I then changed the following settings:

        BackColor: White
        BorderStyle: FixedSingle
        Cursor: Hand
        Font: Tahoma, 36pt, style=bold
        Text: X
        TextAlign: MiddleCenter
        Name: lblGrid00

After everything was set, I dragged the label over to the top-left corner and then made 8 copies of it arranged in a 3x3 grid. I then renamed each label to correspond to the grid.

        lblGrid00       lblGrid01       lblGrid02
        lblGrid10       lblGrid11       lblGrid12
        lblGrid20       lblGrid21       lblGrid22

Since that's all that I'm putting in the windows I got rid of the negative space by dragging the window frame until only the grid could fit. Here's what the initial build looks like.

 

Tic Tac Toe Grid Layout

 

 

 

 

 

 

 

 

 

 

 

 

Game Actions

In every game there needs to be a way to start a new game, an opponent, a win/lose condition, and the ability to quit.

Starting the Game

There are two events that will trigger a new game. The first one is when the application first opens. The second will be when someone goes to Options ? New Game from the menu. For this part of the process there will be some actual coding instead of the drag and drop "programming".

We're going to need a few variables throughout the duration of the program.

Public Shared arrGrid(3, 3) As Integer
Public Shared intAIDifficulty As Integer
Public Shared intWhosTurn As Integer
Public Shared intWhosWinner As Integer

arrGrid is a 3x3 array that represents the game board, a 0 in the board will mean that the spot is still available. A 1 will mean that the player has possession of the space, while a 2 means that the computer opponent does. intAIDifficulty will store difficulty level which will be used when we started adding in the computer opponent. intWhosTurn knows whose turn it is, player 1 or 2. And, intWhosWinner will store a value between 0-3 representing if anybody has won yet. 0 means the game is still going on, while 3 means the game is a tie.

Private Enum Difficulty
      Easy = 0
      Medium = 1
      Hard = 2
End Enum

I also decided to enumerate the difficulty level. The Enum Difficulty isn't required, but if the program got long and the difficulty value was used a lot, it would save a few headaches. Some people might also want to enumerate the players or who's winning; it's up to you.

Below the Window Form Designer generated code I added the following lines:

Private Sub frmTicTacToe_Load(ByVal sender As _
System.Object, ByVal e As System.EventArgs) _
Handles MyBase.Load
      Call InitGrid()
      Call SetDifficulty(Difficulty.Medium)
      intWhosTurn = 1
End Sub ' frmTicTacToe_Load 

The frmTicTacToe_Load() subroutine may already be there, but if not, you'll definitely need to add it. This is what is executed when the program is first loaded (the programs first starting condition). Because there hasn't been any user input yet, I'll need to set up all the defaults and clear the grid.

Drawing The Grid

I created two subroutines and also let the system know that the player should have the first move. InitGrid() will simply set every value in our 3x3 array to 0, and then clear the game board. SetDifficulty() will not only set a difficulty variable, but also put a pretty check on next to difficulty selected.

Private Sub InitGrid()
      ' Sets all values in the grid array to 0
      For i As Integer = 0 To 2
             For j As Integer = 0 To 2
                     arrGrid(i, j) = 0
             Next
      Next
      Call DrawGrid()
End Sub ' InitGrid 

The first thing that happens in the program is to ensure all values in the array are set to 0. I accomplished this by using a nested for loop. It travels down each row setting the values to 0 and before it calls DrawGrid(). DrawGrid() will make the board representative of the array by replacing 1s with Os and 2s with Xs.

Private Sub DrawGrid()
      If arrGrid(0, 0) = 1 Then
             Me.lblGrid00.Text = "O"
      ElseIf arrGrid(0, 0) = 2 Then
             Me.lblGrid00.Text = "X"
      Else
             Me.lblGrid00.Text = ""
      End If
 
End Sub ' DrawGrid 

This is part of the DrawGrid() sub. For each position in the array, it'll check the value and set the label to its corresponding character. Nine of these condition statements are required to draw the whole board.

Setting The Difficulty

Private Sub SetDifficulty(ByVal intDifficulty As Integer)
      intAIDifficulty = intDifficulty
      Select Case intDifficulty
             Case 0
                     Me.menuEasy.Checked = True
                     Me.menuMedium.Checked = False
                     Me.menuHard.Checked = False
             Case 1
                     Me.menuEasy.Checked = False
                     Me.menuMedium.Checked = True
                     Me.menuHard.Checked = False
             Case 2
                     Me.menuEasy.Checked = False
                     Me.menuMedium.Checked = False
                     Me.menuHard.Checked = True
      End Select
End Sub ' SetDifficulty 

Whenever we called SetDifficult() with Difficulty.Medium, it stuck a 1 there and set intAIDifficulty to 1. I also wanted the players to know what level opponent they were competing against so Me.menuMedium.Check = True puts a little check to the left of Medium on our menu. Setting the other values to false ensure that there aren't any extra checks.

Now that the defaults are set up, all the program does is waits for a player to click a square. Using many of the routines already programmed with can set up the board for when a player calls a new game from the menu.

Using the Options Menu

I programmed a behavior for each item on the Options menu: new game, difficulty, and quit.

Private Sub menuNewGame_Click(ByVal sender _
As System.Object, ByVal e As System.EventArgs) Handles menuNewGame.Click
      Call InitGrid()
      intWhosWinner = 0
      If intWhosTurn = 2 Then
             Call AIMove()
      End If
End Sub ' menuNewGame_Click 

New game is similar to when the program starts, except that it will reset the intWhosWinner variable, and tell the computer opponent move if it is its turn. If you wanted the game to be two players, you could remove the AIMove() call.

Private Sub menuEasy_Click(ByVal sender _
As System.Object, ByVal e As System.EventArgs) Handles menuEasy.Click
      Call SetDifficulty(Difficulty.Easy)
End Sub ' menuEasy_Click 

For each difficulty level I added the following to the program. These lines use the SetDifficulty subroutine we set up earlier.

Private Sub menuQuit_Click(ByVal sender _
As System.Object, ByVal e As System.EventArgs) Handles menuQuit.Click
      Application.Exit()
End Sub ' menuQuit_Click 

To quit I just used the built-in Application.Exit() function.

 

Time for Xs and Os

Well, I've programmed a lot of code, but still can't play tic-tac-toe. Here's where that all changes.

Private Sub lblGrid00_Click(ByVal sender _
As System.Object, ByVal e As System.EventArgs) Handles lblGrid00.Click
      Call GridClick(0, 0)
End Sub ' lblGrid00_Click 

I created a subroutine for each label in the 3x3 grid that looks pretty close to this. Seems pretty simple, but let's take a look at what GridClick() does.

Programming the Meat

The first thing to do when a square is clicked, is make sure that the square isn't occupied and also that a winner hasn't already been declared.

If (arrGrid(intRow, intCol) = 0) And (intWhosWinner = 0) Then
 
End If

If the spot on the grid is empty and the game is still being played, then we'll give the square to whoever's turn it is.

If intWhosTurn = 1 Then
      arrGrid(intRow, intCol) = 1
      intWhosTurn = 2
ElseIf intWhosTurn = 2 Then
      arrGrid(intRow, intCol) = 2
      intWhosTurn = 1
End If
 
Call DrawGrid()

After that the display is redrawn by calling the DrawGrid() subroutine.

Call CheckWinLose()
 
If (intWhosTurn = 2) And (intWhosWinner = 0) Then
      Call AIMove()
End If

Near the end of each move, the program will need to check whether someone has got three in a row. CheckWinLose() will handle this and I'll show you the code in just a minute. If there isn't a winner and the player just moved, then it's time for the computer opponent. Once again, if you want this to be two players rather than playing a computer, just remove this line.

' Declare the winner
If intWhosWinner = 3 Then
      MessageBox.Show("It's a draw")
ElseIf intWhosWinner = 2 Then
      MessageBox.Show("You Lose")
ElseIf intWhosWinner = 1 Then
      MessageBox.Show("You Win")
End If

After CheckWinLose() is called, the program knows if we have a winner, tie, or whether to keep playing.

Have I Won Yet?

CheckWinLose() is the main reason I programmed this with a 3x3 array instead of just using the labels. Rather than having to do comparisons for every possible way to win, which is eight by the way, I was able to divide it out into three directions.

Dim intOCount As Integer = 0
Dim intXCount As Integer = 0
Dim intTotalCount As Integer = 0

First I'll introduce the counters. intOCount will let the game know if player 1 is the winner, intXCount player 2, and intTotalCount if there's a tie.

' Check horizontal for win or lose
For i As Integer = 0 To 2
      For j As Integer = 0 To 2
             If arrGrid(i, j) = 1 Then
                     intOCount += 1
                     intTotalCount += 1
             End If
 
             If arrGrid(i, j) = 2 Then
                     intXCount += 1
                     intTotalCount += 1
             End If
      Next
 
      If intOCount = 3 Then
             intWhosWinner = 1
      ElseIf intXCount = 3 Then
             intWhosWinner = 2
      End If
 
      intOCount = 0
      intXCount = 0
Next

Starting out by checking each row, number of spaces that player one and two have possession of are added up. If either has three, then the game is over. If not, then the counters are reset and the next row is checked. This is nearly the same for the columns. But before the columns are checked, if all squares are full then a tie is declared.

' Check for tie
If (intTotalCount = 9) And (intWhosWinner = 0) Then
      intWhosWinner = 3
Else
      intTotalCount = 0
End If

Since intTotalCount incremented any time there was an X or an O, if there are nine spaces filled then it might be a tie. The program will call it a tie unless a win overrides the assumption. After the check is performed, the program no longer needs to be incrementing intTotalCount.

' Check diagonal for win or lose
If arrGrid(0, 0) = 1 And arrGrid(1, 1) = 1 And arrGrid(2, 2) = 1 Then
      intWhosWinner = 1
ElseIf arrGrid(0, 0) = 2 And arrGrid(1, 1) = 2 And arrGrid(2, 2) = 2 Then
      intWhosWinner = 2
ElseIf arrGrid(0, 2) = 1 And arrGrid(1, 1) = 1 And arrGrid(2, 0) = 1 Then
      intWhosWinner = 1
ElseIf arrGrid(0, 2) = 2 And arrGrid(1, 1) = 2 And arrGrid(2, 0) = 2 Then
      intWhosWinner = 2
End If

After it checks for a tie, it'll need see if any of the players won vertically by using the same method as for horizontal winners. After that, the game checks to see if a diagonal winner exists.

At this point, if you removed the AIMove() lines, then you have a perfectly good version of two player tic-tac-toe.

 

Programming the AI

The intelligence was probably the most difficult part of programming the game. You have to learn how to win at tic-tac-toe, every time. Since I decided that our program was going to have 3 different difficulty settings: easy, medium, and hard, I had to decide what strategy each of these opponents would use. For E-Z mode, the program will just place Xs randomly (not much strategy at all). For the mid-level difficulty, it'll be able to know when it's about to win and lose and play accordingly (this is how most people actually play tic-tac-toe). For hard difficulty, it'll know the best way to play during any situation. A draw with this opponent is considered a perfect game (this is how experts play).

Private Sub AIMove()
      Dim intRandomX As Integer = RandomNumber(3, 0)
      Dim intRandomY As Integer = RandomNumber(3, 0)
 
      Select Case intAIDifficulty
             Case Difficulty.Easy
 
             Case Difficulty.Medium
 
             Case Difficulty.Hard
 
      End Select
End Sub ' AIMove 

I created a subroutine with a case statement for each difficulty. Since Easy is going to be completely random I created a couple variables to store a random horizontal position and a random vertical position.

E-Z Mode

Public Function RandomNumber(ByVal MaxNumber _
As Integer, ByVal MinNumber As Integer)
      Dim r As New Random(System.DateTime.Now.Millisecond)
      Return r.Next(MinNumber, MaxNumber)
End Function ' RandomNumber 

Here's the RandomNumber function. It uses the built in random class to generate a value between the numbers supplied, in our case 0, 1, or 2.

While arrGrid(intRandomX, intRandomY) <> 0
      intRandomX = RandomNumber(3, 0)
      intRandomY = RandomNumber(3, 0)
End While
Call GridClick(intRandomX, intRandomY)

The easy strategy looks like this. It gets a random position over and over until a blank spot is found. The other difficulty settings will also use this strategy after the programmed intelligence is exhausted.

Programming An Intermediate Opponent

' Check to see if it can win
For i As Integer = 0 To 2
      For j As Integer = 0 To 2
             If arrGrid(i, j) = 0 Then
                     arrGrid(i, j) = 2
 
                     Call CheckWinLose()
                     If intWhosWinner = 2 Then
                             intWhosWinner = 0
                             arrGrid(i, j) = 0
                             GridClick(i, j)
                             Return
                     Else
                             arrGrid(i, j) = 0
                     End If
             End If
      Next
Next

The first thing the medium opponent should do is see if it has a chance to win. It accomplishes this by placing a hypothetical move for every open position on the board. It then calls the CheckWinLose subroutine. If the move results in a win then it goes ahead and takes that move, otherwise, it'll set that spot back to empty move to the next space.

' Check to see if it will lose
For i As Integer = 0 To 2
      For j As Integer = 0 To 2
             If arrGrid(i, j) = 0 Then
                     arrGrid(i, j) = 1
 
                     Call CheckWinLose()
                     If intWhosWinner = 1 Then
                             intWhosWinner = 0
                             arrGrid(i, j) = 0
                             GridClick(i, j)
                             Return
                     Else
                             intWhosWinner = 0
                             arrGrid(i, j) = 0
                     End If
             End If
      Next
Next

If it's not possible for the computer to win, then it'll need to make sure that it won't lose on the players next turn. It accomplishes this in much the same manner as when it checked for winning conditions. A hypothetical move is made, but this time as the player. If the player would win based on this move, then the computer will move there instead. If the computer opponent can't win or lose in the next move, then it'll just play randomly.

The Game That Knew Too Much

The hard difficulty is where it gets tough. I read a tic-tac-toe strategy manual (how lame is that) and found out that the game is decided in the first 4 moves, everything after that can be won (or tied) by playing like the medium difficulty opponent. So what I needed to do was count have many moves had been made. Based on this and the position of the Xs and Os, the program knows how to move accordingly.

Dim intSquaresUsed As Integer = 0
For i As Integer = 0 To 2
      For j As Integer = 0 To 2
             If arrGrid(i, j) <> 0 Then
                     intSquaresUsed += 1
             End If
      Next
Next
 
Select Case intSquaresUsed
      Case 0
      Case 1
      Case 2
      Case 3
      Case 4
      Case Is > 4
End Select

While I'm not going to go into all the possible moves (you can read the strategy guide if you're that interested), I'll cover the basic structure. First it'll go through each spot on the grid to see how many are filled. This will let us know what move the game is on and let us pick the appropriate strategy. After the fourth move, then it'll just play using the same strategy as the medium difficulty opponent.

The End

Put all this together and you've programmed a game that will be fun for almost five minutes. But hopefully you learned some useful Visual Basic.NET programming in the process.