Programming Games with Small Basic: Chapter 6: Tic Tac Toe Program
Small Basic > Small Basic Books > Programming Games with Small Basic > Chapter 6: Tic Tac Toe Program
This chapter is adapted from the book Programming Games with Microsoft Small Basic by Philip Conrod and Lou Tylee.
To purchase this book in its entirety, please see the Computer Science For Kids web site.
6. Tic Tac Toe Program
The next program we build is the classic Tic Tac Toe game, where you try to line up 3 X’s or 3 O’s in a 3 by 3 grid. We’ll develop a two player version and one where you can play against the computer.
Tic Tac Toe Program Preview
In this chapter, we will build a Tic Tac Toe game. This is purportedly the first game ever programmed on a computer and one of the first ever programmed by Bill Gates when he was a teenager. The object of the game is line up 3 X markers or 3 O markers in a 3 by 3 grid. The markers can run horizontally, vertically or diagonally. Turns alternate between players. The version we build here allows two players to compete against each other or to have a single player compete against a pretty smart computer.
The finished program is saved as TicTacToe in the KidGamesSB\KidGamesSB Programs\TicTacToe folder. Start Small Basic and open the finished program. Run the program (click the Run toolbar button or press <F5>). The game will appear in its ‘stopped’ state. At this point, you can click the Start Game button to start the game, click Change Options to change program options or click Exit to exit the program.
Click Change Options to change options. A text window will appear:
You are given a few questions to choose game options (one or two players and, if one player, who goes first and how smart you want the computer to be). Make your choices (pressing Enter after each). For this example, I selected a one player game, where I go first (giving me X’s) and a smart computer:
The graphics window reappears with the game still in the ‘stopped’ state.
Notice the title bar area reflects a single player game.
Click the Start Game button to start playing. The button choices change. The message at the top of the grid says X’s Turn. X always goes first in this game (whether it’s you, the human player, or the computer). This is the game’s ‘playing’ state:
In this state, you make a mark in the grid by clicking on the desired square. The computer will then place its mark, making it your turn again. After each mark, the board is examined for a win or a draw. You keep alternating turns until there is a win, the grid is full or until you click Stop Game. The game works the same way for two players, with the two players alternating turns marking the grid.
Enter a mark; I chose the center square and the computer immediately placed its mark (an O) in the upper left corner:
I try the lower left corner and the computer blocks me from winning:
I block the computer’s possible win by putting an X in the middle box of the top row. Then, the computer blocks me with a move to the middle of the bottom row:
Looks like no one is going to win here.
I continued playing (moving to the right of the middle row and, following the computer’s block, a move to the right of the bottom row) until finally we ended in a draw:
Looks like the computer is pretty smart! We’ll see how this intelligence is programmed later. Notice the game returns to its ‘stopped’ state to allow another game, changing options or to stop the program.
Continue playing the game to understand its operation. Try using the Random Computer option (the computer just makes random moves making it easier to beat). Click the Exit button when you’re done to exit the game. Look over the code in the editor if you like.
You will now build this program in stages. As you build Small Basic programs, we always recommend taking a slow, step-by-step process. It minimizes programming errors and helps build your confidence as things come together in a complete program.
We address window design. And, we address code design. We discuss how to mark the grid, handle multiple players, check for a win and decide how to make computer-generated moves.
Tic Tac Toe Window Design
Here is a sketch for the window layout of the Tic Tac Toe game:
A message area is at the top. The game grid (drawn with skinny rectangles) is below. Identifying numbers are given to the nine boxes in the grid. Three button controls will be used: one to start and stop the game, one to change options and one to exit the program.
We now begin writing code for the Tic Tac Toe game. We will write the code in several steps. As a first step, we will write the code that draws the window, then starts the game and establishes its ‘stopped’ state. We write code to set game options in a text window. Then, we look at how to go to ‘playing’ state following clicking of the Start Game button. During the code development process, recognize you may modify a particular procedure several times before arriving at the finished product.
Start a new program in Small Basic. Once started, we suggest you immediately save the program with a name you choose. This sets up the folder and file structure needed for your program.
**
**
Window Design – Message Area
The first element of the Tic Tac Toe window is the bordered message area at the top of the window. It is used to inform the user of whose turn it is and who wins (if there is a win).
Add this code to your blank editor:
'Tic Tac Toe
InitializeProgram()
Sub InitializeProgram
'graphics window
GraphicsWindow.Width = 340
GraphicsWindow.Height = 460
* 'Draw message area*
GraphicsWindow.BrushColor = "Yellow"
GraphicsWindow.FillRectangle(10, 10, 320, 50)
GraphicsWindow.PenColor = "Black"
GraphicsWindow.PenWidth = 2
GraphicsWindow.DrawRectangle(10, 10, 320, 50)
GraphicsWindow.BrushColor = "Blue"
GraphicsWindow.FontBold = "false"
GraphicsWindow.FontSize = 30
Message = "Game Stopped"
MessageX = 70
MessageArea = Shapes.AddText(Message)
DisplayMessage()
EndSub
The first line of code calls a subroutine InitializeProgram where we will put all code needed to set up the program for use. All remaining code here goes in that subroutine. The code establishes the window size and draws a filled, bordered rectangle to surround the message area. The message to display is Message and its horizontal location is MessageX. A Shapes object (MessageArea) is created to display the message.
The subroutine DisplayMessage writes the message. Add this to your code:
Sub DisplayMessage
Shapes.Move(MessageArea, MessageX, 15)
Shapes.SetText(MessageArea, Message)
EndSub
This is similar to code we will use often in building game programs. To display information that changes often, the Shapes object that displays text is invaluable. To update the display, we simply change the text using SetText (and in this case Move the text to make it appear centered in MessageArea). An immediate, flicker-free update is seen.
Save and Run the program. You should see the initial ‘Game Stopped’ message:
**
**
Window Design – Draw Grid
The next element of the game window is the Tic Tac Toe grid. It is drawn with ‘skinny’ rectangles. Add these lines of code at the end of the InitializeProgram subroutine:
'draw grid
GraphicsWindow.BrushColor = "Black"
GraphicsWindow.FillRectangle(10, 170, 320, 10)
GraphicsWindow.FillRectangle(10, 280, 320, 10)
GraphicsWindow.FillRectangle(110, 70, 10, 320)
GraphicsWindow.FillRectangle(220, 70, 10, 320)
Save and Run the program to see the grid:
**
**
Window Design – Add Buttons
The final item we need are three button controls used to start/stop the game, change options and exit the program.
Add this code at the end of InitializeProgram:
'define buttons
GraphicsWindow.BrushColor = "Black"
GraphicsWindow.FontSize = 16
StartStopButton = Controls.AddButton("Start Game", 10, 420)
OptionsButton = Controls.AddButton("Change Options", 110, 420)
ExitButton = Controls.AddButton("Exit", 240, 420)
Save and Run the program. The finished window is displayed:
We establish three buttons (StartStopButton, OptionsButton, ExitButton).
Code Design – Initializing Stopped State
Any time we start a program, there are certain initializations that must take place. Let’s look at the initializations needed in the Tic Tac Toe game. All initializations are done in the main program.
We want to initialize the number of players in the game (NumberPlayers), whether you go first (YouGoFirst) and whether the computer is smart (SmartComputer). We will use this information to update the graphics window title. We also want to know the location of the nine areas (boxes) within the Tic Tac Toe grid. This will help us place marks there later.
Add this code at the end of InitializeProgram:
'Default Options
NumberPlayers = 2
YouGoFirst = "true"
SmartComputer = "true"
SetTitle()
'initialize box locations and marks
x = 20
y = 80
For I = 1 to 9
BoxX[I] = x
BoxY[I] = y
x = x + 110
If (x > 240) Then
x = 20
y = y + 110
EndIf
EndFor
GameStatus = "Stopped"
This code initializes the options variables. Next, the (x, y) location of the nine boxes in the grid is established. Lastly, the GameStatus is set to “Stopped”.
The subroutine SetTitle is also needed:
Sub SetTitle
If (NumberPlayers = 1) Then
GraphicsWindow.Title = "Tic Tac Toe - 1 Player"
Else
GraphicsWindow.Title = "Tic Tac Toe - 2 Players"
EndIf
EndSub
This sets the window title based on selected options.
Once again, Save and Run. You’ll see the game in the ‘stopped’ state (using default properties):
As desired, the game initializes in Two Players mode.
We have three choices at this point – either click Start Game to start, click Change Options to change options, or click Exit to exit the program. We will write code for each of these options in reverse order.
First, we need to be able to detect button clicks so add this line at the end of InitializeProgram:
Controls.ButtonClicked = ButtonClickedSub
With this, each time a ButtonClicked event occurs, the subroutine named ButtonClickedSub is called.
The code for exiting is simple. It is placed in the ButtonClickedSub subroutine:
Sub ButtonClickedSub
B = Controls.LastClickedButton
If (GameStatus = "Stopped") Then
If (B = ExitButton) Then
Program.End()
EndIf
EndIf
EndSub
This simply says whenever Exit is clicked, the program ends. Run the program. Click Exit to make sure the game stops.
If the user clicks Change Options, we want to provide the ability to change program options. We will use the Small Basic text window to establish game options. Add the shaded code to the ButtonClickedSub subroutine:
Sub ButtonClickedSub
B = Controls.LastClickedButton
If (GameStatus = "Stopped") Then
If (B = ExitButton) Then
Program.End()
ElseIf (B = OptionsButton) Then
SetOptions()
EndIf
EndIf
EndSub
The options are set in the SetOptions subroutine – add this routine:
Sub SetOptions
GraphicsWindow.Hide()
TextWindow.Show()
TextWindow.Title = "Tic Tac Toe"
TextWindow.CursorLeft = 3
TextWindow.CursorTop = 3
TextWindow.WriteLine("TIC TAC TOE OPTIONS")
TextWindow.WriteLine("")
GetPlayers:
TextWindow.CursorLeft = 3
TextWindow.WriteLine("With one player, you play against the computer.")
TextWindow.CursorLeft = 3
TextWindow.WriteLine("With two players, you play against a friend.")
TextWindow.CursorLeft = 3
TextWindow.Write("How many players do you want (1 or 2)? ")
NumberPlayers = TextWindow.ReadNumber()
If (NumberPlayers < 1 Or NumberPlayers > 2) Then
Goto GetPlayers
EndIf
If (NumberPlayers = 1) Then
GetWhoFirst:
TextWindow.WriteLine("")
TextWindow.CursorLeft = 3
TextWindow.WriteLine("You can go first or the computer can go first.")
TextWindow.CursorLeft = 3
TextWindow.Write("Who goes first (1-You, 2-Computer)? ")
T = TextWindow.ReadNumber()
If (T < 1 Or T > 2) Then
Goto GetWhoFirst
EndIf
If (T = 1) Then
YouGoFirst = "true"
Else
YouGoFirst = "false"
EndIf
GetSmart:
TextWindow.WriteLine("")
TextWindow.CursorLeft = 3
TextWindow.WriteLine("Computer can make random moves or smart moves.")
TextWindow.CursorLeft = 3
TextWindow.Write("What do you want (1-Random, 2-Smart)? ")
T = TextWindow.ReadNumber()
If (T < 1 Or T > 2) Then
Goto GetSmart
EndIf
If (T = 2) Then
SmartComputer = "true"
Else
SmartComputer = "false"
EndIf
EndIf
SetTitle()
TextWindow.Hide()
GraphicsWindow.Show()
EndSub
This routine hides the graphics window, displays a text window and asks the user questions necessary to establish the three options (NumberPlayers, YouGoFirst, SmartComputer). Note, if two players are selected, there is no need to ask who goes first or how smart the computer should be. Once the questions are answered, the program returns to the game in ‘stopped’ state with a new window title.
**
**
Save and Run the program. Click the Change Options button the change options:
Make your choices and press Enter after each. The Tic Tac Toe game window will again appear.
Here’s what I got for a single player (the only difference is in the title bar):
The code for clicking the Start Game button (to start the game) is much more complicated. We will build it in several steps. First, we look at switching the game from stopped to playing state.
**
**
Code Design –Stopped to Playing State
When the user clicks the Start Game button in the ‘stopped’ state, several things must happen to switch the Tic Tac Toe game to ‘playing’ state:
- Change GameStatus to “Playing”
- Change the caption of StartStopButton to Stop Game.
- Hide OptionsButton and ExitButton.
- Establish this as X’s turn (since X always goes first).
- Blank out grid boxes displaying marks.
- Allow player to input a mark on the grid.
We use two variables to help keep track of where we are in the game. If XTurn is “true”, it is X’s turn, otherwise it is O’s turn. NumberClicks keeps track of how many of the grid boxes have been clicked on (9 maximum).
Add the shaded code to the ButtonClickSub subroutine:
Sub ButtonClickedSub
B = Controls.LastClickedButton
If (GameStatus = "Stopped") Then
If (B = ExitButton) Then
Program.End()
ElseIf (B = OptionsButton) Then
SetOptions()
ElseIf (B = StartStopButton) Then
StartGame()
EndIf
EndIf
EndSub
Clicking Start Game calls the StartGame subroutine:
Sub StartGame
GameStatus = "Playing"
XTurn = "true"
Message = "X's Turn"
MessageX = 115
DisplayMessage()
'clear boxes
GraphicsWindow.BrushColor = GraphicsWindow.BackgroundColor
For I = 1 To 9
BoxMark[I] = ""
GraphicsWindow.FillRectangle(BoxX[I] - 5, BoxY[I] - 5, 90, 90)
EndFor
GraphicsWindow.BrushColor = "Black"
GraphicsWindow.FontSize = 16
Controls.SetButtonCaption(StartStopButton, "Stop Game")
Controls.HideControl(OptionsButton)
Controls.HideControl(ExitButton)
NumberClicks = 0
EndSub
Can you see how the needed steps are implemented in code?
Save and Run the program. Click Start Game and the game should switch to two player ‘playing’ state:
The game is waiting for the first player to click one of the grid locations (we’ll write code for that soon). Notice clicking Stop Game does nothing at the moment. Let’s fix that. Stop the program.
Code Design –Playing to Stopped State
When the user clicks the Stop Game button in the two player ‘playing’ state, we want the Tic Tac Toe game to change to ‘stopped’ state. The steps for this are:
- Change GameStatus to “Stopped”
- Change the caption of StartStopButton to Start Game.
- Show OptionsButton and ExitButton.
Add the shaded code to the ButtonClickedSub subroutine:
Sub ButtonClickedSub
B = Controls.LastClickedButton
If (GameStatus = "Stopped") Then
If (B = ExitButton) Then
Program.End()
ElseIf (B = OptionsButton) Then
SetOptions()
ElseIf (B = StartStopButton) Then
StartGame()
EndIf
ElseIf (GameStatus = "Playing") Then
If (B = StartStopButton) Then
'stop program
Message = "Game Stopped"
MessageX = 70
StopGame()
EndIf
EndIf
EndSub
Clicking Stop Game establishes a message to display and calls the StopGame subroutine.
Add the StopGame subroutine:
Sub StopGame
'restore buttons
GameStatus = "Stopped"
GraphicsWindow.BrushColor = "Black"
GraphicsWindow.FontSize = 16
Controls.SetButtonCaption(StartStopButton, "Start Game")
Controls.ShowControl(OptionsButton)
Controls.ShowControl(ExitButton)
DisplayMessage()
EndSub
Save and Run the program. You should be able to now move from ‘stopped’ to ‘playing’ state and back. Try the Exit button. Let’s write the code for the two player game (the default value for NumberPlayers), first looking at how to mark the grid.
Code Design –Marking Grid
In the Tic Tac Toe two player game, when a player clicks a box in the grid, we want the proper mark (X or O) to appear. After each mark, we then need to see if anyone won. If there is no win, and there are still empty locations, we switch to the next player. Recall, for reference purposes, the nine areas of the grid are numbered as follows:
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
So, when a box in the game grid is clicked, we follow these steps:
- Make sure there is not a mark there already.
- Increment NumberClicks.
- Place proper mark in corresponding location (X if XTurn is “true”, otherwise O).
- Switch to next player (not needed if there is a win or draw).
- Check for win. If there is a win, declare the winner and stop the game.
- Check if NumberClicks = 9 (board is full with no win). If so, declare the game a draw and stop.
To detect mouse clicks, we need this line at the end of the InitializeProgram subroutine:
GraphicsWindow.KeyDown = KeyDownSub
With this, each time a KeyDown event occurs, the subroutine named KeyDownSub is called. The code to perform the steps above will be in this subroutine.
Add this subroutine (MouseDownSub) to your program. This implements the needed steps to accept the player’s marking of the grid:
Sub MouseDownSub
If (GameStatus = "Playing") Then
'find which box was clicked
x = GraphicsWindow.MouseX
y = GraphicsWindow.MouseY
ClickedBox = 0
For I = 1 To 9
If (x > BoxX[I] And x < BoxX[I] + 80) Then
If (y > BoxY[I] And y < BoxY[I] + 80) Then
ClickedBox = I
Goto GotIt
EndIf
EndIf
EndFor
GotIt:
If (ClickedBox <> 0) Then
'if already clicked then exit
If (BoxMark[ClickedBox] <> "") Then
Goto LeaveSub
EndIf
MarkAndCheck()
EndIf
EndIf
LeaveSub:
EndSub
Let’s look at this code. Clicking the graphics window with the mouse will call this subroutine. The first part of the code determines which grid box area (if any) was clicked (the variable ClickedBox). If there is already a mark there (not blank), the subroutine is exited. If blank, another subroutine (MarkAndCheck) is called to do the actual marking and check for a win. We use a subroutine for this step because later we will want a way for the computer to mark the grid when it is the opponent.
Add the subroutine MarkAndCheck to your code (we are not checking for a win yet):
Sub MarkAndCheck
NumberClicks = NumberClicks + 1
If (XTurn) Then
BoxMark[ClickedBox] = "X"
DrawX()
XTurn = "false"
Message = "O's Turn"
MessageX = 115
DisplayMessage()
Else
BoxMark[ClickedBox] = "O"
DrawO()
XTurn = "true"
Message = "X's Turn"
MessageX = 115
DisplayMessage()
EndIf
'check for win - will establish a value for WhoWon
CheckForWin()
If (WhoWon <> "") Then
Message = WhoWon + " Wins!"
MessageX = 115
StopGame()
ElseIf (NumberClicks = 9) Then
'draw
Message = "It's a Draw!"
MessageX = 95
StopGame()
EndIf
EndSub
Here, the proper mark is placed in BoxMark[ClickedBox]. After this, we check for a win in the subroutine CheckForWin. If the variable WhoWon is not blank (will be established by the check win logic), we declare the winner. Otherwise, we keep accepting clicks until the grid is full, declaring a draw.
The above subroutine requires these subroutines to draw an X or O. Add them to your program:
Sub DrawX
'draw blue X at ClickedBox
GraphicsWindow.PenColor = "Blue"
GraphicsWindow.PenWidth = 10
GraphicsWindow.DrawLine(BoxX[ClickedBox], BoxY[ClickedBox], BoxX[ClickedBox] + 80, BoxY[ClickedBox] + 80)
GraphicsWindow.DrawLine(BoxX[ClickedBox], BoxY[ClickedBox] + 80, BoxX[ClickedBox] + 80, BoxY[ClickedBox])
EndSub
Sub DrawO
'draw blue O at Clicked Box
GraphicsWindow.PenColor = "Blue"
GraphicsWindow.PenWidth = 10
GraphicsWindow.DrawEllipse(BoxX[ClickedBox], BoxY[ClickedBox], 80, 80)
EndSub
These routines use thick lines for the X and a thick ellipse for the O.
We also need the CheckForWin subroutine. For now, just use this which returns a blank for the WhoWon variable:
Sub CheckForWin
WhoWon = ""
EndSub
We will write code for this subroutine next.
**
**
Save and Run the program. You should be able to click on each of the grid locations, placing X’s and O’s there in alternate turns. Once you have filled all the grid locations, the game will be returned to stopped state. Wins will not be recognized. Here’s what I got when I tried it:
As expected, the computer completely missed the fact that O (Player 2) got a Tic Tac Toe!! Let’s fix that. We will modify the CheckForWin subroutine to determine a value for the WhoWon variable used in the code.
**
**
Code Design – Checking For Win
The CheckForWin subroutine examines the playing grid and determines if there is a winner. If so, the win will be identified and the subroutine will establish a value for WhoWon – it will hold the marker (X or O) for the winner or a blank if there is no winner. Let’s establish a strategy for doing this.
There are eight possible ways to win (3 horizontal, 3 vertical, 2 diagonal). Notice the indices for the BoxMark array used in the playing grid are laid out in this manner:
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
If we have a string array named PossibleWins, its 8 elements would be:
'possible wins
PossibleWins[1] = "123"
PossibleWins[2] = "456"
PossibleWins[3] = "789"
PossibleWins[4] = "147"
PossibleWins[5] = "258"
PossibleWins[6] = "369"
PossibleWins[7] = "159"
PossibleWins[8] = "357"
Add the above lines to the InitializeProgram subroutine to establish values for the PossibleWins array.
So our win logic would be to go through each possible win and see if the corresponding elements of BoxMark all contain the same mark (X or O, but not blank). If so, a winner is declared.
Put the modified CheckForWin subroutine in your program (new lines are shaded):
Sub CheckForWin
WhoWon = ""
'check all possible for win
For I = 1 To 8
For J = 1 To 3
BoxNumber[J] = Text.GetSubText(PossibleWins[I], J, 1)
Mark[J] = BoxMark[BoxNumber[J]]
EndFor
If (Mark[1] = Mark[2] And Mark[1] = Mark[3] And Mark[2] = Mark[3] And Mark[1] <> "") Then
'we have a winner
WhoWon = Mark[1]
For J = 1 To 3
GraphicsWindow.BrushColor = "Red"
GraphicsWindow.FillRectangle(BoxX[BoxNumber[J]] - 5, BoxY[BoxNumber[J]] - 5, 90, 90)
ClickedBox = BoxNumber[J]
If (WhoWon = "X") Then
DrawX()
Else
DrawO()
EndIf
EndFor
EndIf
EndFor
EndSub
This code goes through all the possible wins. If all the marks in a particular horizontal, vertical or diagonal line match, the marks in that line are redrawn in a red rectangle and the corresponding winner returned. Study the code to see how it works. The BoxNumber array holds the indices of the BoxMark array for each possible win.
Save and Run the program. You and a friend should be able to compete with wins and draws properly determined. Here is a replay of the game I tried before (notice the win by O is now declared):
The two player game is now complete. Let’s start looking at how to implement a one player game versus a computer opponent. We’ll start easy, just having the computer make random moves (no brains!). This will help us establish the logic of switching players when the computer is one of them.
Code Design – Random Computer Moves
A big part of allowing the computer to play against a human (you, the player) is to decide what “thought processes” to give the computer. We’re allowing two choices: a random computer and a smart computer. We’ll start by coding up the random computer. This will get all the logic of implementing computer moves along with human moves working properly. Then, we’ll move on to a smart computer, developing a formidable opponent.
All of the logic behind a computer move will be implemented in a subroutine named ComputerTurn. For random moves, it will simply choose (at random) one of the empty boxes in the grid and place a marker in that box (X if computer goes first, O if human goes first.
Place this subroutine ComputerTurn in your program:
Sub ComputerTurn
If (SmartComputer <> "true") Then
'random logic
'put mark in Nth available square
N = Math.GetRandomNumber(9 - NumberClicks)
I = 0
For ClickedBox = 1 To 9
If (BoxMark[ClickedBox] = "") Then
I = I + 1
If I = N Then
Goto GotMark
EndIf
EndIf
EndFor
GotMark:
'put mark in ClickedBox
MarkAndCheck()
Else
'smart computer
EndIf
EndSub
The logic behind the code in ComputerTurn is straightforward. At any time, we have 9 - NumberClicks empty boxes in the grid. The code selects a random number from 1 to 9 – NumberClicks and counts ahead that number of empty boxes to identify the box to mark. To mark the identified box, BoxMark[ClickedBox], we call the subroutine MarkAndCheck. The program will know whether to place an X or O in the box based on previously implemented logic.
Now, let’s implement the ComputerTurn subroutine to allow the computer to play. We need to modify two subroutines. First, when we start a game, if the computer moves first, we need to invoke ComputerTurn. Make the shaded changes to the StartGame subroutine:
Sub StartGame
GameStatus = "Playing"
XTurn = "true"
Message = "X's Turn"
MessageX = 115
DisplayMessage()
'clear boxes
GraphicsWindow.BrushColor = GraphicsWindow.BackgroundColor
For I = 1 To 9
BoxMark[I] = ""
GraphicsWindow.FillRectangle(BoxX[I] - 5, BoxY[I] - 5, 90, 90)
EndFor
GraphicsWindow.BrushColor = "Black"
GraphicsWindow.FontSize = 16
Controls.SetButtonCaption(StartStopButton, "Stop Game")
Controls.HideControl(OptionsButton)
Controls.HideControl(ExitButton)
NumberClicks = 0
If (NumberPlayers = 1 And YouGoFirst = "false") Then
ComputerTurn()
EndIf
EndSub
And, after a mark is placed by the human player, the computer needs to take a turn. This logic is in the MarkAndCheck subroutine. The needed changes are shaded:
Sub MarkAndCheck
NumberClicks = NumberClicks + 1
If (XTurn) Then
BoxMark[ClickedBox] = "X"
DrawX()
XTurn = "false"
Message = "O's Turn"
MessageX = 115
DisplayMessage()
Else
BoxMark[ClickedBox] = "O"
DrawO()
XTurn = "true"
Message = "X's Turn"
MessageX = 115
DisplayMessage()
EndIf
'check for win - will establish a value for WhoWon
CheckForWin()
If (WhoWon <> "") Then
Message = WhoWon + " Wins!"
MessageX = 115
StopGame()
ElseIf (NumberClicks = 9) Then
'draw
Message = "It's a Draw!"
MessageX = 95
StopGame()
EndIf
If (NumberPlayers = 1 And WhoWon = "") Then
If (XTurn = "true" And YouGoFirst = "false") Or (XTurn = "false" And YouGoFirst = "true") Then
ComputerTurn()
EndIf
EndIf
EndSub
With the added code, if there is no win, the computer takes a turn when it goes first and it’s X’s turn or takes a turn when the human goes first and it’s O’s turn.
Save and Run the game. Click Change Options to change options. Choose 1 Player, You Go First and Random Computer. Each option requires entering a 1:
Make sure things work properly, you should see the computer is pretty easy to beat! Here’s a game I won when I went first:
Try playing a few games (you go first, computer going first).
Code Design – Smart Computer Moves
We’ve come to one of the more fun and more challenging parts of this game program. How can we make our computer a smarter opponent? In any game where a computer is playing against a human, we must be able to write down some rules that give the computer the appearance of intelligence. Many computer opponents are unbeatable. For example, it is very hard for a human to beat a computer at the game of chess.
So how do we make our computer a better Tic Tac Toe player? We try to imbed choices we would make if we were playing the game. So for the computer to be a smart player, the programmer needs to be a smart player. This usually takes practice and study. For the game of Tic Tac Toe, we can develop a fairly simple, yet very intelligent strategy. Let’s do it.
When it is the computer’s turn, what should its move be? The rules we will use are (in order of choice):
- If the computer can win with a move, make that move and the game is over. So, if there’s a line with two of the computer’s markers and an empty space, the empty space is the place to mark!
- If the computer can block with a move, make that move and the opponent can’t win on the next move. So, if there’s a line with two of the human player’s markers and an empty space, the empty space is the place to mark!
- If there is no possible win or possible block, make a move in this order: center square, one of the four corner squares, or one of the four side squares.
I think you see this logic makes sense. You may wonder about Step 3 – why we choose that particular order. Recall there are 8 possible ways to win in Tic Tac Toe (3 horizontal, 3 vertical, 2 diagonal). The center square is needed in 4 of these, any one corner is involved in 3 possibilities, while any side is involved in just 2 wins. Hence, the order of choices is made on basic probabilities. Let’s implement this logic in code.
Here is the modified ComputerTurn subroutine that implements this ‘smart’ logic. The changes are shaded:
Sub ComputerTurn
If (SmartComputer <> "true") Then
'random logic
'put mark in Nth available square
N = Math.GetRandomNumber(9 - NumberClicks)
I = 0
For ClickedBox = 1 To 9
If (BoxMark[ClickedBox] = "") Then
I = I + 1
If I = N Then
Goto GotMark
EndIf
EndIf
EndFor
GotMark:
'put mark in ClickedBox
MarkAndCheck()
Else
'smart computer
BestMoves[1] = 5
BestMoves[2] = 1
BestMoves[3] = 3
BestMoves[4] = 7
BestMoves[5] = 9
BestMoves[6] = 2
BestMoves[7] = 4
BestMoves[8] = 6
BestMoves[9] = 8
'determine who has what mark
If (YouGoFirst) Then
ComputerMark = "O"
PlayerMark = "X"
Else
ComputerMark = "X"
PlayerMark = "O"
EndIf
'Step 1 (K = 1) - check for win - see if two boxes hold computer mark and one is empty
'Step 2 (K = 2) - check for block - see if two boxes hold player mark and one is empty
For K = 1 To 2
If K = 1 Then
MarkToFind = ComputerMark
Else
MarkToFind = PlayerMark
EndIf
For I = 1 To 8
N = 0
EmptyBox = 0
For J = 1 To 3
BoxNumber[J] = Text.GetSubText(PossibleWins[I], J, 1)
Mark[J] = BoxMark[BoxNumber[J]]
If (Mark[J] = MarkToFind) Then
N = N + 1
ElseIf (Mark[J] = "") Then
EmptyBox = BoxNumber[J]
EndIf
EndFor
If (N = 2 And EmptyBox <> 0) Then
'mark empty box to win (K = 1) or block (K = 2)
ClickedBox = EmptyBox
MarkAndCheck()
Goto LeaveComputerMove
EndIf
EndFor
EndFor
'Step 3 - find next best move
For I = 1 To 9
If (BoxMark[BestMoves[I]] = "") Then
ClickedBox = BestMoves[I]
MarkAndCheck()
Goto LeaveComputerMove
EndIf
EndFor
EndIf
LeaveComputerMove:
EndSub
In the ‘smart’ logic, we first find out whether the computer has X or O. Steps 1 and 2 of the computer logic are done in a For loop with K as index. In that loop, we go through all the possible wins looking for a line with 2 identical marks and an empty box. For K=1, we look for the computer’s mark and an empty box – giving the computer a win on the next move. For K=2, we look for the human’s mark and an empty box – giving the computer a block on the next move. If neither Step 1 or Step 2 is successful, we move to Step 3. The next best moves are listed in desired order in the array BestMoves. In Step 3, we go through this array, finding the first empty box available and move there.
Save and Run the program. The game is now fully functional. Try playing it against the computer. You should find it can’t be beat. The best you can do against the computer is a draw. Before leaving, let’s add a couple of sounds.
**
**
Code Design – Adding Sounds
We know sounds make games a bit more fun. Let’s add a couple to the Tick Tac Toe game. In the KidGamesSB\KidGamesSB Programs\TicTacToe folder are two wav files that can be used for sound. The file beep.wav is a sound we’ll use for games that end a draw. The tada.wav file is a celebratory sound we’ll use for wins by either player. These files will be loaded when the program starts. Copy the two sound files to your program’s folder.
Play the two sounds at the appropriate (shaded) locations in the MarkAndCheck subroutine:
Sub MarkAndCheck
NumberClicks = NumberClicks + 1
If (XTurn) Then
BoxMark[ClickedBox] = "X"
DrawX()
XTurn = "false"
Message = "O's Turn"
MessageX = 115
DisplayMessage()
Else
BoxMark[ClickedBox] = "O"
DrawO()
XTurn = "true"
Message = "X's Turn"
MessageX = 115
DisplayMessage()
EndIf
'check for win - will establish a value for WhoWon
CheckForWin()
If (WhoWon <> "") Then
Sound.Stop(Program.Directory + "\tada.wav")
Sound.Play(Program.Directory + "\tada.wav")
Message = WhoWon + " Wins!"
MessageX = 115
StopGame()
ElseIf (NumberClicks = 9) Then
'draw
Sound.Stop(Program.Directory + "\beep.wav")
Sound.Play(Program.Directory + "\beep.wav")
Message = "It's a Draw!"
MessageX = 95
StopGame()
EndIf
If (NumberPlayers = 1 And WhoWon = "") Then
If (XTurn = "true" And YouGoFirst = "false") Or (XTurn = "false" And YouGoFirst = "true") Then
ComputerTurn()
EndIf
EndIf
EndSub
Save and Run the program. You should now have a complete, running version of the Tic Tac Toe game. Have fun playing it! Again, you will see the computer can’t be beat. Here’s a game I played against a ‘smart’ computer where I went first, taking the middle square:
Notice, based on the logic we implemented, since the computer couldn’t win or block, it took its next best move, the first available corner square.
My next move was the lower left corner:
The computer logic implemented block my next move.
My next move was to block the computer, who in turn blocked me:
I continued playing (moving to the right of the middle row and, following the computer’s block, a move to the right of the bottom row) until finally we ended in a draw:
I heard the little beep and the computer’s ready to play again.
**
**
Tic Tac Toe Game Program Listing
Here is the complete listing of the Tic Tac Toe Small Basic program:
'Tic Tac Toe
InitializeProgram()
Sub InitializeProgram
'graphics window
GraphicsWindow.Width = 340
GraphicsWindow.Height = 460
'Draw message area
GraphicsWindow.BrushColor = "Yellow"
GraphicsWindow.FillRectangle(10, 10, 320, 50)
GraphicsWindow.PenColor = "Black"
GraphicsWindow.PenWidth = 2
GraphicsWindow.DrawRectangle(10, 10, 320, 50)
GraphicsWindow.BrushColor = "Blue"
GraphicsWindow.FontBold = "false"
GraphicsWindow.FontSize = 30
Message = "Game Stopped"
MessageX = 70
MessageArea = Shapes.AddText(Message)
DisplayMessage()
'draw grid
GraphicsWindow.BrushColor = "Black"
GraphicsWindow.FillRectangle(10, 170, 320, 10)
GraphicsWindow.FillRectangle(10, 280, 320, 10)
GraphicsWindow.FillRectangle(110, 70, 10, 320)
GraphicsWindow.FillRectangle(220, 70, 10, 320)
'define buttons
GraphicsWindow.BrushColor = "Black"
GraphicsWindow.FontSize = 16
StartStopButton = Controls.AddButton("Start Game", 10, 420)
OptionsButton = Controls.AddButton("Change Options", 110, 420)
ExitButton = Controls.AddButton("Exit", 240, 420)
'Default Options
NumberPlayers = 2
YouGoFirst = "true"
SmartComputer = "true"
SetTitle()
'initialize box locations and marks
x = 20
y = 80
For I = 1 to 9
BoxX[I] = x
BoxY[I] = y
x = x + 110
If (x > 240) Then
x = 20
y = y + 110
EndIf
EndFor
GameStatus = "Stopped"
Controls.ButtonClicked = ButtonClickedSub
GraphicsWindow.MouseDown = MouseDownSub
'possible wins
PossibleWins[1] = "123"
PossibleWins[2] = "456"
PossibleWins[3] = "789"
PossibleWins[4] = "147"
PossibleWins[5] = "258"
PossibleWins[6] = "369"
PossibleWins[7] = "159"
PossibleWins[8] = "357"
EndSub
Sub DisplayMessage
Shapes.Move(MessageArea, MessageX, 15)
Shapes.SetText(MessageArea, Message)
EndSub
Sub SetTitle
If (NumberPlayers = 1) Then
GraphicsWindow.Title = "Tic Tac Toe - 1 Player"
Else
GraphicsWindow.Title = "Tic Tac Toe - 2 Players"
EndIf
EndSub
Sub ButtonClickedSub
B = Controls.LastClickedButton
If (GameStatus = "Stopped") Then
If (B = ExitButton) Then
Program.End()
ElseIf (B = OptionsButton) Then
SetOptions()
ElseIf (B = StartStopButton) Then
StartGame()
EndIf
ElseIf (GameStatus = "Playing") Then
If (B = StartStopButton) Then
'stop program
Message = "Game Stopped"
MessageX = 70
StopGame()
EndIf
EndIf
EndSub
Sub SetOptions
GraphicsWindow.Hide()
TextWindow.Show()
TextWindow.Title = "Tic Tac Toe"
TextWindow.CursorLeft = 3
TextWindow.CursorTop = 3
TextWindow.WriteLine("TIC TAC TOE OPTIONS")
TextWindow.WriteLine("")
GetPlayers:
TextWindow.CursorLeft = 3
TextWindow.WriteLine("With one player, you play against the computer.")
TextWindow.CursorLeft = 3
TextWindow.WriteLine("With two players, you play against a friend.")
TextWindow.CursorLeft = 3
TextWindow.Write("How many players do you want (1 or 2)? ")
NumberPlayers = TextWindow.ReadNumber()
If (NumberPlayers < 1 Or NumberPlayers > 2) Then
Goto GetPlayers
EndIf
If (NumberPlayers = 1) Then
GetWhoFirst:
TextWindow.WriteLine("")
TextWindow.CursorLeft = 3
TextWindow.WriteLine("You can go first or the computer can go first.")
TextWindow.CursorLeft = 3
TextWindow.Write("Who goes first (1-You, 2-Computer)? ")
T = TextWindow.ReadNumber()
If (T < 1 Or T > 2) Then
Goto GetWhoFirst
EndIf
If (T = 1) Then
YouGoFirst = "true"
Else
YouGoFirst = "false"
EndIf
GetSmart:
TextWindow.WriteLine("")
TextWindow.CursorLeft = 3
TextWindow.WriteLine("Computer can make random moves or smart moves.")
TextWindow.CursorLeft = 3
TextWindow.Write("What do you want (1-Random, 2-Smart)? ")
T = TextWindow.ReadNumber()
If (T < 1 Or T > 2) Then
Goto GetSmart
EndIf
If (T = 2) Then
SmartComputer = "true"
Else
SmartComputer = "false"
EndIf
EndIf
SetTitle()
TextWindow.Hide()
GraphicsWindow.Show()
EndSub
Sub StartGame
GameStatus = "Playing"
XTurn = "true"
Message = "X's Turn"
MessageX = 115
DisplayMessage()
'clear boxes
GraphicsWindow.BrushColor = GraphicsWindow.BackgroundColor
For I = 1 To 9
BoxMark[I] = ""
GraphicsWindow.FillRectangle(BoxX[I] - 5, BoxY[I] - 5, 90, 90)
EndFor
GraphicsWindow.BrushColor = "Black"
GraphicsWindow.FontSize = 16
Controls.SetButtonCaption(StartStopButton, "Stop Game")
Controls.HideControl(OptionsButton)
Controls.HideControl(ExitButton)
NumberClicks = 0
If (NumberPlayers = 1 And YouGoFirst = "false") Then
ComputerTurn()
EndIf
EndSub
Sub StopGame
'restore buttons
GameStatus = "Stopped"
GraphicsWindow.BrushColor = "Black"
GraphicsWindow.FontSize = 16
Controls.SetButtonCaption(StartStopButton, "Start Game")
Controls.ShowControl(OptionsButton)
Controls.ShowControl(ExitButton)
DisplayMessage()
EndSub
Sub MouseDownSub
If (GameStatus = "Playing") Then
'find which box was clicked
x = GraphicsWindow.MouseX
y = GraphicsWindow.MouseY
ClickedBox = 0
For I = 1 To 9
If (x > BoxX[I] And x < BoxX[I] + 80) Then
If (y > BoxY[I] And y < BoxY[I] + 80) Then
ClickedBox = I
Goto GotIt
EndIf
EndIf
EndFor
GotIt:
If (ClickedBox <> 0) Then
'if already clicked then exit
If (BoxMark[ClickedBox] <> "") Then
Goto LeaveSub
EndIf
MarkAndCheck()
EndIf
EndIf
LeaveSub:
EndSub
Sub MarkAndCheck
NumberClicks = NumberClicks + 1
If (XTurn) Then
BoxMark[ClickedBox] = "X"
DrawX()
XTurn = "false"
Message = "O's Turn"
MessageX = 115
DisplayMessage()
Else
BoxMark[ClickedBox] = "O"
DrawO()
XTurn = "true"
Message = "X's Turn"
MessageX = 115
DisplayMessage()
EndIf
'check for win - will establish a value for WhoWon
CheckForWin()
If (WhoWon <> "") Then
Sound.Stop(Program.Directory + "\tada.wav")
Sound.Play(Program.Directory + "\tada.wav")
Message = WhoWon + " Wins!"
MessageX = 115
StopGame()
ElseIf (NumberClicks = 9) Then
'draw
Sound.Stop(Program.Directory + "\beep.wav")
Sound.Play(Program.Directory + "\beep.wav")
Message = "It's a Draw!"
MessageX = 95
StopGame()
EndIf
If (NumberPlayers = 1 And WhoWon = "") Then
If (XTurn = "true" And YouGoFirst = "false") Or (XTurn = "false" And YouGoFirst = "true") Then
ComputerTurn()
EndIf
EndIf
EndSub
Sub DrawX
'draw blue X at ClickedBox
GraphicsWindow.PenColor = "Blue"
GraphicsWindow.PenWidth = 10
GraphicsWindow.DrawLine(BoxX[ClickedBox], BoxY[ClickedBox], BoxX[ClickedBox] + 80, BoxY[ClickedBox] + 80)
GraphicsWindow.DrawLine(BoxX[ClickedBox], BoxY[ClickedBox] + 80, BoxX[ClickedBox] + 80, BoxY[ClickedBox])
EndSub
Sub DrawO
'draw blue O at Clicked Box
GraphicsWindow.PenColor = "Blue"
GraphicsWindow.PenWidth = 10
GraphicsWindow.DrawEllipse(BoxX[ClickedBox], BoxY[ClickedBox], 80, 80)
EndSub
Sub CheckForWin
WhoWon = ""
'check all possible for wins
For I = 1 To 8
For J = 1 To 3
BoxNumber[J] = Text.GetSubText(PossibleWins[I], J, 1)
Mark[J] = BoxMark[BoxNumber[J]]
EndFor
If (Mark[1] = Mark[2] And Mark[1] = Mark[3] And Mark[2] = Mark[3] And Mark[1] <> "") Then
'we have a winner
WhoWon = Mark[1]
For J = 1 To 3
GraphicsWindow.BrushColor = "Red"
GraphicsWindow.FillRectangle(BoxX[BoxNumber[J]] - 5, BoxY[BoxNumber[J]] - 5, 90, 90)
ClickedBox = BoxNumber[J]
If (WhoWon = "X") Then
DrawX()
Else
DrawO()
EndIf
EndFor
EndIf
EndFor
EndSub
Sub ComputerTurn
If (SmartComputer <> "true") Then
'random logic
'put mark in Nth available square
N = Math.GetRandomNumber(9 - NumberClicks)
I = 0
For ClickedBox = 1 To 9
If (BoxMark[ClickedBox] = "") Then
I = I + 1
If I = N Then
Goto GotMark
EndIf
EndIf
EndFor
GotMark:
'put mark in ClickedBox
MarkAndCheck()
Else
'smart computer
BestMoves[1] = 5
BestMoves[2] = 1
BestMoves[3] = 3
BestMoves[4] = 7
BestMoves[5] = 9
BestMoves[6] = 2
BestMoves[7] = 4
BestMoves[8] = 6
BestMoves[9] = 8
'determine who has what mark
If (YouGoFirst) Then
ComputerMark = "O"
PlayerMark = "X"
Else
ComputerMark = "X"
PlayerMark = "O"
EndIf
'Step 1 (K = 1) - check for win - see if two boxes hold computer mark and one is empty
'Step 2 (K = 2) - check for block - see if two boxes hold player mark and one is empty
For K = 1 To 2
If K = 1 Then
MarkToFind = ComputerMark
Else
MarkToFind = PlayerMark
EndIf
For I = 1 To 8
N = 0
EmptyBox = 0
For J = 1 To 3
BoxNumber[J] = Text.GetSubText(PossibleWins[I], J, 1)
Mark[J] = BoxMark[BoxNumber[J]]
If (Mark[J] = MarkToFind) Then
N = N + 1
ElseIf (Mark[J] = "") Then
EmptyBox = BoxNumber[J]
EndIf
EndFor
If (N = 2 And EmptyBox <> 0) Then
'mark empty box to win (K = 1) or block (K = 2)
ClickedBox = EmptyBox
MarkAndCheck()
Goto LeaveComputerMove
EndIf
EndFor
EndFor
'Step 3 - find next best move
For I = 1 To 9
If (BoxMark[BestMoves[I]] = "") Then
ClickedBox = BestMoves[I]
MarkAndCheck()
Goto LeaveComputerMove
EndIf
EndFor
EndIf
LeaveComputerMove:
EndSub
**
**
Tic Tac Toe Game Program Review
The Tic Tac Toe game program is now complete. Save and Run the program and make sure it works as promised. Check that all options work correctly.
If there are errors in your implementation, go back over the steps of window and code design. Go over the developed code – make sure you understand how different parts of the program were coded. As mentioned in the beginning of this chapter, the completed program is saved as TicTacToe in the KidGamesSB\KidGamesSB Programs\TicTacToe folder.
While completing this program, new concepts and skills you should have gained include:
- Proper steps in program design.
- More practice in using subroutines.
- How to give the computer intelligence in playing games.
** **
**
**
Tic Tac Toe Game Program Enhancements
There are several ways to improve the Tic Tac Toe game. Some possibilities are:
- Use images in the grid. With such a change, you could use something other than X’s and O’s to mark the squares. Try using small digital photos (jpeg files).
- Add better messaging with a computer opponent. As implemented, the computer quickly makes its move. Perhaps, add a delay (see the Timer object) and perhaps some color changes in the grid so the computer has time to tell you where its move will be.
- In the current computer logic (Step 3), the computer always picks corners or sides in the same order. To make things a bit more unpredictable, add logic so the computer makes corner or side moves randomly based on the number of corner or side boxes that are empty.
- The computer in this game is pretty smart, but can be made smarter. Between Steps 2 and 3 in the current computer logic, we could add another step where the computer would be more aggressive. In this new step, have the computer search for horizontal, vertical or diagonal lines with one of its markers and two empty squares. By choosing one of the empty squares, the computer forces your next move. Even better is to find a place where with an additional mark, the computer sets up two wins, making it impossible for you to block him.
- For little kids, maybe the computer is a bit too smart. The random computer is an option, but goes to the other extreme of maybe not being smart enough. A change would be to add levels of intelligence – having the computer use its ‘smart’ logic sometimes and its ‘random’ logic sometimes. To do this, you could generate a random number from 1 to 100 and based on some threshold, determine whether to make a smart move or random move. By varying the threshold, you can make the computer extremely smart or not so smart. We implement such logic in the next game program, Match Game.
To purchase this book in its entirety, please see the Computer Science For Kids web site.
Excerpt © Copyright 2010-2017 By Kidware Software LLC All Rights Reserved. Computer Science For Kids, the Computer Science For Kids logo, and related trade dress are trademarks or registered trademarks of Kidware Software LLC. Philip Conrod & Lou Tylee have co-authored dozens of books and tutorials for beginning Microsoft Basic, Small Basic, Visual Basic, and Visual C# developers of all ages for over 25 years.
Small Basic > Small Basic Books > Programming Games with Small Basic > Chapter 6: Tic Tac Toe Program