Click here to Skip to main content
15,881,882 members
Articles / Programming Languages / Visual Basic
Article

Yet Another Tic Tac Toe

Rate me:
Please Sign up or sign in to vote.
4.53/5 (17 votes)
2 Nov 20034 min read 149.1K   2.3K   30   10
Yet Another Tic Tac Toe program. This example breaks the tic tac toe game into three parts. Model data, UI, and AI.

Sample Image - yetanothertictactoe.jpg

Introduction

I was watching my girl friend play web games the other day, including playing tic-tac-toe, and decided to give it a try in VB.NET. With-in about 30 minutes I had a game up and running in which I could play against my step-daughter. My implementation separated the game logic into three areas: model, UI, and an AI class. The associated source files for these areas are TicTacToeGame.vb, frmTicTacToe.vb, and TicTacToeAI.vb. General programming areas used in this project include Enums, Events, and Overloading a property.

After implementing my version of tic-tac-toe, I researched how others have implemented the game. Grant Richard has written a good article in the C# area here.

The Tic-Tac-Toe Model

A tic-tac-toe board can have three entries in a square: Nothing, X, and O. These are represented by an Enum called GridEntry. The Enums are appropriately called NoEntry, PlayerX, and PlayerO. A multi-dimensional grid of 3x3 squares of the type GridEntry represents the playing board. Also note that the enum is inside the class. This allows the programmer to code CLASSNAME.ENUM from other classes.

0,0 -- 0,1 -- 0,2
1,0 -- 1,1 -- 1,2
2,0 -- 2,1 -- 2,2

VB.NET
Public Class TicTacToe
    Public Enum GridEntry 
        NoEntry = 0
        PlayerX = 1
        PlayerO = 2
    End Enum
 
    Private m_iGrid(2,2) as GridEntry
End Class

The only property of the tic-tac-toe model class is Square. This property gets/sets a type of GridEntry. A user can only set a Square to either X or O. If they try to overwrite a square once it has been written to, the set property ignores the set operation. The set property also checks to see if there is a winner condition or draw condition and raises the appropriate event.

VB.NET
Public Event TicTacToeWinOccured(ByVal aWinner as GridEntry) 'Win Condition
Public Event Cat() ‘Draw Condition

Public Property Square(iRow as Integer, iCol as Integer) as GridEntry
    Get
        Return m_iGrid(iRow, iCol)
    End Get
    Set(ByVal Value as GridEntry)
        If m_iGrid(iRow, iCol) = Grid.NoEntry And _
           m_bHasWinOccured = False Then
            m_iGrid(iRow, iCol) = Value
                    m_iWinner = CheckForWin()
            If m_iWinner = GridEntry.NoEntry Then
                If CheckForDraw() = True
                    RaiseEvent Cat()
                End If
            Else
                m_bHasWinOccured = True
                RaiseEvent TicTacToeWinOccured(m_iWinner)
            End If
          End If
    End Set
End Property

While writing the AI class, I decided to encapsulate iRow and iCol. This class was called SquareCoordinate which would allow me to pass one parameter to a function instead of iRow and iCol. So the property Square was overloaded to take a SquareCoordinate variable.

VB.NET
Public Property Square(aSC as SquareCoordinate) as GridEntry
    Get
        Return m_iGrid(aSC.Row, aSC.Column)
    End Get
    Set(Value as GridEntry)
          Me.Square(aSC.Row, aSC.Column) = Value
     End Set
End Property

The User Interface

The UI portion of this project has 9 labels in a group box which when clicked on call lblGrid_Click. This is setup using an AddHandler command and using the AddressOf operator to point to lblGrid_Click. Each control is named lblGridXX where XX is the grid coordinates for the square as noted above. For example 00 is the first square. The subroutine DrawGrid updates the labels with the data in the tic-tac-toe model.

VB.NET
AddHandler lblGrid00.Click, AddressOf lblGrid_Click.

Public Sub lblGrid_Click(sender as Object, args as EventArgs)
    Try
        Dim aLabelControl As Label = CType(sender, Label)
        Dim sName as String = aLabelControl.Name
        Dim iRow as Integer = sName.SubString(sName.Length-2,1)
        Dim iCol as Integer = sName.SubString(sName.Length-1, 1)
          If m_TicTacToeGame.Square(iRow, iCol) = TicTacToe.GridEntry.NoEntry Then
               m_TicTacToeGame.Square(iRow, iCol) = m_iCurrentPlayer
               ToggleCurrentPlayer()
               DrawGrid()
          End If
    Catch ex as Exception
        Stop  ‘Error Handling to be completed
    End Try
End Sub

Private Sub DrawGrid()
    Dim iRow as Integer
    Dim iCol as Integer
    Dim aLabelControl as Label

    For iRow = 0 to 2
        For iCol = 0 to 2
            Try
                    aLabelControl = GetLabelControl(iRow, iCol)
                   Select Case m_TicTacToeGame.Square(iRow, iCol)
                 Case TicTacToe.GridEntry.NoEntry
                      aLabelControl.Text = ""
                 Case TicTacToe.GridEntry.PlayerX
                      aLabelControl.Text = "X"
                 Case TicTacToe.GridEntry.PlayerO
                      aLabelControl.Text = "O"
                End Select
        Catch ex as Exception
   
        End Try
        Next
    Next
End Sub

Win/Draw Condition Logic

The win condition logic in the tictactoe class checks for a win condition by checking the squares horizontally, vertically, and diagonally. If the same player is in all three squares in one direction, that player is returned from the win logic code; otherwise NoEntry is returned.

VB.NET
Private Function CheckForHorizontalWin() as GridEntry
     Dim iRow as Integer
     Dim iFirstSquarePlayer as GridEntry
     For iRow = 0 to 2
         iFirstSquarePlayer = m_iGrid(iRow,0)
        If iFirstSquarePlayer = m_iGrid(iRow,1) and _
           iFirstSquarePlayer = m_iGrid(iRow,2) Then
            Return iFirstSquarePlayer
        End If
    Next
    Return GridEntry.NoEntry
End Function

The draw logic is pretty simple. We loop through all squares, and if we find a NoEntry, return false; otherwise if they are all full, return true for a draw.

VB.NET
Private Function CheckForDraw() as Boolean
    Dim iRow as Integer, iCol as Integer
    For iRow = 0 to 2
        For iCol = 0 to 2
            If m_iGrid(iRow,iCol) = GridEntry.NoEntry Then Return False
        Next
    Next
    Return True
End Function

AI Logic

The AI Logic is contained in the TicTacToeAI class. There are four general rules that the AI class follows:

  • Win if possible
  • Block opponent from winning
  • Determine a Best Bet move to setup a future win
  • Make any valid move

A MoveEntry class has been created to contain possible moves. This is a private class only accessiable to the TicTacToeAI class. This class contains two variables: a SquareCoordinate, and a MoveType of Winning, Blocking, or Normal.

VB.NET
Private Enum MoveType
    Normal = 0
    Blocking = 1
    Winning = 2
End Enum

The TicTacToeAI class has only one public method GetAIMove(ByVal aTTT as TicTacToe) and returns a SquareCoordinate. The workhorse of the AI is the private function DetermineMoveEntry(aSC as SquareCoordinate) which classifies a SquareCoordinate to either a winning move, a blocking move, or a normal move. It first checks horizontal, vertical, and finally diagonal. To check diagonal, another enum is used along with a function to determine which tic tac toe square is being examined.

VB.NET
Private Enum SquareType
    UpperLeft = 1
    UpperRight = 2
    Center = 3
    BottomLeft = 4
    BottomRight = 5
    Other = 6
End Enum

Private Function DetermineMoveEntry(ByVal aSC as SquareCoordinate) _
                                    as MoveEntry
    Dim iRow as Integer = aSC.Row
    Dim iCol as Integer = aSC.Column
    Dim iGE1 as TicTacToe.GridEntry
    Dim iGE2 as TicTacToe.GridEntry
 
    ' ... Code Omitted for Horizontal and Vertical

    Select Case GetSquareType(aSC)
        Case SquareType.UpperLeft
            iGE1 = m_CurrentBoard.Square(1,1)
            iGE2 = m_CurrentBoard.Square(2,2)

            If CheckPossibleWin(iGE1, iGE2, m_iAIPlayer = True Then
                Return New MoveEntry(aSC, MoveType.Winning)
            End If
        
            If CheckPossibleWin(iGE1, iGE2, m_iHumanPlayer) = True Then
                Return New MoveEntry(aSC, MoveType.Blocking)
            End If
 
        ' ... Code omitted for other cases
    End Select

    Return New MoveEntry(aSC, MoveType.Normal)
End Function

Finally GetAIMove stores each MoveEntry into an ArrayList, and then checks the ArrayList for winning/blocking moves. If there are no winning moves or blocking moves, it tries to determine if there are any best moves by calling DetermineBestNormalMove(aNormalMoveList as ArrayList). Otherwise it picks a normal move at random.

I had fun writting this tic-tac-toe program. I may in the future examine other implementations of the base model and the logic for determining win, draw, and AI moves. For example, Grant Richard uses a single array of 9 squares numbering each square 1 to 9 with his win conditions contained in an array. His article is here.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
Questionask algorithm Pin
Member 121843154-Dec-15 6:40
Member 121843154-Dec-15 6:40 
Questionhdi Pin
Member 921795327-Jul-12 0:03
Member 921795327-Jul-12 0:03 
Questionhai Pin
Member 921795327-Jul-12 0:01
Member 921795327-Jul-12 0:01 
General[Message Deleted] Pin
Danny Rodriguez27-Jan-08 9:16
Danny Rodriguez27-Jan-08 9:16 
Generalverifying winner C++ Pin
flavio.rodriguez31-Aug-07 20:48
flavio.rodriguez31-Aug-07 20:48 
QuestionTicTacToe Pin
rtsalvat12-Dec-05 13:41
rtsalvat12-Dec-05 13:41 
GeneralHmm cant make head or toe out of this Pin
Anonymous5-Oct-04 10:05
Anonymous5-Oct-04 10:05 
GeneralRe: Hmm cant make head or toe out of this Pin
idiot hater7-Oct-04 23:43
sussidiot hater7-Oct-04 23:43 
you are an idiot D'Oh! | :doh: , anyone who reads this, please feel free to post about how idiotic and arrogant the above poster really is.
GeneralRe: Hmm cant make head or toe out of this Pin
datahunter200923-Jul-05 7:16
datahunter200923-Jul-05 7:16 
GeneralRe: Hmm cant make head or toe out of this Pin
Sean Carmody19-Oct-09 16:00
Sean Carmody19-Oct-09 16:00 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.