AI Tic Tac Toe

I need help creating my AI for TicTacToe.
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
int aimove(char board[3][3])
{
	srand(time(NULL));
	int randX,randY,X,Y;
	if (board[1][1]<79)		//If center square is not taken, take it
	{
		board[1][1]=79;
		return 0;
	}
	if (board[1][1]==88)		//If center square is taken, move somewhere else
		{
			randX=rand()%2*2;
			randY=rand()%2*2;
			board[randX][randY]=79;
			return 0;
		}
	for (Y=0;Y<3;Y++)		//Looks for Horizontal block
		for (X=0;X<3;X++)
		{
			if ((board[X][Y])+(board[X+1][Y]=176)) board[X+2][Y]=79;
			return 1;
		}
	for (Y=0;Y<3;Y++)		//Looks for Vertical block
		for (X=0;X<3;X++)
		{
			if ((board[X][Y])+(board[X][Y+1]=176)) board[X][Y+2]=79;
			return 1;
		}
	do
	{
		randX=rand()%3;
		randY=rand()%3;
		//cout<<"("<<randX<<","<<randY<<")\n";
	}while (!(board[randX][randY]<79));
		board[randX][randY]=79;
			/*if ((board[X][Y]!=88)&&(board[X][Y]!=79))
			{
				randX=rand()%2;
				randY=rand()%2;
				board[randX][randY]=79;	
			}*/
	return 0;
}
Last edited on
Could you be more specific about what assistance you are looking for?
@squished18,
I need the AI to be smart. Below is my whole code. Right now the AI takes the center square if its not already taken. Also the AI only goes to the corners after the first player move. I want the AI to make the player to think on where he/she should go next. Also the program puts a ? where you did or where you did not go.
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
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
# include <iostream>
# include <cstdlib>
# include <ctime>
using namespace std;
///////////////////////////////////////////////////////////////////////
int printboard(char board[3][3])
{
	int X,Y;
	char cyan[] = { 0x1b, '[', '0', ';', '3', '6', 'm', 0 };
	char normal[] = { 0x1b, '[', '0', ';', '3', '9', 'm', 0 };
	system("clear");
	for (Y=0;Y<3;Y++)
	{
		for (X=0;X<3;X++)
		{
			cout<<" "<<board[X][Y];
			if (X<2) cout<<cyan<<"║"<<normal;
		}
		cout<<"\n";
		if (Y<2) cout<<cyan<<"══╬══╬══\n"<<normal;
	}
	return 0;
}
///////////////////////////////////////////////////////////////////////
int initboard(char board[3][3])
{
	int X,Y;
	for (Y=0;Y<3;Y++)
		for (X=0;X<3;X++)
			board[X][Y]=X+1+Y*3+48;
	return 0;
}
///////////////////////////////////////////////////////////////////////
int playermove(char board[3][3])
{
	int X,Y,Z,VALID;
	do
	{
		cout<<"Where do You want to place Your X? ";
		cin>>Z;
		VALID=1;
		for (Y=0;Y<3;Y++)
			for (X=0;X<3;X++)
				if(board[X][Y]==(Z+48))
				{
					board[X][Y]=88;
					VALID=1;
				}
	}while (!VALID);
	return 0;
}
///////////////////////////////////////////////////////////////////////
int checkforwin(char board[3][3])
{
	int X,Y,CAT=-1;
	for (Y=0;Y<3;Y++)	//Did X win vertically
		if ((board[0][Y]+board[1][Y]+board[2][Y])==264) return 1;
	for (X=0;X<3;X++)	//Did X win Horizontally
		if ((board[X][0]+board[X][1]+board[X][2])==264) return 1;
		if ((board[0][0]+board[1][1]+board[2][2])==264) return 1;	//Did X win Diagonal1
		if ((board[2][0]+board[1][1]+board[0][2])==264) return 1;	//Did X win Diagonal2

	for (Y=0;Y<3;Y++)	//Did O win vertically
		if ((board[0][Y]+board[1][Y]+board[2][Y])==237) return 2;
	for (X=0;X<3;X++)	//Did O win Horizontally
		if ((board[X][0]+board[X][1]+board[X][2])==237) return 2;
		if ((board[0][0]+board[1][1]+board[2][2])==237) return 2;	//Did O win Diagonal1
		if ((board[2][0]+board[1][1]+board[0][2])==237) return 2;	//Did O win Diagonal2
		
	for (Y=0;Y<3;Y++)	//If board is full
		for (X=0;X<3;X++)
			if ((board[X][Y]!=88)&&(board[X][Y]!=79))	CAT=0;
	return CAT;
}
///////////////////////////////////////////////////////////////////////
int aimove(char board[3][3])
{
	srand(time(NULL));
	int randX,randY,X,Y;
	if (board[1][1]<79)		//If center square is not taken, take it
	{
		board[1][1]=79;
		return 0;
	}
	if (board[1][1]==88)		//If center square is taken, move somewhere else
		{
			randX=rand()%2*2;
			randY=rand()%2*2;
			board[randX][randY]=79;
			return 0;
		}
	for (Y=0;Y<3;Y++)		//Looks for Horizontal block
		for (X=0;X<3;X++)
		{
			if ((board[X][Y])+(board[X+1][Y]=176)) board[X+2][Y]=79;
			return 1;
		}
	for (Y=0;Y<3;Y++)		//Looks for Vertical block
		for (X=0;X<3;X++)
		{
			if ((board[X][Y])+(board[X][Y+1]=176)) board[X][Y+2]=79;
			return 1;
		}
	do
	{
		randX=rand()%3;
		randY=rand()%3;
		//cout<<"("<<randX<<","<<randY<<")\n";
	}while (!(board[randX][randY]<79));
		board[randX][randY]=79;
			/*if ((board[X][Y]!=88)&&(board[X][Y]!=79))
			{
				randX=rand()%2;
				randY=rand()%2;
				board[randX][randY]=79;	
			}*/
	return 0;
}
///////////////////////////////////////////////////////////////////////
int main()
{
	char board[3][3];			//Declare Variables
	//system("espeak -s 130 'Herro I am TicTacToe'");
	initboard(board);
	do
	{
		printboard(board);
		playermove(board);
		printboard(board);
		if (!checkforwin(board)) aimove(board);
	}while (!checkforwin(board));
	if (checkforwin(board)==1) cout<<"Player Wins";
	//if (checkforwin(board)==1) system("espeak -p 30 'Player Wins'");
	if (checkforwin(board)==2) cout<<"AI Wins";
	//if (checkforwin(board)==2) system("espeak -p 60 'AI Wins'");
	if (checkforwin(board)==-1) cout<<"CAT - It's a tie";
	//if (checkforwin(board)==-1) system("espeak -p 70 'MEOW!'");
	return 0;
}
Of all the functionality that you described in your last post, what is your code not doing at this time?
your ai is flawed. taking the center isnt always ideal.

on top of that, all properly played games are drawn, but if the human is not too smart, you should look for doubles (some place where you can win in either of 2 squares, the opponent can only block 1 of them) and look to prevent those same ideas from the human (this is why the center isnt always ideal).




CNoob2,

For availability, instead of comparing to 88 ('X') or 79 ('O'), perhaps instead compare to a default character and initialize your board to this default character. User input could then be changed to ask for both X row (0-2) and Y column (0-2). By the way, try to abstract the game characters to not always depend on 79 ('O'), etc. Maybe two const chars or other ways.

I see for display and user input, you're instead using some math to generate numbers 49 through 57 and then convert them to characters '1' through '9'. This makes the board not have a default character, but perhaps this is overthinking it a bit (comparison operators to int representations of characters make the intent a little vague at first glance). Less mathematical operations on the characters would make it all more readable and maintainable.

lines 85-91 it looks like AI tries to take a square whether it's available or not (pure RNG). You should instead search through the board and generate a list of pairs for open spots. Only then RNG on that if you want him to make a random yet available move. Once you have random move completely working and not accidentally grabbing unavail squares, you can look for smarter approach.

TicTacToe doesn't have too many possibilities, so you could do it as follows:
1. Search for immediate wins -- two of AI character in a row. Play in the first one found; return.
2. Search for immediate "about to lose" states -- two of Human character in a row. Play to prevent the first one found; return.
3. Now it gets trickier. An open middle on the AI's first move is safe, for sure, especially if the Human began the game. The AI's second move, if not obvious (#1 and #2), can be life or death. Suppose game went like this, Human 'X', AI 'O':

- - -
- - -
- - -

- - -
- - X
- - -

- - -
- O X
- - -

- - -
- O X
- X -


Now if the AI makes any of the moves [top-left, top-middle, middle-left] , he'll lose because he's not forcing human away from his plan of bottom-right.
4. Perhaps search for a forcing move (threatening a win) such that upon the forced human response, the human will not have two win threats. Return.
5. As last resort, lowest priority, make a random move from available moves.

gl.
Last edited on
icy1,
Thank you so much for the input, I will try to improve on my code from this.
Topic archived. No new replies allowed.