Tic Tac Toe Bugs/Optimization

Hello, I am trying to produce a Tic Tac Toe problem that puts the user against the computer. This is the (probably inefficient) code that I have right now. There are two significant problems:

1. When I input field '7' it automatically says, "You win."
2. The Mersenne Twister generator that is supposed to generate a random number between 1 and 9 does not seem to work properly. It seems like the only number that it produces is the number 6. What is going on?

Please let me know what do vis-a-vis the above, and also if you have any optimization suggestions. Thank you in advance!

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
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
#include <iostream>
#include <limits>
#include <random>
#include <time.h>

using namespace std;

char grid[3][3] = {'1', '2', '3', '4', '5', '6', '7', '8', '9'};
char player = 'X';

//The following are prototypes of functions.

void Draw();

void PlayerInput();
void AIinput();
char HowToWin();
void CheckWin(int count);

int main()
{
    mt19937 mt_rand(time(0));

    int count = 0;
    cout << "Welcome to the Tic Tac Toe Game! Press enter to begin!" << endl; cout << endl;
    cin.get();

    Draw();
    while (count < 9)
    {
        ++count;
        PlayerInput();Draw();
        CheckWin(count);
        AIinput(); Draw();
        CheckWin(count);
    }
    if(count == 9)
    {
        cout << endl; cout << "Game over!" << endl;
    }
    system("Pause");
    return 0;
}


void Draw()
{
    system("CLS");                        // clears the screen

    for(int r = 0; r < 3 ; ++r)
    {
       cout << "-----------"<< endl;
       for(int c = 0; c < 3; ++c)
       {
           cout << grid[r][c] << " | ";
       }
       cout << endl;
    }
}
void GeneralInput(int n)
{
    if(n == 1)
    {
        if(grid[0][0] == '1')
        {
            grid[0][0] = player;
        }
        else
        {
            if(player = 'X')
            {
                cout << "This field has already been taken; please try again!" << endl;
                PlayerInput();
            }
            else if (player = 'O')
            {
                AIinput();
            }
        }
    }
    else if(n == 2)
    {
        if(grid[0][1] == '2')
        {
            grid[0][1] = player;
        }
        else
        {
            if(player = 'X')
            {
                cout << "This field has already been taken; please try again!" << endl;
                PlayerInput();
            }
            else if(player = 'O')
            {
                AIinput();
            }
        }
    }
    else if(n == 3)
    {
        if(grid[0][2] == '3')
        {
            grid[0][2] = player;
        }
        else
        {
            if(player = 'X')
            {
                cout << "This field has already been taken; please try again!" << endl;
                PlayerInput();
            }
            else if(player = 'O')
            {
                AIinput();
            }
        }
    }
    else if(n == 4)
    {
        if(grid[1][0] == '4')
        {
            grid[1][0] = player;
        }
        else
        {
            if(player = 'X')
            {
                cout << "This field has already been taken; please try again!" << endl;
                PlayerInput();
            }
            else if(player = 'O')
            {
                AIinput();
            }
        }
    }
    else if(n == 5)
    {
        if(grid[1][1] == '5')
        {
            grid[1][1] = player;
        }
        else
        {
            if(player = 'X')
            {
                cout << "This field has already been taken; please try again!" << endl;
                PlayerInput();
            }
            else if(player = 'O')
            {
                AIinput();
            }
        }
    }
    else if(n == 6)
    {
        if(grid[1][2] == '6')
        {
            grid[1][2] = player;
        }
        else
        {
            if(player = 'X')
            {
                cout << "This field has already been taken; please try again!" << endl;
                PlayerInput();
            }
            else if(player = 'O')
            {
                AIinput();
            }
        }
    }
    else if(n == 7)
    {
        if(grid[2][0] == '7')
        {
           grid[2][0] = player;
        }
        else
        {
            if(player = 'X')
            {
                cout << "This field has already been taken; please try again!" << endl;
                PlayerInput();
            }
            else if(player = 'O')
            {
                AIinput();
            }
        }
    }
    else if(n == 8)
    {
        if(grid[2][1] == '8')
        {
            grid[2][1] = player;
        }
        else
        {
            if(player = 'X')
            {
                cout << "This field has already been taken; please try again!" << endl;
                PlayerInput();
            }
            else if(player = 'O')
            {
                AIinput();
            }
        }
    }
    else if(n == 9)
    {
        if(grid[2][2] == '9')
        {
            grid[2][2] = player;
        }
        else
        {
            if(player = 'X')
            {
                cout << "This field has already been taken; please try again!" << endl;
                PlayerInput();
            }
            else if(player = 'O')
            {
                AIinput();
            }
        }
    }
}
void PlayerInput()
{
    player = 'X';
    cout << endl;

    cout << "Input the number of the field you would like to fill." << endl;

    int n;
    cin >> n;

    while(!cin || n < 1 || n > 9)                                       // disallows inputs that are not integers between 1 and 9.
    {
        cout << "Please enter an integer value between 1 and 9." << endl;
        cin.clear();
        cin.ignore(numeric_limits <streamsize>::max(), '\n');
        cout << "Input the number of the field you would like to fill." << endl;
        cin >> n;
    }
    GeneralInput(n);
}
void AIinput()
{
    player = 'O';

    random_device rd;                                                           // lines 258-261 used to generate a random number between x and y
    mt19937 gen(rd());
    uniform_real_distribution<> dis(1, 9);
    int n = dis(gen);

    GeneralInput(n);
}

char HowToWin()
{
    // Checking to see if we have three 'X's in a row

    if(grid[0][0] == 'X' && grid[0][1] == 'X' && grid[0][2] == 'X')
    {
        return 'X';
    }
    if(grid[1][0] == 'X' && grid[1][1] == 'X' && grid[1][2] == 'X')
    {
        return 'X';
    }
    if(grid[2][0] == 'X' && grid[2][1] == 'X' && grid[2][2] == 'X')
    {
        return 'X';
    }

    //Checking to see if we have three 'X's in a column

    if(grid[0][0] == 'X' && grid[1][0] == 'X' && grid[2][0] == 'X')
    {
        return 'X';
    }
    if(grid[0][1] == 'X' && grid[1][1] == 'X' && grid[2][1] == 'X')
    {
        return 'X';
    }
    if(grid[0][2] == 'X' && grid[1][2] == 'X' && grid[2][2] == 'X')
    {
        return 'X';
    }

    //Checking to see if we have three 'X's in a diagonal

    if(grid[0][0] == 'X' && grid[1][1] == 'X' && grid[2][2] == 'X')
    {
        return 'X';
    }
    if(grid[2][0] == 'X' && grid[1][1] == 'X' && grid[0][2] == 'X')
    {
        return 'X';
    }

    //Checking to see if we have three 'O's in a row

    if(grid[0][0] == 'O' && grid[0][1] == 'O' && grid[0][2] == 'O')
    {
        return 'O';
    }
    if(grid[1][0] == 'O' && grid[1][1] == 'O' && grid[1][2] == 'O')
    {
        return 'O';
    }
    if(grid[2][0] == 'O' && grid[2][1] == 'O' && grid[2][2] == 'O')
    {
        return 'O';
    }

    //Checking to see if we have three 'O's in a column

    if(grid[0][0] == 'O' && grid[1][0] == 'O' && grid[2][0] == 'O')
    {
        return 'O';
    }
    if(grid[0][1] == 'O' && grid[1][1] == 'O' && grid[2][1] == 'O')
    {
        return 'O';
    }
    if(grid[0][2] == 'O' && grid[1][2] == 'O' && grid[2][2] == 'O')
    {
        return 'O';
    }

    //Checking to see if we have three 'O's in a diagonal

    if(grid[0][0] == 'O' && grid[1][1] == 'O' && grid[2][2] == 'O')
    {
        return 'O';
    }
    if(grid[2][0] == 'O' && grid[1][1] == 'O' && grid[0][2] == 'O')
    {
        return 'O';
    }
}
void CheckWin(int count)
{
        if (HowToWin() == 'X')
        {
            cout << "You win!" << endl;
        }
        else if (HowToWin() == '0')
        {
            cout << "The computer wins!" << endl;
        }
        else if (HowToWin() != 'X' && HowToWin() != 'X' && count == 9)
        {
            cout << "You drew with the computer!" << endl;
        }
}
wrt 1: The problem seems to be somewhere in HowToWin(), but I don't see exactly what.
wrt 2: The random number generator is found in AIinput();

UPDATE: I fixed Problem 1 by adding "return '/';" right before the termination of HowtoWin(). Very strange!
Fixed the random number generator problem!
There are very many compiler warnings issued.
In many places, e.g. line 70, you have the assignment operator = rather than the comparison operator ==

 
if (player = 'X')
should be
 
if (player == 'X')
Hi,

Just an idea not related to your questions :+)

When one represents a 2d array as a 1d array, there is a formula: row * NumCols + col. So this works when rows and cols are numbered in the normal C/C++ fashion: 0 to 2. In the 1d array runs from 0 to 8. You could adjust your numbering system to suit this, or subtract 1 from the user input. So one can do the reverse of this - work out the row and col given a number from 0 to 8. Use integer division and the modulus. And the point of all this is so you can have one function instead of 9 repetitions of lines 62 to 80 :+)

To check for a win, consider maintaining a sum of each column, row and both diagonals. If the sum of a row equals 3 * the value for an X then that is a win. So then you could have:

1
2
3
4
5
6
7
// check rows
if (SumRow[0] == 3 || SumRow[1] == 3 || SumRow[2] == 3) {
    WinX = true;
   return;
}

// check columns and diagonals  


Checking for a win only needs to happens after 5 turns (Player 1 could win on third turn).


I might not reply for awhile - it's late at my end, but hopefully this helps you out a bit :+)
Last edited on
Perhaps you should increase your compiler warning level, or fix the warnings your compiler is generating?

In function 'void GeneralInput(int)': 70:28: warning: suggest parentheses around assignment used as truth value [-Wparentheses] 75:34: warning: suggest parentheses around assignment used as truth value [-Wparentheses] 89:28: warning: suggest parentheses around assignment used as truth value [-Wparentheses] 94:33: warning: suggest parentheses around assignment used as truth value [-Wparentheses] 108:28: warning: suggest parentheses around assignment used as truth value [-Wparentheses] 113:33: warning: suggest parentheses around assignment used as truth value [-Wparentheses] 127:28: warning: suggest parentheses around assignment used as truth value [-Wparentheses] 132:33: warning: suggest parentheses around assignment used as truth value [-Wparentheses] 146:28: warning: suggest parentheses around assignment used as truth value [-Wparentheses] 151:33: warning: suggest parentheses around assignment used as truth value [-Wparentheses] 165:28: warning: suggest parentheses around assignment used as truth value [-Wparentheses] 170:33: warning: suggest parentheses around assignment used as truth value [-Wparentheses] 184:28: warning: suggest parentheses around assignment used as truth value [-Wparentheses] 189:33: warning: suggest parentheses around assignment used as truth value [-Wparentheses] 203:28: warning: suggest parentheses around assignment used as truth value [-Wparentheses] 208:33: warning: suggest parentheses around assignment used as truth value [-Wparentheses] 222:28: warning: suggest parentheses around assignment used as truth value [-Wparentheses] 227:33: warning: suggest parentheses around assignment used as truth value [-Wparentheses] In function 'char HowToWin()': 349:1: warning: control reaches end of non-void function [-Wreturn-type]

I fixed Problem 1 by adding "return '/';" right before the termination of HowtoWin(). Very strange!


Why are you returning '/'? That doesn't look like a valid value for the logic in your CheckWin() function. By the way it's not strange, you should always insure that a function that returns a value actually returns a value.

This looks like you have some logic holes in that function, perhaps you should try to simplify the logic. You have what appears to be a lot of duplicate code, you may want to investigate using some loops.

Thanks for the replies everyone. I am currently thinking through them. My code at the moment is as follows:

http://paste.ofcode.org/fKVfEcb2fppKujSf7NPnGY

The "only" problem now (other than obvious optimization issues) is that the Mersenne Twister random number generator in the AIinput() sometimes returns values for fields that are already taken. I tried to get around this in GeneralInput() by looping back to AIinput().

Once again, I very much appreciate all the feedback!
Hi,

Just had another thought. If you made a vector of the all sums, with the first 8 for the O's (3 rows, 3 cols, 2 diag), then the next 8 for the X's , then you could use the find algorithm to search for a winning value. That way, to determine a win could be only 1 statement. Note that the values in this vector are only incremented once a O or X is placed.

Also, just to reiterate about compiler warning levels, they are your friend - they tell where problems are, so set the warning level at it's highest (or at least turn on as many warnings as are practicable). From my point of view, if I have warnings, then I am not finished.

The other concept is of code repetition, if you find yourself repeating code, or doing code that is very similar, then there is almost always a better way :+)

Good Luck !!

The "only" problem now (other than obvious optimization issues) is that the Mersenne Twister random number generator in the AIinput() sometimes returns values for fields that are already taken. I tried to get around this in GeneralInput() by looping back to AIinput().

There is a problem with the way you are generating random numbers.
251
252
253
254
255
256
257
258
259
260
void AIinput()
{
    player = 'O';

    mt19937 mt_rand(time(0)); // seed the generator
    uniform_int_distribution<> distr(1, 9); // define the range
    int n = (int)distr(mt_rand);

    GeneralInput(n);
}

Above, at line 255, the generator is reseeded using the current time on every call. The generator should be seeded just once at the start of the program, and then subsequent values are generated by the mt19937 itself.

It may not seem to matter much here, because the user input may be sufficiently slow as to allow the time to change on each move. But for example if you were to get the computer to play both X and O sides of the game, the speed of play would be fast enough to cause it to be trapped by the re-seeding problem, every move would be identical - at least the first several million iterations.
Last edited on
Topic archived. No new replies allowed.