Tic-Tac-Toe Engine Bug.

Trying to make a Tic-Tac-Toe engine with smart computer
Most things are explained in the code.
2 Player mode works just fine, 1 player mode against computer that plays randomly is fine too.
But after implementing code that helps the computer find a square that will cause it to lose and blocking it, problems started to appear.
1. The first time it encounters danger, it blocks just fine, but if it happens a second time, it does nothing.
2. If X has 2 squares in a row, but an O is blocking it's path, the computer will nevertheless try to put a O there.
(The code is too long, splitting it into 3 parts)
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
  Trying to make a Tic-Tac-Toe engine with board.
2 player mode works just fine, and 1 player mode with computer choosing randomly also works well.
But now I'm trying to make so that if there's danger of the user winning, the computer will block.
The first time there's danger, it will block, but if it happens a second time, it does n]othing.
Also if there's danger of user winning but the computer already wrote in that square, it will still try to write in that square, which results in nothing happening.
It's probably because of some stupid error, but I can't find it.
[code
#include <iostream>
#include <string>
#include <stdio.h>
#include <ctime>
#include <stdlib.h> 
using namespace std;

int main(){
//square: The square that the user picks.
//win: When this turns into 1, the game ends.
//change: Determining if it's X's turn or O's turn. (0 = X, 1 = Y)
/* X[10], Y[10]: 9 arrays for remembering which squares are already filled in.
Put in 11 just in case, X[1] - First square taken by X etc.*/
//tie: Determining if all squares are filled in, if it is, print DRAW!!.
//random: Computer's random number.
    int square, win, change, X[10], O[10], tie[10], random;
    srand (time(0));
    random = rand() % 9 + 1;
    X[5] = 0;
    O[5] = 0;
//board: The Tic-Tac-Toe board template. Also changes for each square that's filled in.
//temp, temp2: temp first copies board, then cuts out what it needs depending on which square it's filling, then it adds in "XXX" or "OOO" depending on whose turn it is. temp2 then copies board and erases the part that has been changed. Finally, board turns into temp + temp2.
    string board, temp, temp2;
    board = "    |   |   \n-------------\n    |   |    \n-------------\n    |   |    ";
    cout << board << endl;
//Checking if X has 3 X's in a row.
    while(win != 1){
        if((X[1] == 1 && X[4] == 1 && X[7] == 1 )|| (X[2] == 1 && X[5] == 1 && X[8] == 1 )|| (X[3] == 1 && X[6] == 1 && X[9] == 1 )|| (X[1] == 1 && X[2] == 1 && X[3] == 1 )|| (X[4] == 1 && X[5] == 1 && X[6] == 1 )|| (X[7] == 1 && X[8] == 1 && X[9] == 1 )|| (X[1] == 1 && X[5] == 1 && X[9] == 1) || (X[3] == 1 && X[5] == 1 && X[7] == 1)){
            cout << "\nX WINS!!!" << endl;
            return 0;
        }
//Checking if O has 3 O's in a row.
        else if((O[1] == 1 && O[4] == 1 && O[7] == 1) || (O[2] == 1 && O[5] == 1 && O[8] == 1) || (O[3] == 1 && O[6] == 1 && O[9] == 1 )|| (O[1] == 1 && O[2] == 1 && O[3] == 1 )|| (O[4] == 1 && O[5] == 1 && O[6] == 1 )|| (O[7] == 1 && O[8] == 1 && O[9] == 1) || (O[1] == 1 && O[5] == 1 && O[9] == 1 )|| (O[3] == 1 && O[5] == 1 && O[7] == 1)){
            cout << "\nO WINS!!!" << endl;
            return 0;
        }
//If all squares are filled, and no one has a row, print DRAW!!
        else if(tie[1] == 1 && tie[2] == 1 && tie[3] == 1 && tie[4] == 1 && tie[5] == 1 && tie[6] == 1 && tie[7] == 1 && tie[8] == 1 && tie[9] == 1){
            cout << "\nDRAW!!!" << endl;
            return 0;
        }
/*The user enters a number between 1 ~ 9. If the square it represents:
 1: Top left square
 2: Top middle square
 3: Top right square
 etc.
        is taken, then tell the user so. It also checks for numbers that are not within that range.*/
        if(change == 0){
        cout << "Which square?" << endl;
        cin >> square;
        if(square == 1){
            if(X[1] == 1 || O[1] == 1){
                cout << "Square Already Taken!\n" << endl;
                continue;
            }
        }
        else if(square == 2){
            if(X[2] == 1 || O[2] == 1){
                cout << "Square Already Taken!\n" << endl;
                continue;
            }
        }
        else if(square == 3){
            if(X[3] == 1 || O[3] == 1){
                cout << "Square Already Taken!\n" << endl;
                continue;
            }
        }
        else if(square == 4){
            if(X[4] == 1 || O[4] == 1){
                cout << "Square Already Taken!\n" << endl;
                continue;
            }
        }
        else if(square == 5){
            if(X[5] == 1 || O[5] == 1){
                cout << "Square Already Taken!\n" << endl;
                continue;
            }
        }
        else if(square == 6){
            if(X[6] == 1 || O[6] == 1){
                cout << "Square Already Taken!\n" << endl;
                continue;
            }
        }
        else if(square == 7){
            if(X[7] == 1 || O[7] == 1){
                cout << "Square Already Taken!\n" << endl;
                continue;
            }
        }
        else if(square == 8){
            if(X[8] == 1 || O[8] == 1){
                cout << "Square Already Taken!\n" << endl;
                continue;
            }
        }
        else if(square == 9){
            if(X[9] == 1 || O[9] == 1){
                cout << "Square Already Taken!\n" << endl;
                continue;
            }
        }
        if(square < 1 || square > 9){
            cout << "Invalid Number!" << endl;
            continue;
        }
Last edited on
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
//change == 0: X's turn
//change = 1: Change to O's turn
//X[1] = 1: Square 1 is taken by X.
        if(change == 0){
            switch(square){
                case 1:
                    temp = board;
                    temp = board.substr (0,1);
                    temp += "XXX";
                    temp2 = board.erase (0,4);
                    board = temp + temp2;
                    cout << board << endl;
                    change = 1;
                    X[1] = 1;
                    tie[1] = 1;
                    break;
                case 2:
                    temp = board;
                    temp = board.substr (0,5);
                    temp += "XXX";
                    temp2 = board.erase (0,8);
                    board = temp + temp2;
                    cout << board << endl;
                    change = 1;
                    X[2] = 1;
                    tie[2] = 1;
                    break;
                case 3:
                    temp = board;
                    temp = board.substr (0,9);
                    temp += "XXX";
                    temp2 = board.erase (0,12);
                    board = temp + temp2;
                    cout << board << endl;
                    change = 1;
                    X[3] = 1;
                    tie[3] = 1;
                    break;
                case 4:
                    temp = board;
                    temp = board.substr (0,28);
                    temp += "XXX";
                    temp2 = board.erase (0,31);
                    board = temp + temp2;
                    cout << board << endl;
                    change = 1;
                    X[4] = 1;
                    tie[4] = 1;
                    break;
                case 5:
                    temp = board;
                    temp = board.substr (0,32);
                    temp += "XXX";
                    temp2 = board.erase (0,35);
                    board = temp + temp2;
                    cout << board << endl;
                    change = 1;
                    X[5] = 1;
                    tie[5] = 1;
                    break;
                case 6:
                    temp = board;
                    temp = board.substr (0,36);
                    temp += "XXX";
                    temp2 = board.erase (0,39);
                    board = temp + temp2;
                    cout << board << endl;
                    change = 1;
                    X[6] = 1;
                    tie[6] = 1;
                    break;
                case 7:
                    temp = board;
                    temp = board.substr (0,56);
                    temp += "XXX";
                    temp2 = board.erase (0,59);
                    board = temp + temp2;
                    cout << board << endl;
                    change = 1;
                    X[7] = 1;
                    tie[7] = 1;
                    break;
                case 8:
                    temp = board;
                    temp = board.substr (0,60);
                    temp += "XXX";
                    temp2 = board.erase (0,63);
                    board = temp + temp2;
                    cout << board << endl;
                    change = 1;
                    X[8] = 1;
                    tie[8] = 1;
                    break;
                case 9:
                    temp = board;
                    temp = board.substr (0,64);
                    temp += "XXX";
                    temp2 = board.erase (0,67);
                    board = temp + temp2;
                    cout << board << endl;
                    change = 1;
                    X[9] = 1;
                    X[9] = 1;
                    tie[9] = 1;
                    break;
            }
        }
    }
Last edited on
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
140
141
142
143
144
145
146
147
148
149
150
151
152
153
/* Problem here. If it's O's turn, the computer should pick a random square that has not been filled in yet.
   After that, it will check if X has any move that will let it win. If there is, block it. 
    First problem: The first time X gets a win in 1 move, O will block it automatically, but the second time, it will do nothing.
    Second problem: If X has a win in 1 move but O has already filled in the winning square, it will nevertheless put write in there, regardless of the while loop.
 */
        else if(change == 1){
        	while(X[random] == 1 || O[random] == 1){
                random = rand() % 9 + 1;
            }
            if((X[2] == 1 && X[3] == 1) || (X[4] == 1 && X[7] == 1) || (X[5] == 1 && X[9] == 1)){
        		random = 1;
        	}
        	else if((X[1] == 1 && X[3] == 1) || (X[5] == 1 && X[8] == 1)){
        		random = 2;
        	}
        	else if((X[1] == 1 && X[2] == 1) || (X[6] == 1 && X[9] == 1) || (X[5] == 1 && X[7] == 1)){
        		random = 3;
        	}
        	else if((X[1] == 1 && X[7] == 1) || (X[5] == 1 && X[6] == 1)){
        		random = 4;
        	}
        	else if((X[2] == 1 && X[8] == 1) || (X[4] == 1 && X[6] == 1) || (X[1] == 1 && X[9] == 1) || (X[3] == 1 && X[7] == 1)){
        		random = 5;
        	}
        	else if((X[3] == 1 && X[9] == 1) || (X[4] == 1 && X[5] == 1)){
        		random = 6;
        	}
        	else if((X[1] == 1 && X[4] == 1) || (X[8] == 1 && X[9] == 1) || (X[3] == 1 && X[5] == 1)){
        		random = 7;
        	}
        	else if((X[2] == 1 && X[5] == 1) || (X[7] == 1 && X[9] == 1)){
        		random = 8;
        	}
        	else if((X[1] == 1 && X[5] == 1) || (X[3] == 1 && X[6] == 1) || (X[7] == 1 && X[8] == 1)){
        		random = 9;
        	}
//change == 1: O's turn
//change = 0: Change to X's turn
//O[1] = 1: Square 1 is taken by O.
            switch(random){
                case 1:
                    cout << "Computer's Move" << endl;
                    temp = board;
                    temp = board.substr (0,1);
                    temp += "OOO";
                    temp2 = board.erase (0,4);
                    board = temp + temp2;
                    cout << board << endl;
                    change = 0;
                    O[1] = 1;
                    tie[1] = 1;
                    break;
                case 2:
                    cout << "Computer's Move" << endl;
                    temp = board;
                    temp = board.substr (0,5);
                    temp += "OOO";
                    temp2 = board.erase (0,8);
                    board = temp + temp2;
                    cout << board << endl;
                    change = 0;
                    O[2] = 1;
                    tie[2] = 1;
                    break;
                case 3:
                    cout << "Computer's Move" << endl;
                    temp = board;
                    temp = board.substr (0,9);
                    temp += "OOO";
                    temp2 = board.erase (0,12);
                    board = temp + temp2;
                    cout << board << endl;
                    change = 0;
                    O[3] = 1;
                    tie[3] = 1;
                    break;
                case 4:
                    cout << "Computer's Move" << endl;
                    temp = board;
                    temp = board.substr (0,28);
                    temp += "OOO";
                    temp2 = board.erase (0,31);
                    board = temp + temp2;
                    cout << board << endl;
                    change = 0;
                    O[4] = 1;
                    tie[4] = 1;
                    break;
                case 5:
                    cout << "Computer's Move" << endl;
                    temp = board;
                    temp = board.substr (0,32);
                    temp += "OOO";
                    temp2 = board.erase (0,35);
                    board = temp + temp2;
                    cout << board << endl;
                    change = 0;
                    O[5] = 1;
                    tie[5] = 1;
                    break;
                case 6:
                    cout << "Computer's Move" << endl;
                    temp = board;
                    temp = board.substr (0,36);
                    temp += "OOO";
                    temp2 = board.erase (0,39);
                    board = temp + temp2;
                    cout << board << endl;
                    change = 0;
                    O[6] = 1;
                    tie[6] = 1;
                    break;
                case 7:
                    cout << "Computer's Move" << endl;
                    temp = board;
                    temp = board.substr (0,56);
                    temp += "OOO";
                    temp2 = board.erase (0,59);
                    board = temp + temp2;
                    cout << board << endl;
                    change = 0;
                    O[7] = 1;
                    tie[7] = 1;
                    break;
                case 8:
                    cout << "Computer's Move" << endl;
                    temp = board;
                    temp = board.substr (0,60);
                    temp += "OOO";
                    temp2 = board.erase (0,63);
                    board = temp + temp2;
                    cout << board << endl;
                    change = 0;
                    O[8] = 1;
                    tie[8] = 1;
                    break;
                case 9:
                    cout << "Computer's Move" << endl;
                    temp = board;
                    temp = board.substr (0,64);
                    temp += "OOO";
                    temp2 = board.erase (0,67);
                    board = temp + temp2;
                    cout << board << endl;
                    change = 0;
                    O[9] = 1;
                    tie[9] = 1;
                    break;
            }
        }
    } 
return 0;
}
First post:
Line 20: initialize win=0 and change=0
Lines 58-115: You can simplify this to:
1
2
3
4
5
6
7
	    if (square < 1 || square > 9) {
		cout << "Invalid Number!" << endl;
		continue;
	    } else if (X[square]==1 || O[square] == 1) {
		cout << "Square Already Taken!\n" << endl;
		continue;
	    }


Second post:
Line 4 is not needed. change is always 0 here because it's inside the if at line 55 of the first post.

Third post:
Lines 7-9. The second time it's the computer's move, random will be whatever move value you had on the previous turn. The code works but it's fragile. I'd move line 26 of the first post down here.

Lines 10-36: you select a blocking square, but what if that square is already occupied? This is why it isn't selecting the right blocking move on the 2nd turn.

General:
- You only check for a winner after both the player and computer have had a move. What if the player wins after his move?
- Along the same lines, if the player takes the last square, then you'll still try to compute a move for the computer, which will probably result in an infinite loop as the computer tries to find an empty square. The outer loop should look like this:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
while (true) {
    display the board
    if (there is a winner) {
         say who won
         break;
    }
    if (board is full) {
         say it's a tie
         break;
    }
    if (choice == 0) {
          // get the player's move
    } else {
          // get the computer's move
    }
    choice = !choice;
}


Notice the long switch statements and all the code to update the board string? You're making a common mistake by combining the display of the date (your board string) with the representation of the data. You don't actually need the board string at all. Instead, write a function to display the board based on the contents of the X and O arrays.
Right, changed it to this:
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
  if(change == 1){
            random = rand() % 9 + 1;
        	while(X[random] == 1 || O[random] == 1){
                random = rand() % 9 + 1;
            }
            if((X[2] == 1 && X[3] == 1) || (X[4] == 1 && X[7] == 1) || (X[5] == 1 && X[9] == 1) && O[1] != 1){
        		random = 1;
        	}
        	else if((X[1] == 1 && X[3] == 1) || (X[5] == 1 && X[8] == 1) && O[2] != 1){
        		random = 2;
        	}
        	else if((X[1] == 1 && X[2] == 1) || (X[6] == 1 && X[9] == 1) || (X[5] == 1 && X[7] == 1) && O[3] != 1){
        		random = 3;
        	}
        	else if((X[1] == 1 && X[7] == 1) || (X[5] == 1 && X[6] == 1) && O[4] != 1){
        		random = 4;
        	}
        	else if((X[2] == 1 && X[8] == 1) || (X[4] == 1 && X[6] == 1) || (X[1] == 1 && X[9] == 1) || (X[3] == 1 && X[7] == 1) && O[5] != 1){
        		random = 5;
        	}
        	else if((X[3] == 1 && X[9] == 1) || (X[4] == 1 && X[5] == 1) && O[6] != 1){
        		random = 6;
        	}
        	else if((X[1] == 1 && X[4] == 1) || (X[8] == 1 && X[9] == 1) || (X[3] == 1 && X[5] == 1) && O[7] != 1){
        		random = 7;
        	}
        	else if((X[2] == 1 && X[5] == 1) || (X[7] == 1 && X[9] == 1) && O[8] != 1){
        		random = 8;
        	}
        	else if((X[1] == 1 && X[5] == 1) || (X[3] == 1 && X[6] == 1) || (X[7] == 1 && X[8] == 1) && O[9] != 1){
        		random = 9;
        	}


But it works sometimes, and sometimes does not.
It sometimes picks another square, and sometimes just picks the same square as usual.
C++ evaluates && before || so for example this:
if((X[2] == 1 && X[3] == 1) || (X[4] == 1 && X[7] == 1) || (X[5] == 1 && X[9] == 1) && O[1] != 1){
is evaluated like this:
1
2
3
 if((X[2] == 1 && X[3] == 1) ||
    (X[4] == 1 && X[7] == 1) ||
    (  (X[5] == 1 && X[9] == 1) && O[1] != 1)  ){

You need some parentheses like this:
1
2
3
4
 if((  (X[2] == 1 && X[3] == 1) ||
       (X[4] == 1 && X[7] == 1) ||
       (X[5] == 1 && X[9] == 1)) && 
    O[1] != 1){
Topic archived. No new replies allowed.