Page 1 of 1

Tic Tac Toe Step by Step

#1 RexGrammer  Icon User is offline

  • Coding Dynamo
  • member icon

Reputation: 181
  • View blog
  • Posts: 777
  • Joined: 27-October 11

Posted 03 December 2011 - 03:24 PM

Tic Tac Toe Tutorial

Hello,
In this tutorial I will try to explain how to make a very good Tic Tac Toe game.
For the purpose of maximizing the compatibility we will write the game in .NET Framework 2 (But any other version will also do as well)

If you're not familiar to the rules of tic tac toe look HERE (Wikipedia)

1. Open your IDE and make a new project, call it Tic Tac Toe (If you want)
2. Open the project properties tab, go to the resources
3. Add these images for X and O (Download Images Here), of course you can put any image you want there...

OK we have our project prepared, now's the time for the real stuff...

Before we start to code we will just think through the process of creating this game:

Let's think of what will our game need to have:
1. It will need to have 9 buttons for fields (where would you play if it hasn't them? :) )
2. It will need to have a button to start a new game
3. It will need to have a close button
4. It will need to have two radio Buttons to select if you want the single Player or the multi Player mode (optional)
5. It can have a menu strip with the new game and exit menu item.

Before you put these controls in the designer let's think a bit more...

How are we going to realize the public interface?

1. Well for starters we will need to have a resize method so that our controls stay accordingly positioned always
2. We will need to have a reset method so that we can start a new game
3. We will need to have a check method to see If anyone has won
4. We will need to have some computer logic for the AI
5. We also need to have some logic what happens when a button is clicked

So that wraps up our planing of the game
Enough with the 'foreplay' time to do some code

Not that fast actually...

First put down your design, I suggest you put the buttons like this so that we don't get any misunderstanding problems

Posted Image

And then I added all of the controls we discussed so I got a window layout like this

Posted Image

So now we finally get to actually coding something...

Declarations

This one is short...
We will add two images, one X and one O, we will use the images from the resources (if you added it at the start)
AND we will add a player Turn indicator for the multi player mode so that the AI knows then to put X and when O

        //Declaring the variables in the project

        //The turn indicator, for multi player mode
        private bool player Turn = false;

        //The images for x and o
        private Image x = Properties.Resources.X;
        private Image o = Properties.Resources.O;


Resize Method

We will start with the resize method:

So we need to set the button width, we will set it to the (client rectangle width - radio buttons width) / 3 (it looks nice that way)

            //Declare and initialize a local variable, the default button width
            int defaultWidth = (this.ClientRectangle.Width - radioButton1.Width) / 3;
            //Set the buttons width to the default one
            button1.Width = button2.Width = button3.Width = button4.Width = button5.Width = button6.Width = button7.Width = button8.Width = button9.Width = defaultWidth;



Next we set the height, I set this one to the (client rectangle height - menu strip height) / 3 (again it looks nice that way)

            //Declare and initialize a local variable, the default button height
            int defaultHeight = (this.ClientRectangle.Height - menuStrip1.Height) / 3;
            //Set the buttons height to the default one
            button1.Height = button2.Height = button3.Height = button4.Height = button5.Height = button6.Height = button7.Height = button8.Height = button9.Height = defaultHeight;



Next we need to left align the buttons, a thing to notice is that there are 3 columns and the buttons of a same column have the same left distances

            //Declare the two positions for the two columns
            int secondColumn = button1.Width;
            int thirdColumn = 2 * button1.Width;
            //Position the buttons to the according column
            button1.Left = button4.Left = button7.Left = 0;
            button2.Left = button5.Left = button8.Left = secondColumn;
            button3.Left = button6.Left = button9.Left = thirdColumn;



Notice there isn't any first row, that's because it's left distance is 0

Now we top align the buttons, a thing to notice is that there are 3 rows

           //Declare the three positions for the two rows
            int firstRow = menuStrip1.Height;
            int secondRow = menuStrip1.Height + button1.Height;
            int thirdRow = menuStrip1.Height + 2 * button1.Height;
            //Position the buttons to the according row
            button1.Top = button2.Top = button3.Top = firstRow;
            button4.Top = button5.Top = button6.Top = secondRow;
            button7.Top = button8.Top = button9.Top = thirdRow;



Now we position the radio buttons

           //Positions the left anchor of the radio buttons
            radioButton1.Left = button1.Left + button1.Width + 5;
            radioButton2.Left = radioButton1.Left;

            //Positions the top anchor of the radio buttons
            radioButton1.Top = button1.Top;
            radioButton2.Top = radioButton1.Top + radioButton1.Height + 10;



I trust you understand how this is aligned... :)

Now we just need to align the two remaining buttons (new game and exit)

            //Positions the left anchor of the buttons
            button10.Left = button11.Left = radioButton1.Left;

            //Positions the top anchor of the buttons
            button10.Top = radioButton2.Top + radioButton2.Height + 10;
            button11.Top = button10.Top + button10.Height + 10;



So my resize method looks like this:

Spoiler


Notice that it's void since it doesn't return any value, and that it's private since it is to be called from the same class

If you build and run your program, you'll see that nothing happens :)
That's because we haven't called the method anywhere...

I called the method in the Form1_Load and Form1_Resize events

Now when you build and run your program you'll see that it's always nicely positioned and sized no matter what you do to the form... :)
Reset Method

Now for the reset method:

We need to make all of the buttons background images to nothing (that is to remove the background images)
We also need to make sure that the player 2 turn is not active

private void _Reset()
        {
            //This resets the game (Initializes a new game)

            button1.BackgroundImage = null;
            button2.BackgroundImage = null;
            button3.BackgroundImage = null;
            button4.BackgroundImage = null;
            button5.BackgroundImage = null;
            button6.BackgroundImage = null;
            button7.BackgroundImage = null;
            button8.BackgroundImage = null;
            button9.BackgroundImage = null;

            playerTurn = false;
        }



This was my reset method
Check Method


This one is rather long and I will explain only a part of it and the logic behind it, I trust you can figure out the rest (If not I will include the method at the end of this paragraph)

So this time I will firstly put code and explain it
if (button3.BackgroundImage == x)
            {
                if (button5.BackgroundImage == x)
                {
                    if (button7.BackgroundImage == x)
                    {
                        //Show the message to tell who has won
                        MessageBox.Show("X has won!");
                        return;
                    }
                }
            }



This is a nested if statement, so we check if button 3 has been clicked by the player (X) then if the button5 and finally if button7 is clicked, if this is all true then X has won and then a MessageBox should be showed

Logic:
We will check for every combination if X has won and also for O...
If buttons 1, 2 and 3
If buttons 1, 4, and 7
....
And the same for O

Here's the full method

Spoiler

Computer Logic


There are two ways to do this: the 'stupid' way and the 'half-smart' way (or the 'half-stupid' choose your favorite)...


The stupid way would be to program every possible move and the response to it
It is rather long and tedious and there is no gain, so I won't explain it here

The 'half-smart' way is to actually put some logic
So the computer priorities should be:
1. Play a move that can win (If that isn't possible -->)
2. Play a move that stops your opponent from winning (If that isn't possible -->)
3. Play any move

This shortens the actual amount of work but there is still a considerable amount of work to do

I won't explain everything just a few examples, the rest is up to you (I will still post the whole method bellow)

if (button1.BackgroundImage == o && button2.BackgroundImage == o)
            {
                if (button3.BackgroundImage == null)
                {
                    button3.BackgroundImage = o;
                    _Check();
                    return;
                }
            }



This is an example of the wining move
So if the buttons 1 and 2 are already pressed by the AI and button 3 isn't the press it...
The check to see if someone has one (And the computer has...)

if (button1.BackgroundImage == x && button2.BackgroundImage == x)
            {
                if (button3.BackgroundImage == null)
                {
                    button3.BackgroundImage = o;
                    _Check();
                    return;
                }
            }



This is an example of the defending move
Again if buttons 1 and 2 are already pressed then press button 3 to prevent the computer from winning
Check to see if anyone has won (And no one has) then return to the selection of moves

if (button1.BackgroundImage == null)
            {
                button1.BackgroundImage = o;
                _Check();
                return;
            }



This is an example of just playing any button.
If button 1 isn't taken then press it
Check to see if there are any winners if not then return to the move selection
Button Click Logic

The last remaining thing to do is to add the behind the curtains logic in the buttons

So here also I will first add the method and then explain it...

 private void _Buttonclick(ref Button theButton)
        {
            //This is the button happenings
            if (radioButton1.Checked)
            {
                if (theButton.BackgroundImage == null)
                {
                    //Sets the background image of the button to x
                    theButton.BackgroundImage = x;
                    //Checks to see if the match is over
                    _Check();
                    //Lets the computer play
                    _ComputerPlay();
                }
                else
                    MessageBox.Show("The wanted button is already chosen!");
            }
            else if (radioButton2.Checked)
            {
                if (theButton.BackgroundImage == null)
                {
                    if (playerTurn == false)
                    {
                        theButton.BackgroundImage = x;
                        playerTurn = true;
                        _Check();
                    }
                    else
                    {
                        theButton.BackgroundImage = o;
                        playerTurn = false;
                        _Check();
                    }
                }
                else
                {
                    //Shows a message to tell that the button is already chosen
                    MessageBox.Show("The wanted button is already chosen!");
                }
            }
            else
            {
                //Shows a message to tell the player that it has first to select a game mode
                MessageBox.Show("Please chose a mode of play first...");
            }
        }



So just a thing to notic is the ref keyword, that tells the compiler to REFERENCE the button and NOT COPY IT INTO A BUFFER so that any changes that happen in the method parameter happen in the referenced button

If the single player mode is selected and if the button that you are clicking is not taken then mark that button and check to see if anyone has won if not the let the computer play, and if the button is taken then tell the user that...
And if the multi player mode is selected the in accordance to the player turn mark the button X or O then check to see if anyone has won, if the button is taken tell that to the user
And if no radiobutton is slected tell the user to select a game mode

Note: The given computer logic makes him unbeatable so that we don't need to make the check return a bool indicating if anyone has won, since the player can't win. The checking if X has one is there just for the multi player mode! :)

You should implement the button logic in every button click event like this
this._Buttonclick(ref button1); // If it's button one event


Do this for all nine buttons
Accesories

Call this method on the exit button and the exit toolstrip menu item
Close();



Call this method in the new game button adn toolstrip menu item
_Reset();



You could also add the ability for the user to play using the keyboard like this
 private void Form1_KeyDown(object sender, KeyEventArgs e)
        {
            //Gets the key that was pressed and presses the corresponding button
            switch (e.KeyData)
            {
                case Keys.NumPad1:
                    button7.PerformClick();
                    break;
                case Keys.NumPad2:
                    button8.PerformClick();
                    break;
                case Keys.NumPad3:
                    button9.PerformClick();
                    break;
                case Keys.NumPad4:
                    button4.PerformClick();
                    break;
                case Keys.NumPad5:
                    button5.PerformClick();
                    break;
                case Keys.NumPad6:
                    button6.PerformClick();
                    break;
                case Keys.NumPad7:
                    button1.PerformClick();
                    break;
                case Keys.NumPad8:
                    button2.PerformClick();
                    break;
                case Keys.NumPad9:
                    button3.PerformClick();
                    break;
                case Keys.Divide:
                    button10.PerformClick();
                    break;
                case Keys.Multiply:
                    if (radioButton1.Checked == false && radioButton2.Checked == false)
                        radioButton1.PerformClick();
                    else if (radioButton1.Checked)
                        radioButton2.PerformClick();
                    else if (radioButton2.Checked)
                        radioButton1.PerformClick();
                    break;
            }
        }



If you're adding this make sure that the KeyPreview property of the form is on true!!! :)

The switch statement takes a value and compares it to it's cases, if nothing is true the do nothing... :)

We are doing this in the Form1_KeyDown event so that we can check the key pressed by using e.KeyData

This more or less expains itself, no need to reinvent the wheel



And there you go we made a feature rich Tic Tac Toe game

If you need some more help or you need the source I uploaded it here and you can see the project at my GitHub Repo.

PM me or post if you have more questions.

Attached File(s)



Is This A Good Question/Topic? 0
  • +

Replies To: Tic Tac Toe Step by Step

#2 tlhIn`toq  Icon User is offline

  • Please show what you have already tried when asking a question.
  • member icon

Reputation: 5316
  • View blog
  • Posts: 11,361
  • Joined: 02-June 10

Posted 08 December 2011 - 11:02 AM

*
POPULAR

I don't mean to rain on your parade, but we do try to stress good design. Rookies for years that come here will look at this and use it as a guide for their habits. Tic-Tac-Toe is a common first year assignment so hundreds of students will copy this and submit it as their homework. We would prefer to not use freshman-level examples as the right way to do things. That becomes rookies trying to teach rookies, and the perpetuation of bad habits that have to be unlearned before good habits can take root.

While I think it wonderful and exciting that you want to give back to the community and offer up tutorials for coding - I REALLY do think this is great - I'm not sure you're quite ready to do that yet. I don't think this tutorial is quit ready to be what DIC wants to use as an example of how it should be done. This would have been a great thread to offer up as "Please critique my code"; spawning a lot of conversation, debate about game logic to make it smarter, suggestions for improved GUI design and so on.

Quote

So just a thing to notic is the ref keyword, that tells the compiler to REFERENCE the button and NOT COPY IT INTO A BUFFER so that any changes that happen in the method parameter happen in the referenced button

Objects like buttons are passed by reference automatically and not copied. If you make a change to the button where you have done this without it being a ref, it will still change the original button.

A couple critiques:

button1-button9... This naming isn't too bad as they are in the 1-9 keypad type arrangment.
But button10, button11 are not good names and we don't recommend keeping the default names that Visual Studio assigns. It makes the code hard to read. Renaming GUI controls is almost always the very first thing someone should do. So these become btnNew and btnExit. By renaming controls ASAP it makes the automatic naming of methods by Visual Studio much cleaner. Which would you rather see in your code when you open this project 6 months from now?

button10_click(object sender, eventargs e)
or
btnNewGame_click(object sender, eventargs e)

Same with the radio buttons. rbSinglePlayer and rbTwoPlayer are a lot more readable/maintainable than radiobutton1 and radiobutton2

The nearly 50 lines of resize method just isn't needed. There are GUI controls that will do all that for you. If you put your 9 game buttons on a TableLayoutPanel, set each button to Dock.Fill and anchor all four sides of the TableLayoutPanel then all the resize and reposition is done for you.
Spoiler


There is no need to have all these individual button_click handlers.
Spoiler

The (object sender) is the button that was clicked. You can have all your buttons point to the same method to handle their click then check who is the sender. (lines 67-79 GameSquareButton_Click in the code below)

The _check() method is 196 line of brute force. It works. But not what I would call elegant. With a little planning the same can be done in a couple nested loops of 12 total lines. May I offer an alternative?
Each row is added to a list, as is each column and diagonal. Each of those lists is added to a list. Now you just loop through to see if you have a winning combination. My example uses the .Text property to see if it is X or O, which also makes it easy to say "The winner is : X" because the winner is the text on the button. But its just an example. You could still use the background image if you like for aesthetics.

The end result of reworking the majority of your logic reduces about 800 lines of code to these 125 lines
Spoiler

[see this post's attachment for the Visual Studio 2010 solution as a .zip]

I did not completely rework your 600 lines of ComputerPlay(). If you apply the same looping concepts to replace the brute force approach you have another few hundred lines of code that can be reduced.

PLEASE do not take this badly. I think its a great early effort while you are learning to code. I think your attitude about sharing with the community is something we need more of in today's world. I do believe you have the potential to become a good coder. You just have to be patient about getting there before you start telling others the right way to do things. Please keep up the good work. Please feel free to post projects like this for review by the more experienced volunteers. Having half a dozen senior coders offer up opinions and suggestions is a great way to learn a LOT: Not just about raw code about about architecting your software.

Attached File(s)


Was This Post Helpful? 5
  • +
  • -

Page 1 of 1