Skip to content

JoeLumbley/Tic-Tac-Toe

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

70 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Tic-Tac-Toe

Three in a row game, also known as Noughts and Crosses or X's and O's.

This app is resizable, supports mouse input and has a computer player. Works with Windows 10 and 11.

001

Set Up Board

The Board is a 3x3 grid of cells where players place their mark to try to get three in a row. We represent The Board as a two-dimensional array of cells, where each cell can be empty or occupied by a player's mark.

We define the Board as a two-dimensional array that holds the state of each cell.


Private ReadOnly Board(2, 2) As Cell

We use the Cell enumeration to represent the possible states of each cell.


Enum Cell
        Empty
        X
        O
End Enum

The possible states of each cell on the Board being: Empty, X, or O.

002

We initialize the Board to all Empty cells using a loop:


Private Sub InitializeBoard()

        For X = 0 To 2

            For Y = 0 To 2

                Board(X, Y) = Cell.Empty

            Next

        Next

End Sub

This will set every cell on the Board to Cell.Empty.

Now we're ready to play.

Making Moves

For the human player we handle mouse clicks on the form to update the state of the Board.


Private Sub Form1_MouseClick(sender As Object, e As MouseEventArgs) Handles MyBase.MouseClick

        UpdateMouse(e)

End Sub

We handle clicks on a square by converting form coordinates to board coordinates.

If the square is empty we mark the square with the player's mark.

We then check for a win or draw using the CheckForWin and CheckForDraw methods.



Private Sub UpdateMouse(e As MouseEventArgs)

        Select Case GameState

            Case GameStateEnum.Playing

                Dim X As Integer = MouseToBoardX(e)

                Dim Y As Integer = MouseToBoardY(e)

                If Board(X, Y) = Cell.Empty Then

                    If CurrentPlayer = Cell.X Then

                        'Human move.
                        Board(X, Y) = Cell.X

                        If CheckForWin(Cell.X) Then

                            Winner = Win.Human

                            GameState = GameStateEnum.EndScreen

                        ElseIf CheckForDraw() Then

                            Winner = Win.Draw

                            GameState = GameStateEnum.EndScreen

                        Else

                            'We switch to the computer player's turn.
                            CurrentPlayer = Cell.O

                        End If

                        My.Computer.Audio.Play(My.Resources.tone700freq,
                                               AudioPlayMode.Background)

                    End If

                End If

            Case GameStateEnum.EndScreen

                ResetGame()

                GameState = GameStateEnum.Playing

        End Select

End Sub

    

If the human player didn't win and the game isn't a draw we switch to the computer player's turn.



Private Sub UpdatePlaying()

        If CurrentPlayer = Cell.O Then
            'Computer player's turn

            ComputerMove()

            If CheckForWin(Cell.O) Then

                Winner = Win.Computer

                GameState = GameStateEnum.EndScreen

            ElseIf CheckForDraw() Then

                Winner = Win.Draw

                GameState = GameStateEnum.EndScreen

            Else

                'We switch to the human player's turn.
                CurrentPlayer = Cell.X

            End If

        End If

End Sub


Draw Board

We draw the game board by first clearing the background to black.


Private Sub DrawGame()

        Buffer.Graphics.Clear(Color.Black)

        Select Case GameState

            Case GameStateEnum.StartScreen

                'DrawStartScreen()

            Case GameStateEnum.Instructions

                'DrawInstructions()

            Case GameStateEnum.Playing

                DrawPlaying()

            Case GameStateEnum.EndScreen

                DrawEndScreen()

        End Select

End Sub


Private Sub DrawPlaying()

        DrawBoardLines()

        DrawXsAndOs()

        DrawCoordinates()

End Sub
    
    

We draw the board lines using a white pen.



Private Sub DrawBoardLines()

        Dim LinePen As New Pen(Color.White, LinePenWidth)

        'Draw vertical board lines
        Buffer.Graphics.DrawLine(LinePen,
                                 CellWidth,
                                 0,
                                 CellWidth,
                                 ClientSize.Height)

        Buffer.Graphics.DrawLine(LinePen,
                                 ClientSize.Width * 2 \ 3,
                                 0,
                                 ClientSize.Width * 2 \ 3,
                                 ClientSize.Height)

        'Draw horizontal board lines
        Buffer.Graphics.DrawLine(LinePen,
                                 0,
                                 CellHeight,
                                 ClientSize.Width,
                                 ClientSize.Height \ 3)

        Buffer.Graphics.DrawLine(LinePen,
                                 0,
                                 ClientSize.Height * 2 \ 3,
                                 ClientSize.Width,
                                 ClientSize.Height * 2 \ 3)

        LinePen.Dispose()

End Sub


We then loop through the game board array.

We draw X's and O's on the cells based on their state in the board array.



Private Sub DrawXsAndOs()

        For X = 0 To 2

            For Y = 0 To 2

                'Does the cell contain an x?
                If Board(X, Y) = Cell.X Then
                    'Yes, the cell contains an x.

                    DrawX(X, Y)

                    'Does the cell contain an o?
                ElseIf Board(X, Y) = Cell.O Then
                    'Yes, the cell contains an o.

                    DrawO(X, Y)

                End If

            Next

        Next

End Sub
    
    

We draw the X's using a blue pen.



Private Sub DrawX(X As Integer, Y As Integer)
        'To draw the letter X, we start by drawing two diagonal lines that
        'cross in the middle.

        Dim XPen As New Pen(Color.Blue, XPenWidth)

        'We begin by drawing a diagonal line
        'from the top left corner to the bottom right corner.
        Buffer.Graphics.DrawLine(XPen,
        X * CellWidth + CellPaddingWidth,
                                 Y * CellHeight + CellPaddingHeight,
                                 (X + 1) * CellWidth - CellPaddingWidth,
                                 (Y + 1) * CellHeight - CellPaddingHeight)

        'Then we draw a second diagonal line this time
        'from the top right corner to the bottom left corner.
        Buffer.Graphics.DrawLine(XPen,
                                 X * CellWidth + CellPaddingWidth,
                                 (Y + 1) * CellHeight - CellPaddingHeight,
                                 (X + 1) * CellWidth - CellPaddingWidth,
                                 Y * CellHeight + CellPaddingHeight)

        'The two lines intersect in the middle to form an X shape.

        XPen.Dispose()

End Sub


We draw the O's using a red pen.



Private Sub DrawO(X As Integer, Y As Integer)

        Dim OPen As New Pen(Color.Red, OPenWidth)

        Buffer.Graphics.DrawEllipse(OPen,
                                    X * CellWidth + CellPaddingWidth,
                                    Y * CellHeight + CellPaddingHeight,
                                    CellWidth - 2 * CellPaddingWidth,
                                    CellHeight - 2 * CellPaddingHeight)

        OPen.Dispose()

End Sub


We draw the buffer to the form in the Paint event handler.



Protected Overrides Sub OnPaint(ByVal e As PaintEventArgs)

        DrawGame()

        DrawFPS()

        'Show buffer on form.
        Buffer.Render(e.Graphics)

        'Release memory used by buffer.
        Buffer.Dispose()
        Buffer = Nothing

        'Create new buffer.
        Buffer = Context.Allocate(CreateGraphics(), ClientRectangle)

        'Use these settings when drawing to the backbuffer.
        With Buffer.Graphics

            'Bug Fix: Don't Change.
            'To fix draw string error with anti aliasing: "Parameters not valid."
            'I set the compositing mode to: SourceOver.
            .CompositingMode = CompositingMode.SourceOver

            .TextRenderingHint = TextRenderingHint.AntiAliasGridFit
            .SmoothingMode = SmoothingMode.AntiAlias
            .CompositingQuality = CompositingQuality.HighQuality
            .InterpolationMode = InterpolationMode.HighQualityBicubic
            .PixelOffsetMode = PixelOffsetMode.HighQuality
            .TextContrast = 6 'a value between 0 and 12
        End With

        UpdateFrameCounter()

End Sub


I'm making a video to explain the code on my YouTube channel. https://www.youtube.com/@codewithjoe6074