Friday, September 14, 2012

Creating Tic-Tac-Toe with C# - Part 2

In the previous part of this tutorial, we created the Tic Tac Toe game for two players.
Now in Part-2 we will add the option to let the user Play against the computer.
So open your project and let's continue building..

Create a new form

Add a new Form to your project.
Tic Tac Toe
Leave its name to Form3, If you don't know how to add a new form check this: Creating Tic-Tac-Toe with C# - Part 1

Design

Add buttons, labels, radioButtons to make it look close to the picture on the right.

Naming:

change the name of your created buttons and radioButtons to:
XButtonOButtoneasyRBnormalRB, backButton.
We are going to use these names in our code later..

You can customize the design as you want, but here I just want to make a practical game, i'm not a Designer.

I forgot to change the Text property of the form,  change it to Tic Tac Toe or something else...
Tic Tac Toe


Now go to Form2 Design, select the tableLayoutPanel1 copy it to your clipboard, then paste it in Form3 Design.

The form will  look like the second picture.

Now look at the properties window, and find the Visible property, set it to False. 
Also change the button text from "Reset" to "Play again", it makes more sense, and its name to playAgainButton.

This will hide the game until the user select one of the two Buttons created previously.

In this part, I'm going to write only the Easy difficulty mode.

Coding

Declare some variables in the class of Form3
        bool[,] X, O;
        string player;
        bool finished; // true when the game finish
        int difficulity;
        string ButtonsIndexs; //Indexs of remaining buttons
        int x, y; //coordinate of button
        Random rnd; 
Lets write some helper methods now.

Reset Variables

        private void resetVars()
        {
            ButtonsIndexs = "123456789";
            finished = false;
            rnd = new Random();
            difficulity = 0;
            O = new bool[3, 3];
            X = new bool[3, 3];
        }

Writing on a Button

private void Write(string p, Button targetButton)
        {
            if (p == "O")
            {
                targetButton.ForeColor = Color.Red;
                O[x, y] = true; //the place (x,y) is occupied by an 'O'
            }
            else
            {
                targetButton.ForeColor = Color.Blue;
                X[x, y] = true; //the place (x,y) is occupied by an 'X'
            }
            targetButton.Text = p; //Writing X or O

            //Removing the index of the clicked button from ButtonIndexs
            if (targetButton == button1) 
ButtonsIndexs=ButtonsIndexs.Remove(ButtonsIndexs.IndexOf('1'), 1);
            if (targetButton == button2)
ButtonsIndexs=ButtonsIndexs.Remove(ButtonsIndexs.IndexOf('2'), 1);
            if (targetButton == button3)
ButtonsIndexs=ButtonsIndexs.Remove(ButtonsIndexs.IndexOf('3'), 1);
            if (targetButton == button4)
ButtonsIndexs=ButtonsIndexs.Remove(ButtonsIndexs.IndexOf('4'), 1);
            if (targetButton == button5)
ButtonsIndexs=ButtonsIndexs.Remove(ButtonsIndexs.IndexOf('5'), 1);
            if (targetButton == button6)
ButtonsIndexs=ButtonsIndexs.Remove(ButtonsIndexs.IndexOf('6'), 1);
            if (targetButton == button7)
ButtonsIndexs=ButtonsIndexs.Remove(ButtonsIndexs.IndexOf('7'), 1);
            if (targetButton == button8)
ButtonsIndexs=ButtonsIndexs.Remove(ButtonsIndexs.IndexOf('8'), 1);
            if (targetButton == button9)
ButtonsIndexs=ButtonsIndexs.Remove(ButtonsIndexs.IndexOf('9'), 1);
        }

Easy difficulity, Computer turn

        private void EasyPCWrite(string p)
        {
            // a Random button index (to chose a random free button)
            int index = int.Parse(ButtonsIndexs[rnd.Next(ButtonsIndexs.Length)].ToString());
            switch (index)
            {
                case 1: x = y = 0; Write(p, button1); break;
                case 2: x = 0; y = 1; Write(p, button2); break;
                case 3: x = 0; y = 2; Write(p, button3); break;
                case 4: x = 1; y = 0; Write(p, button4); break;
                case 5: x = 1; y = 1; Write(p, button5); break;
                case 6: x = 1; y = 2; Write(p, button6); break;
                case 7: x = 2; y = 0; Write(p, button7); break;
                case 8: x = 2; y = 1; Write(p, button8); break;
                case 9: x = 2; y = 2; Write(p, button9); break;
            }
        }

Normal difficulty, Computer turn

        private void NormalPCWrite(string p)
        { 
           //Coming in Part 3
        }

Checking if any diagonal wins

        private bool Diagonals(bool[,] current)
        {
            return (
                (current[0, 0] && current[1, 1] && current[2, 2])
                ||
                (current[0, 2] && current[1, 1] && current[2, 0])
                );
        }

Checking if any row wins

        private bool Row(int p, bool[,] current)
        {
            return (current[p, 0] && current[p, 1] && current[p, 2]);
        }

Checking if any column wins

        private bool Column(int p, bool[,] current)
        {
            return (current[0, p] && current[1, p] && current[2, p]);
        }

Checking for win or Draw

 
private bool CheckForWinOrDraw(bool[,] current)
        {
            if (Row(0, current) || Row(1, current) || Row(2, current) || Column(0, current) || Column(1, current) || Column(2, current) || Diagonals(current))
            {
                //updating score
                if (current == X)
                    xLabel.Text = (int.Parse(xLabel.Text) + 1).ToString();
                else
                    oLabel.Text = (int.Parse(oLabel.Text) + 1).ToString();
                //game finished
                finished = true;
                MessageBox.Show(string.Format("{0} Wins", (ButtonsIndexs.Length%2==0)?"X":"O"));//showing result
            }
            else
            {
                if (ButtonsIndexs=="")
                {
                    //game finished
                    finished = true;
                    MessageBox.Show("Draw");
                }

            }
            return finished;
        }

Checking if the button was clicked before (by PC or player)

        private bool wasClickedBefore(Button button, out int x, out int y)
        {
            x = y = 0;
            switch (button.Name)
            {
                case "button1": x = 0; y = 0; break;
                case "button2": x = 0; y = 1; break;
                case "button3": x = 0; y = 2; break;
                case "button4": x = 1; y = 0; break;
                case "button5": x = 1; y = 1; break;
                case "button6": x = 1; y = 2; break;
                case "button7": x = 2; y = 0; break;
                case "button8": x = 2; y = 1; break;
                case "button9": x = 2; y = 2; break;
            }
            return (X[x, y] || O[x, y]);
        }

only one helper method left, it's the computer turn in Normal mode. I will leave it to the third part of this tutorial.
Now lets write the code to the buttons.

Play again button event

private void playAgianButton_Click(object sender, EventArgs e)
        {
            resetVars();
            button1.Text =
            button2.Text =
            button3.Text =
            button4.Text =
            button5.Text =
            button6.Text =
            button7.Text =
            button8.Text =
            button9.Text =
            "";
            if (player == "O") player = "X";
            else
            {
                player = "O";
                if (difficulity == 0)
                    EasyPCWrite("X");
                else
                    NormalPCWrite("X");
            }

        }

Game buttons Click(the 9 buttons)

private void gameButtons_Click(object sender, EventArgs e)
        {
            if(!wasClickedBefore(sender as Button,out x, out y)&&!finished)
            {
                if(player=="O")
                {
                    //The user turn
                    Write("O",sender as Button);
                    if (!CheckForWinOrDraw(O))
                    {
                        //PC turn
                        if (difficulity == 0)
                            EasyPCWrite("X");
                        else
                            NormalPCWrite("X");
                        CheckForWinOrDraw(X);
                    }
                }
                else
                {
                    //The user turn
                    Write("X",sender as Button);
                    if(!CheckForWinOrDraw(X))
                    {
                        //PC turn
                        if(difficulity==0)
                            EasyPCWrite("O");
                        else
                            NormalPCWrite("O");
                        CheckForWinOrDraw(O);
                    }
                }
            }
        }

The back button (second one)

        private void back_Click(object sender, EventArgs e)
        {
            easyRB.Checked = true;
            playAgainButton.PerformClick();
            xLabel.Text = oLabel.Text = "0";
            tableLayoutPanel1.Visible = false;
            this.Size = new Size(229, 300);
        }

Radio buttons checked

        private void easyRB_Click(object sender, EventArgs e)
        {
            difficulity = 0; //Easy level
        }
        private void normalRB_CheckedChanged(object sender, EventArgs e)
        {
            difficulity = 1; //Normal Level
        }

Main buttons (XButton and OButton)

        private void mainButtons(object sender, EventArgs e)
        {
                        player = (sender as Button).Text; //user choice, X or O
            this.Size = new Size(382, 300); //changes form size
            tableLayoutPanel1.Visible = true;
            if (difficulity == 0)
                if (player == "O")
                    EasyPCWrite("X");
                else
                    return;
            else
                if (player == "O")
                    NormalPCWrite("X");
        }

Back button

        private void backButton_Click(object sender, EventArgs e)
        {
            Form1 f1 = new Form1();
            this.Hide();
            f1.Show();
        }

Form3_Load event

        private void Form3_Load(object sender, EventArgs e)
        {
            resetVars();
        }

Last step, go to Form1 and double click the PC vs Player button..

Player vs PC button

        private void playerVsPC_Click(object sender, EventArgs e)
        {
            Form3 f3 = new Form3();
            this.Hide();
            f3.Show();
        }
We've created today the Easy PC mode of player vs PC option in Tic Tac Toe game.
It's almost done, Last part of this tutorial will include the Normal PC mode, and some other final touches for your game to be ready!
Stay tuned, if you have any question post it to my Facebook page and I'll try to help, see you later.

>>CLICK FOR THE NEXT PART OF THIS TUTORIAL