My Tic-tac-toe program

Hello folks !
This is my first post here and I'm sort of a beginner in C++. Learnt the basics in my junior year and am on my way to senior ! Here's a tic-tac-toe program I made with an AI and I believe it is impossible to defeat the AI though it is possible for the AI to win or draw the match. This is the first half of my Tic-tac-toe project which lets you do the first move. (The second part has the AI do the first move and is more "attacking in nature")

I would appreciate tips regarding shortening the program and the like. I have also added comments where necessary and would like to help out regarding any block of code.

#include <iostream>
using namespace std;
void player_move(char A[3][3])
{
int x;
cout << '\n' << "Enter the square";
cin >> x;
if(x == 1 && A[0][0] == '.')
A[0][0] = 'X';
else if(x == 2 && A[0][1] == '.')
A[0][1] = 'X';
else if(x == 3 && A[0][2] == '.')
A[0][2] = 'X';
else if(x == 4 && A[1][0] == '.')
A[1][0] = 'X';
else if(x == 5 && A[1][1] == '.')
A[1][1] = 'X';
else if(x == 6 && A[1][2] == '.')
A[1][2] = 'X';
else if(x == 7 && A[2][0] == '.')
A[2][0] = 'X';
else if(x == 8 && A[2][1] == '.')
A[2][1] = 'X';
else if(x == 9 && A[2][2] == '.')
A[2][2] = 'X';
}
int checkpw(char A[3][3])
{ int p = 2;
if(A[0][0] == 'X' && A[0][1] == 'X' && A[0][2] == 'X')
p=0;
else if(A[1][0] == 'X' && A[1][1] == 'X' && A[1][2] == 'X')
p=0;
else if(A[2][0] == 'X' && A[2][1] == 'X' && A[2][2] == 'X')
p=0;
else if(A[0][0] == 'X' && A[1][0] == 'X' && A[2][0] == 'X')
p=0;
else if(A[0][1] == 'X' && A[1][1] == 'X' && A[2][1] == 'X')
p=0;
else if(A[0][2] == 'X' && A[1][2] == 'X' && A[2][2] == 'X')
p=0;
else if(A[0][0] == 'X' && A[1][1] == 'X' && A[2][2] == 'X')
p=0;
else if(A[0][2] == 'X' && A[1][1] == 'X' && A[2][0] == 'X')
p=0;
return p;
}
int checkcw(char A[3][3])
{ int p=0;
// Horizontal 1
if(A[0][0] == 'O' && A[0][1] == 'O' && A[0][2] == '.')
{A[0][2] = 'O';p=1;}
else if(A[0][0] == 'O' && A[0][2] == 'O' && A[0][1] == '.')
{A[0][1] = 'O';p=1;}
else if(A[0][1] == 'O' && A[0][2] == 'O' && A[0][0] == '.')
{A[0][0] = 'O';p=1;}

// Horizontal 2
else if(A[1][0] == 'O' && A[1][1] == 'O' && A[1][2] == '.')
{A[1][2] = 'O';p=1;}
else if(A[1][0] == 'O' && A[1][2] == 'O' && A[1][1] == '.')
{A[1][1] = 'O';p=1;}
else if(A[1][1] == 'O' && A[1][2] == 'O' && A[1][0] == '.')
{A[1][0] = 'O';p=1;}

// Horizontal 3
else if(A[2][0] == 'O' && A[2][1] == 'O' && A[2][2] == '.')
{A[2][2] = 'O';p=1;}
else if(A[2][0] == 'O' && A[2][2] == 'O' && A[2][1] == '.')
{A[2][1] = 'O';p=1;}
else if(A[2][1] == 'O' && A[2][2] == 'O' && A[2][0] == '.')
{A[2][0] = 'O';p=1;}

// Vertical 1
else if(A[0][0] == 'O' && A[1][0] == 'O' && A[2][0] == '.')
{A[2][0] = 'O';p=1;}
else if(A[0][0] == 'O' && A[2][0] == 'O' && A[1][0] == '.')
{A[1][0] = 'O';p=1;}
else if(A[1][0] == 'O' && A[2][0] == 'O' && A[0][0] == '.')
{A[0][0] = 'O';p=1;}

// Vertical 2
else if(A[0][1] == 'O' && A[1][1] == 'O' && A[2][1] == '.')
{A[2][1] = 'O';p=1;}
else if(A[0][1] == 'O' && A[2][1] == 'O' && A[1][1] == '.')
{A[1][1] = 'O';p=1;}
else if(A[1][1] == 'O' && A[2][1] == 'O' && A[0][1] == '.')
{A[0][1] = 'O';p=1;}

// Vertical 3
else if(A[0][2] == 'O' && A[1][2] == 'O' && A[2][2] == '.')
{A[2][2] = 'O';p=1;}
else if(A[0][2] == 'O' && A[2][2] == 'O' && A[1][2] == '.')
{A[1][2] = 'O';p=1;}
else if(A[1][2] == 'O' && A[2][2] == 'O' && A[0][2] == '.')
{A[0][2] = 'O';p=1;}

// Diagonal Left-Right
else if(A[0][0] == 'O' && A[1][1] == 'O' && A[2][2] == '.')
{A[2][2] = 'O';p=1;}
else if(A[0][0] == 'O' && A[2][2] == 'O' && A[1][1] == '.')
{A[1][1] = 'O';p=1;}
else if(A[1][1] == 'O' && A[2][2] == 'O' && A[0][0] == '.')
{A[0][0] = 'O';p=1;}

// Diagonal Right-Left
else if(A[0][2] == 'O' && A[1][1] == 'O' && A[2][0] == '.')
{A[2][0] = 'O';p=1;}
else if(A[0][2] == 'O' && A[2][0] == 'O' && A[1][1] == '.')
{A[1][1] = 'O';p=1;}
else if(A[1][1] == 'O' && A[2][0] == 'O' && A[0][2] == '.')
{A[0][2] = 'O';p=1;}
return p;
}
int checkifpw(char A[3][3])
{ int p=0;
// Horizontal 1
if(A[0][0] == 'X' && A[0][1] == 'X' && A[0][2] == '.')
{A[0][2] = 'O';p=1;}
else if(A[0][0] == 'X' && A[0][2] == 'X' && A[0][1] == '.')
{A[0][1] = 'O';p=1;}
else if(A[0][1] == 'X' && A[0][2] == 'X' && A[0][0] == '.')
{A[0][0] = 'O';p=1;}

// Horizontal 2
else if(A[1][0] == 'X' && A[1][1] == 'X' && A[1][2] == '.')
{A[1][2] = 'O';p=1;}
else if(A[1][0] == 'X' && A[1][2] == 'X' && A[1][1] == '.')
{A[1][1] = 'O';p=1;}
else if(A[1][1] == 'X' && A[1][2] == 'X' && A[1][0] == '.')
{A[1][0] = 'O';p=1;}

// Horizontal 3
else if(A[2][0] == 'X' && A[2][1] == 'X' && A[2][2] == '.')
{A[2][2] = 'O';p=1;}
else if(A[2][0] == 'X' && A[2][2] == 'X' && A[2][1] == '.')
{A[2][1] = 'O';p=1;}
else if(A[2][1] == 'X' && A[2][2] == 'X' && A[2][0] == '.')
{A[2][0] = 'O';p=1;}

// Vertical 1
else if(A[0][0] == 'X' && A[1][0] == 'X' && A[2][0] == '.')
{A[2][0] = 'O';p=1;}
else if(A[0][0] == 'X' && A[2][0] == 'X' && A[1][0] == '.')
{A[1][0] = 'O';p=1;}
else if(A[1][0] == 'X' && A[2][0] == 'X' && A[0][0] == '.')
{A[0][0] = 'O';p=1;}

// Vertical 2
else if(A[0][1] == 'X' && A[1][1] == 'X' && A[2][1] == '.')
{A[2][1] = 'O';p=1;}
else if(A[0][1] == 'X' && A[2][1] == 'X' && A[1][1] == '.')
{A[1][1] = 'O';p=1;}
else if(A[1][1] == 'X' && A[2][1] == 'X' && A[0][1] == '.')
{A[0][1] = 'O';p=1;}

// Vertical 3
else if(A[0][2] == 'X' && A[1][2] == 'X' && A[2][2] == '.')
{A[2][2] = 'O';p=1;}
else if(A[0][2] == 'X' && A[2][2] == 'X' && A[1][2] == '.')
{A[1][2] = 'O';p=1;}
else if(A[1][2] == 'X' && A[2][2] == 'X' && A[0][2] == '.')
{A[0][2] = 'O';p=1;}

// Diagonal Left-Right
else if(A[0][0] == 'X' && A[1][1] == 'X' && A[2][2] == '.')
{A[2][2] = 'O';p=1;}
else if(A[0][0] == 'X' && A[2][2] == 'X' && A[1][1] == '.')
{A[1][1] = 'O';p=1;}
else if(A[1][1] == 'X' && A[2][2] == 'X' && A[0][0] == '.')
{A[0][0] = 'O';p=1;}

// Diagonal Right-Left
else if(A[0][2] == 'X' && A[1][1] == 'X' && A[2][0] == '.')
{A[2][0] = 'O';p=1;}
else if(A[0][2] == 'X' && A[2][0] == 'X' && A[1][1] == '.')
{A[1][1] = 'O';p=1;}
else if(A[1][1] == 'X' && A[2][0] == 'X' && A[0][2] == '.')
{A[0][2] = 'O';p=1;}
return p;
}
int trapcheck(char A[3][3])
{ int p = 0;
// Diagonal-check

if(A[0][0] == 'X' && A[1][1] == 'O' && A[2][2] == 'X' && A[2][1] == '.')
{A[2][1] = 'O';p=1;}
else if(A[0][0] == 'X' && A[1][1] == 'O' && A[2][2] == 'X' && A[0][1] == '.')
{A[0][1] = 'O';p=1;}
else if(A[0][2] == 'X' && A[1][1] == 'O' && A[2][0] == 'X' && A[2][1] == '.')
{A[2][1] = 'O';p=1;}
else if(A[0][0] == 'X' && A[1][1] == 'O' && A[2][2] == 'X' && A[0][1] == '.')
{A[0][1] = 'O';p=1;}

// L-check

else if(A[0][2] == 'X' && A[2][1] == 'X' && A[2][2] == '.')
{A[2][2] = 'O';p=1;}
else if(A[0][0] == 'X' && A[2][1] == 'X' && A[2][0] == '.')
{A[2][0] = 'O';p=1;}
else if(A[2][0] == 'X' && A[0][1] == 'X' && A[0][0] == '.')
{A[0][0] = 'O';p=1;}
else if(A[2][2] == 'X' && A[0][1] == 'X' && A[0][2] == '.')
{A[0][2] = 'O';p=1;}
else if(A[0][2] == 'X' && A[1][0] == 'X' && A[0][0] == '.')
{A[0][0] = 'O';p=1;}
else if(A[2][2] == 'X' && A[1][0] == 'X' && A[2][0] == '.')
{A[2][0] = 'O';p=1;}
else if(A[0][0] == 'X' && A[1][2] == 'X' && A[0][2] == '.')
{A[0][2] = 'O';p=1;}
else if(A[2][0] == 'X' && A[1][2] == 'X' && A[2][2] == '.')
{A[2][2] = 'O';p=1;}
return p;
}
void show_game(char A[3][3])
{
int i,j=0;
for(i=0;i<2;i++)
{
cout << A[i][j] << " | " << A[i][j+1] << " | " << A[i][j+2] << '\n';
cout << "--+---+--" << '\n';
}
cout << A[i][j] << " | " << A[i][j+1] << " | " << A[i][j+2] << '\n';
}
void main()
{
while(true)
{
int p,q,r;
char A[3][3] = {'.','.','.','.','.','.','.','.','.'};
show_game(A);
cout << endl << endl;
for(int i=0;i<5;i++)
{
player_move(A);
p=checkpw(A);
if(p==0)
break;
q=checkcw(A);
if(q==0)
r=checkifpw(A);
else
{
show_game(A);
cout << endl << endl;
break;
}
if(q==0 && r==0)
{
if(A[1][1]=='.')
A[1][1]='O';
else if(trapcheck(A) == 0)
{
if(A[0][0] == '.')
A[0][0] = 'O';
else if(A[0][2] == '.')
A[0][2] = 'O';
else if(A[2][0] == '.')
A[2][0] = 'O';
else if(A[2][2] == '.')
A[2][2] = 'O';
else if(A[0][1] == '.')
A[0][1] = 'O';
else if(A[1][0] == '.')
A[1][0] = 'O';
else if(A[1][2] == '.')
A[1][2] = 'O';
else if(A[2][1] == '.')
A[2][1] = 'O';
}
}
cout << endl << endl;
show_game(A);
}
if(q==1)
cout << " HAHAHA YOU LOSE ! \n \n";
else if (p==0)
cout << " OMG YOU MUST BE A GENIUS ! YOU WIN ! \n \n";
else
cout << "DRAW ! \n \n";
system("pause");
system("CLS");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#include <iostream>

//using namespace std;

void player_move( char A[3][3] )
{
    int x = 0 ;
    do
    {
        std::cout << "\nEnter the square: ";
        std::cin >> x ;
    } while( x < 1 || x > 9 ) ;

    int i = (x-1) / 3 ;
    int j = (x-1) % 3 ;
    if( A[i][j] == '.' ) A[i][j] = 'X' ;
}

bool check_win( const char A[3][3], char c )
{
    // rows
    for( int row = 0 ; row < 3 ; ++row )
        if( A[row][0] == c && A[row][1] == c && A[row][2] == c ) return true ;

    // cols
    for( int col = 0 ; col < 3 ; ++col )
        if( A[0][col] == c && A[1][col] == c && A[2][col] == c ) return true ;

    // diagonal left-right
    if( A[0][0] == c && A[1][1] == c && A[2][2] == c ) return true ;

    // diagonal right-left
    return A[0][2] == c && A[1][1] == c && A[2][0] == c ;
}

int checkpw( const char A[3][3] ) { return check_win( A, 'X' ) ? 0 : 2 ; }

int checkcw( const char A[3][3] ) { return check_win( A, 'O' ) ? 1 : 0 ; }

// ... 

Take it up from there.
Thanks :-) I'm not really familiar with Boolean functions, but will look into it now as it REALLY saves a lot of space :-) Thank you :)

A small problem with that is even after the computer gets a 3 in a row against me, it does not show that I lost. It takes another run so that the 3 in a row is proved.
In my method, it checks if it can win, puts an O in there and immediately changes the value of p to 1 (which is q in void main). The 'else' proves it false and it breaks out of the loop.
In your method, it will check if there's a 3 in a row first instead of checking the possibility and hence won't ALWAYS win. (Try entering squares in the order 1,2,9 and see what happens)

Besides that, I think it's an excellent way of doing this. So, thanks again.
Last edited on
> A small problem with that is even after the computer gets a 3 in a row against me,
> it does not show that I lost. It takes another run so that the 3 in a row is proved.

I had assumed that a check for a win is made after the player / computer makes a move.


> In my method, it checks if it can win, puts an O in there and immediately changes the value of p to 1

Equivalent logic would be:
Try placing an O in each available position, and then check for a win.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
#include <iostream>

//using namespace std;

void player_move( char A[3][3] )
{
    int x = 0 ;
    do
    {
        std::cout << "\nEnter the square: ";
        std::cin >> x ;
    } while( x < 1 || x > 9 ) ;

    int i = (x-1) / 3 ;
    int j = (x-1) % 3 ;
    if( A[i][j] == '.' ) A[i][j] = 'X' ;
}

bool check_win( const char A[3][3], char c )
{
    // rows
    for( int row = 0 ; row < 3 ; ++row )
        if( A[row][0] == c && A[row][1] == c && A[row][2] == c ) return true ;

    // cols
    for( int col = 0 ; col < 3 ; ++col )
        if( A[0][col] == c && A[1][col] == c && A[2][col] == c ) return true ;

    // diagonal left-right
    if( A[0][0] == c && A[1][1] == c && A[2][2] == c ) return true ;

    // diagonal right-left
    return A[0][2] == c && A[1][1] == c && A[2][0] == c ;
}

bool checkpw( const char A[3][3] ) { return check_win( A, 'X' ) ; }

bool checkcw( const char A[3][3] ) { return check_win( A, 'O' ) ; }

bool try_for_cw( char A[3][3] )
{
    for( int i = 0 ; i < 3 ; ++i )
        for( int j = 0 ; j < 3 ; ++j )
        {
            if( A[i][j] == '.' ) // for each available position:
            {
                A[i][j] = 'O' ; // try placing an O here
                if( checkcw(A) ) return true ;
                A[i][j] = '.' ; // no win, put it back to a .
            }
        }

    return false ;
}

// ... 
I shortened the code using another logic but has the for loop similar to yours. It uses a position algorithm to determine the win and I will post that here. I've also added a return function in every statement so that it saves execution time. Doesn't have to run through the entire thing once it finds a match.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
int checkpw(char A[3][3])
{ int p=2,i;
for(i=0;i<3;i++)
	if(A[i][0] == 'X' && A[i][1] == 'X' && A[i][2] == 'X')
	{p=0;return p;}
for(i=0;i<3;i++)
	if(A[0][i] == 'X' && A[1][i] == 'X' && A[2][i] == 'X')
		{p=0;return p;}
if(A[0][0] == 'X' && A[1][1] == 'X' && A[2][2] == 'X')
	{p=0;return p;}
if(A[0][2] == 'X' && A[1][1] == 'X' && A[2][0] == 'X')
	{p=0;return p;}
return p;
}
int checkcw(char A[3][3])
{ int p=0,i,j;
	// Horizontals
	for(i=0;i<3;i++)
		for(j=0;j<3;j++)
			if(A[i][j] == 'O' && A[i][(j+2)%3] == 'O' && A[i][3-(j+((j+2)%3))] == '.')
					{A[i][3-(j+((j+2)%3))] = 'O';p=1;return p;}
			
	// Verticals
	for(j=0;j<3;j++)
		for(i=0;i<3;i++)
			if(A[i][j] == 'O' && A[(i+2)%3][j] == 'O' && A[3-(i+((i+2)%3))][j] == '.')
				{A[3-(i+((i+2)%3))][j] = 'O';p=1;return p;}
	
	// Diagonal Left-Right
	for(i=0,j=0;i<3&&j<3;i++,j++)
		if(A[i][j] == 'O' && A[(i+2)%3][(j+2)%3] == 'O' && A[3-(i+((i+2)%3))][3-(j+((j+2)%3))] == '.')
		{A[3-(i+((i+2)%3))][3-(j+((j+2)%3))] = 'O';p=1;return p;}

	// Diagonal Right-Left
	for(i=0,j=2;i<3&&j>=0;i++,j--)
		if(A[i][j] == 'O' && A[(i+2)%3][(j+1)%3] == 'O' && A[3-(i+((i+2)%3))][3-(j+((j+1)%3))] == '.')
			{A[3-(i+((i+2)%3))][3-(j+((j+1)%3))] = 'O';p=1;return p;}
return p;
}


However with that additional function you provided, the program looks neat. Thanks !
Last edited on
Topic archived. No new replies allowed.