I 've made a tic-tac-toe game in C, I want some tips on how to improve it since the program is a little bit too long.
(500 lines with comments)
How I done the game:
Mapped a 3x3 Matrix like the board bellow:
/*
-------------------------
| 1 || 2 || 3 |
-------------------------
| 4 || 5 || 6 |
-------------------------
| 7 || 8 || 9 |
-------------------------
*/
With the 3x3 i did a switch with the position, as show in the code snippets bellow
I think that this solution is a little bit too long, so as the algorithms to calculate the win on a given column, row or diagonal.
Here I will show some snipets of the code:
/*
is_pos_free takes as arg1 a square matrix (board), state (from the enum on Main), and a position (from 1 trought 9)
return:
return 1 if position is free
return 0 if position isn't free
return -1 if position isn't within range of 1 until 9 (position < 1 or position > 9)
*/
int is_pos_free(int board[SIZE][SIZE],int state,int pos){
int cond;
switch(pos){
case 1: if(state == 0 && board[0][0] == 0 ){
board[0][0] = 1;
cond = 1;
break;
}
else if(state == 1 && board[0][0] == 0){
board[0][0] = 2;
break;
}
else{
cond = 0;
break;
}
case 2: if(state == 0 && board[0][1] == 0){
board[0][1] = 1;
cond = 1;
break;
}
else if(state == 1 && board[0][1] == 0){
board[0][1] = 2;
break;
}
else{
cond = 0;
break;
}
case 3: if(state == 0 && board[0][2] == 0){
board[0][2] = 1;
cond = 1;
break;
}
else if(state == 1 && board[0][2] == 0){
board[0][2] = 2;
break;
}
else{
cond = 0;
break;
}
case 4: if(state == 0 && board[1][0] == 0){
board[1][0] = 1;
cond = 1;
break;
}
else if(state == 1 && board[1][0] == 0){
board[1][0] = 2;
break;
}
else{
cond = 0;
break;
}
case 5: if(state == 0 && board[1][1] == 0){
board[1][1] = 1;
cond = 1;
break;
}
else if(state == 1 && board[1][1] == 0){
board[1][1] = 2;
break;
}
else{
cond = 0;
break;
}
case 6: if(state == 0 && board[1][2] == 0){
board[1][2] = 1;
cond = 1;
break;
}
else if(state == 1 && board[1][2] == 0){
board[1][2] = 2;
break;
}
else{
cond = 0;
break;
}
case 7: if(state == 0 && board[2][0] == 0){
board[2][0] = 1;
cond = 1;
break;
}
else if(state == 1 && board[2][0] == 0){
board[2][0] = 2;
break;
}
else{
cond = 0;
break;
}
case 8: if(state == 0 && board[2][1] == 0){
board[2][1] = 1;
cond = 1;
break;
}
else if(state == 1 && board[2][1] == 0){
board[2][1] = 2;
break;
}
else{
cond = 0;
break;
}
case 9: if(state == 0 && board[2][2] == 0){
board[2][2] = 1;
cond = 1;
break;
}
else if(state == 1 && board[2][2] == 0){
board[2][2] = 2;
break;
}
else{
cond = 0;
break;
}
default:
printf("%s",RED);
printf("Error 2\n");
printf("%s",YELLOW);
printf("Position <1 or Position > 9\nType a valid position\n");
cond = -1;
break;
}
return cond;
}
/*
calculate_win_line takes as arg1 a square matrix (board), and the size of the board
the function calculate if the player or the machine has won on a line
return:
returns 1 for 'O' won
returns 2 for 'X' won
default 0 -> No one won
*/
int calculate_win_line(int board[][SIZE],int size){
int j;
int k;
int win_count_O ;
int win_count_X ;
int who_won = 0;
for(j = 0;j<size;j++){
win_count_O = 0;
win_count_X = 0;
for(k = 0;k<size;k++){
if(board[j][k] == 1){
win_count_O++;
}
else if(board[j][k] == 2){
win_count_X++;
}
if(win_count_O == 3){
who_won = 1;
break;
}
else if(win_count_X == 3 ){
who_won = 2;
break;
}
}
}
return who_won;
}
/*
calculate_win_col takes as arg1 a square matrix (board), and the size of the board
the function calculate if the player or the machine has won on a column
return:
returns 1 for 'O' won
returns 2 for 'X' won
default 0 -> No one won
*/
int calculate_win_col(int board[][SIZE],int size){
int j;
int k;
int win_count_O = 0;
int win_count_X = 0;
int who_won = 0;
for(j = 0;j<size;j++){
win_count_O = 0;
win_count_X = 0;
for(k = 0;k<size;k++){
if(board[k][j] == 1){
win_count_O++;
}
else if(board[k][j] == 2){
win_count_X++;
}
if(win_count_O == 3){
who_won = 1;
break;
}
if(win_count_X == 3){
who_won = 2;
break;
}
}
}
return who_won;
}
/*
calculate_win_diagonal takes as arg1 a square matrix (board), and the size of the board
the function calculate if the player or the machine has won on a main diagonal or on a antidiagonal of the given matrix
return:
returns 1 for 'O' won on a main diagonal or antidiagonal
returns 2 for 'X' won on a main diagonal or antidiagonal
default 0 -> No one won
*/
int calculate_win_diagonal(int board[][SIZE],int size){
int j;
int k;
int win_count_X = 0;
int win_count_O = 0;
int who_won = 0;
for(j = 0;j<size;j++){
if(board[j][j] == 1){
win_count_O++;
}
if(board[j][j] == 2){
win_count_X++;
}
}
if(win_count_O == 3){
return who_won = 1;
}
else if(win_count_X == 3){
return who_won = 2;
}
else{
win_count_O = 0;
win_count_X = 0;
k = size - 1;
}
for(j = 0;j<size;j++){
if(board[j][k] == 1){
win_count_O++;
}
else if(board[j][k] == 2){
win_count_X++;
}
k--;
}
if(win_count_O == 3){
who_won = 1;
}
else if(win_count_X == 3){
who_won = 2;
}
return who_won;
}
Full code:
//HUMAN-> O
//MACHINE -> X
#include<stdio.h>
#include<time.h>
#include<string.h>
#include<stdlib.h>
#define SIZE 3
//Colors
#define RED "\033[1;31m"
#define PURPLE "\033[1;35m"
#define CYAN "\033[1;36m"
#define YELLOW "\033[1;33m"
#define RESET "\033[0m"
#define GREEN "\033[1;32m"
//end Colors definitions
// functions prototypes
void determine_turn(int * turn);
void verify_pos(long int * pos,char * buff,char * end);
void draw_pos(void);
void draw_line(void);
void draw_board(int board[][SIZE],int size);
void restart_board(int board[][SIZE],int size);
int is_pos_free(int board[SIZE][SIZE],int state,int pos);
int calculate_win_line(int board[][SIZE],int size);
int calculate_win_col(int board[][SIZE],int size);
int calculate_win_diagonal(int board[][SIZE],int size);
//end of function prototypes
int main(void){
srand(time(0));
enum GameState{O_PLAY,X_PLAY};
int move = 1;
char buff [255];
long int pos;
char * end;
int status = 0;
int state = O_PLAY;
int board [SIZE][SIZE] = {0};
int win_line;
int win_col;
int win_diagonal;
int cond = 1;
while(cond == 1){
while(move <= 9 ){
draw_pos();
switch (state) {
case O_PLAY:
printf("Turn of %sO%s(YOU)\n",YELLOW,RESET);
printf("Type the position you want to play based on the board bellow\n");
if(fgets(buff,sizeof buff,stdin) == NULL){
printf("%s",RED);
printf("\nFatal Error\n");
printf("%s",PURPLE);
printf("Error reading position\nExpected a number instead got position = NULL\nTerminating program\n");
exit(1);
}
buff[strcspn(buff,"\n")] = 0;
pos = strtol(buff,&end,10);
break;
case X_PLAY:
printf("Turn of %sX%s\n(MACHINE)",CYAN,RESET);
break;
}//end_switch(state)
status = is_pos_free(board, state, pos);
while(status == 0 && state == 0){
printf("%s",RED);
printf("Position %ld already used\n",pos);
printf("%s",RESET);
printf("Type a new position\n");
if(fgets(buff,sizeof buff,stdin) == NULL){
printf("%s",RED);
printf("\nFatal Error\n");
printf("%s",PURPLE);
printf("Error reading position\nExpected a number instead got position = NULL\nTerminating program\n");
exit(1);
}
buff[strcspn(buff,"\n")] = 0;
pos = strtol(buff,&end,10);
status = is_pos_free(board, state, pos);
}//end_while(status == 0 && state == 0)
while(status == 0 && state == 1){
pos = 1 + rand() % 9;
status = is_pos_free(board, state, pos);
}//end_while(status == 0 && state == 1)
printf("%s",RESET);
if(state == 0){
state = 1;
}
else{
state = 0;
}
draw_board(board,SIZE);
if(status){
move++;
}
win_line = calculate_win_line(board, SIZE);
win_col = calculate_win_col(board, SIZE);
win_diagonal = calculate_win_diagonal(board, SIZE);
if(win_line == 1 || win_col == 1 || win_diagonal == 1){
printf("%sO%s won\n",YELLOW,RESET);
move = 0;
break;
}
else if (win_line == 2 || win_col == 2 || win_diagonal == 2){
printf("%sX%s won\n",CYAN,RESET);
move = 0;
break;
}
}//end while(move<=9)
if(move == 10){
printf("Tie\n");
}
printf("Wanna play again? (Type 1 to continue playing or type any other key to exit the game)\n");
if(fgets(buff,sizeof buff,stdin) == NULL){
printf("%s",RED);
printf("\nFatal Error\n");
printf("%s",PURPLE);
printf("Error reading position\nExpected a number instead got position = NULL\nTerminating program\n");
exit(1);
}
buff[strcspn(buff,"\n")] = 0;
cond = strtol(buff,&end,10);
if(cond == 1){
move = 1;
win_line = win_col = win_diagonal = 0;
restart_board(board,SIZE);
}
else{
return 0;
}
}//end while(cond == 1)
}//end main()
/*
draw_line takes as argument void and draws a line on the screen
return:
The function returns nothing
*/
void draw_line(void){
int j;
printf("\n");
for(j = 1;j<= (SIZE * 16) + 1;j++){
printf("-");
}
printf("\n");
}
/*
draw_pos takes as arguments void and draws the positions of the board (from 1 trought 9)
return:
The function returns nothing
*/
void draw_pos(void){
int i;
int j;
int val = 1;
draw_line();
for(i = 0;i<SIZE;i++){
for(j = 0;j<SIZE;j++){
printf("|\t%d\t|",j + val);
}
draw_line();
val+=3;
}
}
/*
draw_board takes as arg1 a square matrix (board), and the size of the board, the function draws the board on the screen
attetion: Do not mistake with draw_pos, since one draws the position mapping and the other draw the board with O and X
return:
The function returns nothing
*/
void draw_board(int board[][SIZE],int size){
int j;
int k;
draw_line();
for( j = 0;j<size;j++){
for(k = 0;k<size;k++){
if(board[j][k] == 1){
printf("|\t%sO%s\t|",YELLOW,RESET);
}
else if(board[j][k] == 2){
printf("|\t%sX%s\t|",CYAN,RESET);
}
else{
printf("|\t \t|");
}
}
draw_line();
}
}
/*
is_pos_free takes as arg1 a square matrix (board), state (from the enum on Main), and a position (from 1 trought 9)
return:
return 1 if position is free
return 0 if position isn't free
return -1 if position isn't within range of 1 until 9 (position < 1 or position > 9)
*/
int is_pos_free(int board[SIZE][SIZE],int state,int pos){
int cond;
switch(pos){
case 1: if(state == 0 && board[0][0] == 0 ){
board[0][0] = 1;
cond = 1;
break;
}
else if(state == 1 && board[0][0] == 0){
board[0][0] = 2;
break;
}
else{
cond = 0;
break;
}
case 2: if(state == 0 && board[0][1] == 0){
board[0][1] = 1;
cond = 1;
break;
}
else if(state == 1 && board[0][1] == 0){
board[0][1] = 2;
break;
}
else{
cond = 0;
break;
}
case 3: if(state == 0 && board[0][2] == 0){
board[0][2] = 1;
cond = 1;
break;
}
else if(state == 1 && board[0][2] == 0){
board[0][2] = 2;
break;
}
else{
cond = 0;
break;
}
case 4: if(state == 0 && board[1][0] == 0){
board[1][0] = 1;
cond = 1;
break;
}
else if(state == 1 && board[1][0] == 0){
board[1][0] = 2;
break;
}
else{
cond = 0;
break;
}
case 5: if(state == 0 && board[1][1] == 0){
board[1][1] = 1;
cond = 1;
break;
}
else if(state == 1 && board[1][1] == 0){
board[1][1] = 2;
break;
}
else{
cond = 0;
break;
}
case 6: if(state == 0 && board[1][2] == 0){
board[1][2] = 1;
cond = 1;
break;
}
else if(state == 1 && board[1][2] == 0){
board[1][2] = 2;
break;
}
else{
cond = 0;
break;
}
case 7: if(state == 0 && board[2][0] == 0){
board[2][0] = 1;
cond = 1;
break;
}
else if(state == 1 && board[2][0] == 0){
board[2][0] = 2;
break;
}
else{
cond = 0;
break;
}
case 8: if(state == 0 && board[2][1] == 0){
board[2][1] = 1;
cond = 1;
break;
}
else if(state == 1 && board[2][1] == 0){
board[2][1] = 2;
break;
}
else{
cond = 0;
break;
}
case 9: if(state == 0 && board[2][2] == 0){
board[2][2] = 1;
cond = 1;
break;
}
else if(state == 1 && board[2][2] == 0){
board[2][2] = 2;
break;
}
else{
cond = 0;
break;
}
default:
printf("%s",RED);
printf("Error 2\n");
printf("%s",YELLOW);
printf("Position <1 or Position > 9\nType a valid position\n");
cond = -1;
break;
}
return cond;
}
/*
calculate_win_line takes as arg1 a square matrix (board), and the size of the board
the function calculate if the player or the machine has won on a line
return:
returns 1 for 'O' won
returns 2 for 'X' won
default 0 -> No one won
*/
int calculate_win_line(int board[][SIZE],int size){
int j;
int k;
int win_count_O ;
int win_count_X ;
int who_won = 0;
for(j = 0;j<size;j++){
win_count_O = 0;
win_count_X = 0;
for(k = 0;k<size;k++){
if(board[j][k] == 1){
win_count_O++;
}
else if(board[j][k] == 2){
win_count_X++;
}
if(win_count_O == 3){
who_won = 1;
break;
}
else if(win_count_X == 3 ){
who_won = 2;
break;
}
}
}
return who_won;
}
/*
calculate_win_col takes as arg1 a square matrix (board), and the size of the board
the function calculate if the player or the machine has won on a column
return:
returns 1 for 'O' won
returns 2 for 'X' won
default 0 -> No one won
*/
int calculate_win_col(int board[][SIZE],int size){
int j;
int k;
int win_count_O = 0;
int win_count_X = 0;
int who_won = 0;
for(j = 0;j<size;j++){
win_count_O = 0;
win_count_X = 0;
for(k = 0;k<size;k++){
if(board[k][j] == 1){
win_count_O++;
}
else if(board[k][j] == 2){
win_count_X++;
}
if(win_count_O == 3){
who_won = 1;
break;
}
if(win_count_X == 3){
who_won = 2;
break;
}
}
}
return who_won;
}
/*
calculate_win_diagonal takes as arg1 a square matrix (board), and the size of the board
the function calculate if the player or the machine has won on a main diagonal or on a antidiagonal of the given matrix
return:
returns 1 for 'O' won on a main diagonal or antidiagonal
returns 2 for 'X' won on a main diagonal or antidiagonal
default 0 -> No one won
*/
int calculate_win_diagonal(int board[][SIZE],int size){
int j;
int k;
int win_count_X = 0;
int win_count_O = 0;
int who_won = 0;
for(j = 0;j<size;j++){
if(board[j][j] == 1){
win_count_O++;
}
if(board[j][j] == 2){
win_count_X++;
}
}
if(win_count_O == 3){
return who_won = 1;
}
else if(win_count_X == 3){
return who_won = 2;
}
else{
win_count_O = 0;
win_count_X = 0;
k = size - 1;
}
for(j = 0;j<size;j++){
if(board[j][k] == 1){
win_count_O++;
}
else if(board[j][k] == 2){
win_count_X++;
}
k--;
}
if(win_count_O == 3){
who_won = 1;
}
else if(win_count_X == 3){
who_won = 2;
}
return who_won;
}
/*
the function restart_board takes as arg1 a square matrix (board) and the size of the matrix
the functions restarts the board to 1
return:
the function returns nothing
*/
void restart_board(int board[][SIZE],int size){
int j;
int k;
for(j = 0;j<size;j++){
for(k = 0;k<size;k++){
board[j][k] = 0;
}
}
}
I'd like sugestions on how to improve it.
intarray, a more human-readableenum { FREE, X, O }would be good for readability. You will have to explain whatstateandposare. \$\endgroup\$