Jump to content

Basics of while loop, char arrays in c and Tic Tac Toe using them (Part 2)

- - - - -

  • Please log in to reply
6 replies to this topic

#1
fayyazlodhi

fayyazlodhi

    Programming Expert

  • Members
  • PipPipPipPipPipPip
  • 403 posts
As explained and referred in part 1 earlier of this tutorial, we are going to actually create a working tic tac toe game.

I intend to walk through the game as I personally thought and implemented it.

But before going into implementation, to get a feel for the game and perhaps have some fun, just copy paste the code given at the end of this tutorial into your favorite c or c++ IDE, compile and run it.

Following image might give some feel of the game.

Attached File  tictactoe.jpg   47.89K   155 downloads

Now moving towards the code, firstly, we already agreed with a basic board of 3X3 can be created using a char array in c. Details have been discussed already in part 1.

We also saw how we can iterate through this board using a nested while loop. If any of those aspects is not clear yet, feel free to ask questions in the comments and I would be more than happy to explain.

The main function of my program starts with printing tic tac toe on a line.

Next it goes in a while loop which RUNS ONCE PER GAME i.e. our one complete game runs inside a single iteration of this while loop. If you break from this loop game does not proceed. The terminating condition of this while loop is like (1) which means always true or infinite loop. So this loop keeps running forever. You can hit control + c to break and hence exit from the program though.

Then I wrote a function called clear_board() whose job is to run a nested while loop and filling each element of the board with ‘.’ (dots). This dot basically is used to indicate that the location is currently empty and a player could choose to fill it on his turn.

Next comes a function called print_board() which is nearly the same as clear_board(). The only difference is that instead of filling board with empty chars, it prints the current value of that element. So in turn we see the whole board using this function.

Both of these functions are called in the beginning of each game so that we have a clean board and printed in front of the players to begin with. Of course they are written as separate functions because they might be used and other instances too.

Next we have a while loop which runs the number of times there are possible moves within a single game with each player taking alternate turns.

Then we have used a simple char array named p in which we copy a string “Player One (or Two)”. Since there are a total of nine moves and the first player is always ‘X’ so all even numbered moves are of Player One which is ‘X’ and odd numbered ones are ‘O’. This distinction is important since we put the corresponding char of a move (X or O) as well as declare a winner through this string.

This string is passed to a function called “take_turn” whose job is to do whatever is required for executing a turn. This function would return 1 if the game is won in which case we immediately break from the current loop even if we haven’t completed max 9 moves.

After termination of the num moves loop, we print the result i.e. if nobody won. In case of win, the result is already printed inside the take_turn function.

Then we proceed to starting the game again from scratch.

If you wish to exit the game hit ctrl+C.

Now we dig into the code of take_turn function. First it decides whether it is player one or two which is passed as a string, and assigns ‘X’ or ‘O’ to a char variable which is to be placed on the player’s chosen location.

Then it asks the user to input coordinates (row col) of his current move. Please note that these are zero based arrays so each row is from 0 – 2 and each col is also from 0 – 2. In arrays, rows and column start from top left of the array so the first top left element is 0 0, Bottom left element is 2 0 (2nd row and 0th column), top right is 0 2 and bottom right is 2 2.

The above representation is different from the coordinates that we are normally familiar through geometry so please make sure you get it right.

Right after the input of both characters, there is a while loop whose job is to determine if your input coordinates are valid (i.e. they are in the range 0-2 and 0-2 and also we are not overwriting a previously filled move). It keeps on asking for input again until valid coordinates are filled.

Once validated, it gets over the loop (it won’t go into the loop if you enter correct coordinates the first time), and fills the corresponding location in board with the current move character. Also it increments the global variable num_moves which is tested in the outer while loop tracking the current number of moves of the game.

Then it calls the print_board function once again to show the entire board with the current situation. This needs to be done after every turn since players should see and choose their next move accordingly.

Now remains the most crucial function named “evaluate” which might appear a bit tricky in the first look at the code. On a high level this function, decides if any of the players has won the game and indicates this by returning 1, otherwise it returns 0.

The function “evaluate” keeps track of multiple things. First it checks all possibilities of a game being won. Since a player needs 3 horizontal or vertical or diagonal ‘Xs’ or ‘0s’ to win, there are following possibilities:

3 same chars (X’s or 0’s) in the first row
3 same chars (X’s or 0’s) in the second row
3 same chars (X’s or 0’s) in the third row
3 same chars (X’s or 0’s) in the first column
3 same chars (X’s or 0’s) in the second column
3 same chars (X’s or 0’s) in the third column
3 same chars (X’s or 0’s) in the left diagonal
3 same chars (X’s or 0’s) in the right diagonal

This makes a total of 3+3+2 = 8 checks. Each of them is represented by an ‘if’ block. Because we need to test 3 locations each we compare first and second for equality AND second and third for equality.

Another factor is, we cannot declare a win if all matches are due to empty i.e. ‘.’ characters. Since this function is called after every move, if it results in a win, it is one by the current player who just took his turn. Code follows:


#include <iostream>

#include <cstring>

#include <cstdio>

using namespace std;


char board[3][3];

int num_moves;


void print_board(void)

{

    int row = 0;


    cout << endl;

    while(row < 3)

    {

        int col = 0;


        while(col < 3)

        {

            cout << board[row][col] << " ";

            col++;

        }

        cout << endl;

        row++;

    }

}


void clear_board(void)

{

    int row = 0;


    while(row < 3)

    {

        int col = 0;


        while(col < 3)

        {

            board[row][col] = '.'; // empty char

            col++;

        }

        row++;

    }


    num_moves = 0;

}


/* Returns 1 if game won and 0 otherwise. */

int evaluate()

{

    if (((board[0][0] == board[0][1]) && (board[0][1] == board[0][2]) && board[0][0] != '.') || // first row

        ((board[1][0] == board[1][1]) && (board[1][1] == board[1][2]) && board[1][0] != '.') || // sec row

        ((board[2][0] == board[2][1]) && (board[2][1] == board[2][2]) && board[2][0] != '.') || // thrd row

        ((board[0][0] == board[1][0]) && (board[1][0] == board[2][0]) && board[0][0] != '.') || // first col

        ((board[0][1] == board[1][1]) && (board[1][1] == board[2][1]) && board[0][1] != '.') || // sec col

        ((board[0][2] == board[1][2]) && (board[1][2] == board[2][2]) && board[0][2] != '.') || // thrd col

        ((board[0][0] == board[1][1]) && (board[1][1] == board[2][2]) && board[0][0] != '.') || // left diagnol

        ((board[2][0] == board[1][1]) && (board[1][1] == board[0][2]) && board[2][0] != '.'))   // right diagnol

        return 1;

    else

        return 0;

}


int take_turn(char *player)

{

    int r=-1, c=-1;

    char mv;


    if(strstr(player, "One") != NULL)

        mv = 'X';


    else

        mv = 'O';


    cout << "\nEnter coordinates of your move (row col) : " << player << " : ";


    cin >> r >> c;


    while (!(r>-1 && r<3 && c>-1 && c<3) || board[r][c] != '.')

    {

        cout << "Invalid coordinates: Please try again (row col): ";

        cin >> r >> c;

    }


    board[r][c] = mv;

    num_moves++;


    print_board();


    if(evaluate() == 1)

    {

        cout << "Game won by " << player << endl;

        return 1;

    }


    return 0;

}


int main()

{

    int result;

    cout << "Tic Tac Toe" << endl;


    while(1) // Per game while loop

    {

        clear_board();


        print_board();



        while(num_moves < 9) 

        {

            char p[20];


            if(num_moves % 2 == 0)

                strcpy(p, "Player One");

            else

                strcpy(p, "Player Two");


            if ((result = take_turn(p)) == 1)

                break;


            cout << "\n" << "Next Move" << endl ;

        }


        if(result == 0)

            cout << "No body won - " << endl; 

        

        cout << "starting again" << endl;


        //char unused;

        //scanf("%c\n",&unused);

    }


    return 0;

}


Today is the first day of the rest of my life

#2
tunde

tunde

    Newbie

  • Members
  • PipPip
  • 13 posts
This Code Did not run on Turbo

#3
fayyazlodhi

fayyazlodhi

    Programming Expert

  • Members
  • PipPipPipPipPipPip
  • 403 posts

tunde said:

This Code Did not run on Turbo

A couple of things i would mention.

1. I assume you were referring to Turbo C++ and it is pretty old compiler. So unless you have a special reason, i would suggest using a newer one such as Visual studio express or Code blocks. These are IDE's containing compiler, linker and pretty good editors. I wrote this using VS.

2. Nevertheless to run on TC, please make the following changes:

#include <iostream.h> // tc only recognizes old names with .h and doesn't recognize the new naming scheme
#include <string.h>
#include <stdio.h>
//using namespace std; // this also needs to be commented out and works fine without that on TC.

I ran and verified in TC 3.1 and played one full game.

3. If you are running into errors, it is a good idea to paste them here, so we know for sure what is causing you problems and can suggest a way out in a better way.

Hope that helps.

Regards,
Fayyaz

Edited by fayyazlodhi, 25 January 2012 - 07:06 AM.
fixing typos

Today is the first day of the rest of my life

#4
tunde

tunde

    Newbie

  • Members
  • PipPip
  • 13 posts
Ok...Thank You Very Much

#5
Blimp

Blimp

    Programmer

  • Members
  • PipPipPipPip
  • 154 posts
Great tutorial. Sure it will help a lot of people out!

#6
naveenkothiyal

naveenkothiyal

    Newbie

  • Members
  • Pip
  • 1 posts
what are the main difference b/w windows and unix:cursing:lol:

#7
Ljubov_86

Ljubov_86

    Newbie

  • Members
  • Pip
  • 1 posts
You completed some good points on forum.codecall.net . I did a search on the theme and found the majority of persons will have the same opinion with your phorum.




1 user(s) are reading this topic

0 members, 1 guests, 0 anonymous users