# The creation of Tic-Tac-Toe game using the C/C++ language, includes full code of the actual program

Essay by STUBBY{UK}University, Bachelor'sB, March 2003

Introduction

This is the final report showing all the work put into Mark Smith and Trevor Snaith's noughts and crosses program, for this semesters work. The report includes the aims throughout the project, which is to produce a noughts and crosses programme in "C", the problems which we encountered during writing the program and how we solved them. It shows the results of our noughts and crosses program including some printout from the program and an explanation of how the programme works. It also shows some improvements on how the programme could be made better, and a conclusion.

Aims

To develop a "C" program that allows two people to play "noughts and crosses. The computer must decide when the game has been won and by whom, or whether the game has been drawn. The computer should display the board after each move.

Results

When the programme is first run the opening screen appears as below:-

This opening screen is put together using a series of "printf" functions.

It asks the user if they wish to play, if the user decides to play they key in number one. The one is stored as an integer in a box called "play". Using an if statement, if play is equal to one then this follows:-

Once the one is pressed then the rules appear and using a printf statement to bring up "Player 1 (x) please enter your first name", Mark the first player then enters his name which is stored using a scanf statement. Once his name has been entered and the carriage return button is pressed then "Player 2 (o) please enter your first name" appears. Trevor the second player enters his name, which is also stored as a scanf statement. Once the carriage return is pressed the following appears:-

"Mark will go first" appears using a printf statement. Before the board appears the computer checks using a while statement to see if there is a win for "x - Mark", "0 - Trevor" or a draw, if none of these statements are true then the board is displayed and "Mark please enter a row number and press enter" appears. Mark goes first and enters in a row number followed by a column number, the computer checks to see if the space is occupied or not, if the box is occupied then "please enter a valid move" appears (See problems part). If the box is not occupied then the computer displays a new board with an "X" in the appropriate box and "Trevor please enter the row number and press enter" appears.

Before "Trevor please enter the row number and press enter" the computer checks for another win or draw and then prints the printf statement. Trevor enters his row and column, the computer checks to see if it is a valid move, then displays the "O" in the appropriate box. The computer then prompts Mark to take his go after checking for a win or a draw..

This sequence of events continues to occur until either there is a win by one of the two players or a draw. When a win or a draw occurs then the comparer prints out "Player "X" wins Mark is far superior than Trevor" or "Player "O" wins. Trevor is far superior than Mark", if it is a draw then it prints out "Sorry this game is a draw".

Then the programme ends.

The board

The board is a set of printf statements with a series of numbers, lines and %c's

printf (" COLUMNS\n\n");

printf (" [0] [1] [2]\n");

printf ("\n [0] %c | %c | %c \n", grid [0] [0], grid [0] [1], grid [0] [2] );

printf ("R ---| --- |----\n");

printf ("O [1] %c | %c | %c \n", grid [1] [0], grid [1] [1], grid [1] [2] );

printf ("W ---| --- |----\n");

printf (" [2] %c | %c | %c \n", grid [2] [0], grid [2] [1], grid [2] [2] );

There are three rows with three %c's in each of them, all of which have been given grid arrays so all the %c have been given a particular grid reference once a %c has been given a character it remains there till the programme finishes. The character can not be changed once a character has been placed there because of an if statement after the code which allows the player to make a move.

if

(grid [row] [column] ==' ')

(grid [row] [column] = 'X');

else

printf ("Please enter a valid move!\n");

After every move the computer check to see if there is a win or a Draw. To check to see if a win has occurred a global variable called "int check winX" checks to see if there is a win. The code is below:-

int check_winX ()

{

if

((grid[0][0]=='X' && grid[0][1]=='X' && grid[0][2]=='X') ||

(grid[1][0]=='X' && grid[1][1]=='X' && grid[1][2]=='X') ||

(grid[2][0]=='X' && grid[2][1]=='X' && grid[2][2]=='X') ||

(grid[0][0]=='X' && grid[1][0]=='X' && grid[2][0]=='X') ||

(grid[0][1]=='X' && grid[1][1]=='X' && grid[2][1]=='X') ||

(grid[0][2]=='X' && grid[1][2]=='X' && grid[2][2]=='X') ||

(grid[0][0]=='X' && grid[1][1]=='X' && grid[2][2]=='X') ||

(grid[2][0]=='X' && grid[2][1]=='X' && grid[2][2]=='X'))

WINX=1;

Else

WINX=0;

{

if (WINX==1 )

printf ("\n Player X wins. %s is far superior than %s\n",playerone,playertwo);

Wherever "int check winX" occurs in the programme then it checks for a win using the if-else statement above. If "WINX" is equal to any of the win lines above then WINX is equal to "1" else it is equal to "0". If "WINX" is equal to "1" then it prints out who is the winner. But if "WINX" is equal to "O" then play continues till there is a win or a draw.

There is another set of code almost identical to the one above but for the "O" win.

To check to see if a draw has occurred a global variable called "int check draw" checks to see if there is a draw. The code is below:-

int check_draw ()

{

if((grid[0][0]==('X')|| grid [0][0]==('O')) && (grid[0][1]==('X') || grid [0][1]==('O')) && (grid[0][2]==('X') || grid [0][2]==('O')) &&(grid[1][0]==('X')|| grid [1][0]==('O')) && (grid[1][1]==('X') || grid [1][1]==('O')) && (grid[1][2]==('X') || grid [1][2]==('O')) &&(grid[2][0]==('X')|| grid [2][0]==('O')) && (grid[2][1]==('X') || grid [2][1]==('O')) && (grid[2][2]==('X') || grid [2][2]==('O')))

DRAW=1;

else

DRAW=0;

{

if (DRAW==1)

printf ("\nSorry the game is a draw.");

Wherever "int check draw" occurs in the programme then it checks for a draw using the if-else statement above. If "DRAW" is equal to any of the draw lines above then "DRAW" is equal to "1" else it is equal to "0". If "DRAW" is equal to "1" then it prints out "sorry the game is a draw". But if "DRAW" is equal to "O" then play continues till there is a win or a draw.

Possible Enhancements

The program itself works perfectly fine as it stands, but there are many ways in which we would be able to improve game.

* One of our first mistakes was to think that this project would be fairly easy. If we had the opportunity to construct another program in 'C' we would make sure that we had a clear schedule that we would stick to by setting ourselves goals and targets, also using a lot more flow charts, pseudo code and structure diagrams to aid our progress. As we look back these would have been invaluable to us.

* To give the game another dimension it would have been a good idea to have a chance to play the computer. This meant that we would have to write code that would randomly generate the Row and Column co-ordinates taking into account what squares had been taken. (Extra enhancements on this suggestion could be that the computer had different levels of ability e.g. Novice, Amateur, Expert.)

* The size of the program is another way in which we could enhance the project. At the moment the code is pretty difficult to understand, so we believe that we could of condensed most of the code into more functions. This would of given the same result but made the code more legible, and professional looking.

* We believe that the program contains too many 'global' variables. Although this enables you to get away without using many functions it can, in large programs, be very difficult to see actually what part of the program changes the contents of the global variable. So again by using more functions it would tidy the program up so you know what part of the program does what.

* Going back to the idea of having a computer player, it would have been a good idea to have a computer-aided player. This would be the computer suggesting moves to the user whilst they were playing the computer.

* A minor enhancement would be to make the executable program more pleasing to the eye. This gives the program a more professional appearance.

* Another enhancement we should have used is the incorporation of more header files. The only one we used was '#include ', other ones might have been the '#include ', however we will be trying to incorporate the '#include ', as we are using arrays for names and the %s action to print out the players names.

* One of the lines of code in the program, although affective, is not a good way to write code for a program. The line I am referring to is the 'printf ("\n\n\n\n\n\n\n\n");' lines that are scattered throughout the program, they are a basic way of clearing the screen, but this is not very practical, and if the program was any longer it waste of memory. For further developments we would have used one of the many textbooks to use, or adapt one of the 'clear screen' functions.

* If we had more time we believe that it would have been a good idea to have a league system. This way you would be able to choose your name at the beginning of the program and your results stored in the league. The league itself could display things like, Wins, Losses, Draws, Winning streak, Winning percentage. For this we may have to use the header file '#include '.

One of the biggest mistakes that we made was properly designing the structure of the program, often two programs with the same output are totally different. The more efficient way, and one that we shall use in the future, is to design a structure that makes the program, concise, readable, easily modified, and to make good use of functions and pointers. For our design structure we believe that we were using the top-down programming method, as you can clearly see from our lab books we started with the need for a board and then worked out we needed a place to store a move etc.

Problems and possible solutions

Below are some of the major and minor problems that we faced during brain storming sessions of week one, right through to the full implementation of the whole program.

* One of the earliest problems came when we had to decide on all the rules and regulations of the noughts and crosses game. Although this is a simple game we believe that our minds were distracted by the fact that we would have to at some point put this into a 'C' program. If we had thought about it logically and without thinking of program at this stage we would have been able to achieve our aim.

* Another one of our problems was the use of functions at the early stages, (see pages 18 and 19 of Mark Smith's lab book.) We used the function recall (int check_WinX and int check_WinO), and made that equal to a local variable in the main program WIN. When the check wins were used, if player X had won a local variable inside the check_WinX function called 'z' would be made equal to 1, we then returned the value 'z = =1' back to the main program which would be stored in the variable WIN. This however did not work, so instead we decided on the use of global variables, whose contents would hold whether or not there was a win (see page 14 of Mark Smith's lab book.)

* A minor problem that we came across was the fact that the program did not print out the final board after a draw or if anyone won, although it would print out if there were a win (see below)

It shows that the player has entered the correct co-ordinates for a win but it has not printed an updated board.

The way around this was simply to place another display_board () function before the 'printf ("Player X wins\n");' statement in the check_winX function. Display_board functions where also placed in the check_draw and the check_winO functions. You can clearly see this on the final version of the program in Appendix .

* Another one of the minor problems was that the program didn't print out the name of the person who was playing when; it got to their turn, and when they had won. The solution to this was to use the %s command and by using the address of either player1 or player2. As mentioned above the #include header file is important so that we will be able to store and display their whole name by using the concatenation command.

* One of the major problems was the 'while loop' and the 'if statement' contained within the loop. This has been mentioned before in the lab books but it did take up majority of our time trying to solve this one problem. The problem in a nutshell was that when Player X had won the while loop went on to ask Player O for the move. (See below for picture and Mark Smith's lab book for a more detailed explanation.)

From the insert A you can see that Player X has clearly won, but the program still asked Player O to have ago. The consequences are explained on pages 14-17 in Mark's lab book.

* Another one of our major problems is if the user enters the wrong co-ordinates e.g. Row 3, Column 4, or if the co-ordinates are already in use we wanted the computer to print out "Please enter a valid move." This does happen, however it then misses their turn completely and asks the next person to have a go (see pic C).

Picture C shows Player O entering the wrong co-ordinates, and printing out "Please enter a valid move!" statement, which then gives Player X the opportunity to make three in a row and win the game. We have not been able to rectify the problem as yet but will have made an adjustment in the rules to clearly state that "Please only enter the numbers 0,1, and 2 for the rows and columns, and please ensure that you do not enter the co-ordinates of a square that have already been used. This will result in the forfeit of your go."

***Below is the full program in 9-point font***

#include

display_board ();

int WINX=0;

int WINY=0;

int DRAW=0;

char playerone [50]; /*An array used to store player one`s name*/

char playertwo [50];

int check_draw ();

int check_winX ();

int check_winO ();

char playagain;

char grid [3] [3] = { ' ',' ',' ',' ',' ',' ',' ',' ',' ',};

int main (void)

{

int row;

int column;

int x;

int play;

printf("\n\t\t************************\n");

printf ("\t\t Noughts and Crosses\n");

printf ("\t\t By Mark Smith and\n");

printf ("\t\t Trevor Snaith\n");

printf("\t\t************************\n");

printf ("\nPlease enter an option (1 or 2)\n");

printf ("\t\t 1)Play the game\n");

printf ("\t\t 2)Quit!!\n");

scanf ("\n%d",&play);

if (play==1)

{

printf ("\n\n RULES\n\n");

printf ("1. Each player takes aulturnate turns placing either\n");

printf (" an X or a O untill there is a win.\n");

printf ("2. A win is when three X's or three O's are placed next\n");

printf (" in a streight line. The line can be either horizontal \n");

printf (" vertical or diagonal. \n");

scanf ("\n%s",playerone);

scanf ("\n%s",playertwo);

printf ("\n%s will go first",playerone);

printf ("\n\n");

}

while ((WINX==0) && (WINY==0) && (DRAW==0) && (play==1))

{

display_board ();

printf("\n\n\n\n\n");

printf ("\n %s please enter the row number and press Enter\n",playerone);

scanf ("%d", &row);

printf ("\n %s please enter the column number and press Enter\n",playerone);

scanf ("%d", &column);

if

(grid [row] [column] ==' ')

(grid [row] [column] = 'X');

else

printf ("Please enter a valid move!\n");

check_winX ();

check_draw ();

if ( (DRAW==0)&&(WINX==0) ) /*By placing the IF statement here it solved the problem

of if X had won, or there was a draw, O still had to have another go*/

{

printf("\n\n\n\n\n\n\n\n\n\n\n\n\n\n");

display_board();

printf("\n\n\n\n\n");

printf ("\n %s please enter the row number and press Enter\n",playertwo);

scanf ("%d", &row);

printf ("\n %s please enter the column number and press Enter\n",playertwo);

scanf ("%d", &column);

if

(grid [row] [column] ==' ')

(grid [row] [column] = 'O');

else

printf ("Please enter a valid move!\n");

check_winO ();

if

(WINY=='0')

check_draw ();

else

printf("\n\n\n\n\n\n\n\n\n\n\n\n");

}

}

return 0;

}

display_board ()

{

int row;

int column;

printf (" \t\t\t COLUMNS\n\n");

printf ("\t\t\t [0] [1] [2]\n");

printf ("\n\t\t\t [0] %c | %c | %c \n", grid [0] [0], grid [0] [1], grid [0] [2] );

printf ("\t\t\tR -----|-----|-----\n");

printf ("\t\t\tO [1] %c | %c | %c \n", grid [1] [0], grid [1] [1], grid [1] [2] );

printf ("\t\t\tW -----|-----|-----\n");

printf ("\t\t\t [2] %c | %c | %c \n", grid [2] [0], grid [2] [1], grid [2] [2] );

return 0;

}

int check_winX ()

{

if((grid[0][0]=='X' && grid[0][1]=='X' && grid[0][2]=='X') ||

(grid[1][0]=='X' && grid[1][1]=='X' && grid[1][2]=='X') ||

(grid[2][0]=='X' && grid[2][1]=='X' && grid[2][2]=='X') ||

(grid[0][0]=='X' && grid[1][0]=='X' && grid[2][0]=='X') ||

(grid[0][1]=='X' && grid[1][1]=='X' && grid[2][1]=='X') ||

(grid[0][2]=='X' && grid[1][2]=='X' && grid[2][2]=='X') ||

(grid[0][0]=='X' && grid[1][1]=='X' && grid[2][2]=='X') ||

(grid[2][0]=='X' && grid[2][1]=='X' && grid[2][2]=='X') ||

(grid[2][0]=='X' && grid[1][1]=='X' && grid[0][2]=='X'))

WINX=1;

else

WINX=0;

{

if (WINX==1 )

{

printf("\n\n\n\n\n");

display_board();

printf ("\n Player X wins. %s is far superior than %s\n",playerone,playertwo);

}

}

return 0;

}

int check_winO ()

{

if((grid[0][0]=='O' && grid[0][1]=='O' && grid[0][2]=='O') ||

(grid[1][0]=='O' && grid[1][1]=='O' && grid[1][2]=='O') ||

(grid[2][0]=='O' && grid[2][1]=='O' && grid[2][2]=='O') ||

(grid[0][0]=='O' && grid[1][0]=='O' && grid[2][0]=='O') ||

(grid[0][1]=='O' && grid[1][1]=='O' && grid[2][1]=='O') ||

(grid[0][2]=='O' && grid[1][2]=='O' && grid[2][2]=='O') ||

(grid[0][0]=='O' && grid[1][1]=='O' && grid[2][2]=='O') ||

(grid[2][0]=='O' && grid[2][1]=='O' && grid[2][2]=='O') ||

(grid[2][0]=='O' && grid[1][1]=='O' && grid[0][2]=='O'))

WINY=1;

else

WINY=0;

{

if (WINY==1 )

{

printf("\n\n\n\n\n");

display_board();

printf ("\n Player O wins. %s is far superior than %s\n",playertwo,playerone);

}

}

return 0;

}

int check_draw ()

{

if((grid[0][0]==('X')|| grid [0][0]==('O')) && (grid[0][1]==('X') || grid [0][1]==('O'))

&& (grid[0][2]==('X') || grid [0][2]==('O')) &&(grid[1][0]==('X')|| grid [1][0]==('O'))

&& (grid[1][1]==('X') || grid [1][1]==('O')) && (grid[1][2]==('X') || grid [1][2]==('O'))

&& (grid[2][0]==('X')|| grid [2][0]==('O')) && (grid[2][1]==('X') || grid [2][1]==('O'))

&& (grid[2][2]==('X') || grid [2][2]==('O')))

DRAW=1;

else

DRAW=0;

{

if (DRAW==1)

{

printf ("\n\n\n\n\n");

display_board ();

printf ("\nSorry the game is a draw.");

}

}

return 0;

}