Console tic tac toe win condition

closed account (10oTURfi)
Is this really only way to make win condition for tic tac toe? Or there is better and easier way? (see lines 55-70)
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
#include <iostream>
#include <cctype>
//#include <string>
//#include "windows.h"
using namespace std;

char board[9];
bool isover = false;
bool forever = false;
int input;
int counter;

void PrintBoard();
void Reset();
void CheckWinCondition();
void moveX();
void moveO();

int main()
{
	Reset();

	while(!forever)
	{
		PrintBoard();
		moveX();
		CheckWinCondition();
		if(isover == true) break;
		PrintBoard();
		moveO();
		CheckWinCondition();
		if(isover == true) break;
	}
//Insert "Do you want to play again" here??

	return 0;
}

void PrintBoard()
{
	system("cls");
	cout << " " << board[1] << " | " << board[2] << " | " << board[3] <<  endl;
	cout << "---" << "+" << "---" << "+" << "---" << endl;
	cout << " " << board[4] << " | " << board[5] << " | " << board[6] << endl;
	cout << "---" << "+" << "---" << "+" << "---" << endl;
	cout << " " << board[7] << " | " << board[8] << " | " << board[9] << endl;

}

void Reset()
{
	strcpy(board, "0123456789");
}

void CheckWinCondition()
{
	if((board[1] == 'X' && board[2] == 'X' && board[3] == 'X') || (board[1] == 'O' && board[2] == 'O' && board[3] == 'O'))
	{
		PrintBoard();
		cout << board[1] << " je pobjedio!" << endl;
		isover = true;
	}
	else if((board[4] == 'X' && board[5] == 'X' && board[6] == 'X') || (board[4] == 'O' && board[5] == 'O' && board[6] == 'O'))
	{
		PrintBoard();
		cout << board[4] << " je pobjedio!" << endl;
		isover = true;
	}
//Insert all possible win conditions here...
}

void moveX()
{
opet:
	cin >> input;
	if(isalpha(board[input])) goto opet; //Prevents cheating??
	board[input] = 'X';
}

void moveO()
{
opet:
	cin >> input;
	if(isalpha(board[input])) goto opet; //Prevents cheating??
	board[input] = 'O';
}
Yes, this can be done much easier using loops. See here:
http://www.cplusplus.com/doc/tutorial/control/#loops

Also, arrays are 0-based, so the indices for board go from 0 to 8.
And this won't work: strcpy(board, "0123456789");
You're trying to store 11 characters (10 digits and the terminating \0) in an array with 9 elements.

Edit: more stuff:
Don't do this: if(isover == true)
If you want to check whether a boolean expression is true, use if (isover) or if (!isover) if you want to check whether it's false.

In moveX, moveO get rid of goto. The correct solution here is to use a loop.
Never use goto within the first five years of learning C++ (after that, I assume that you'll know enough to make the decision yourself).

And you can't use isalpha with an int.
Last edited on
closed account (10oTURfi)
Edit: more stuff:
Don't do this: if(isover == true)
If you want to check whether a boolean expression is true, use if (isover) or if (!isover) if you want to check whether it's false.

Before I compiled this program for first time, there was if(isover). However, strange problem would happen after first move; while loop would instantly break. Thats why I added == true, and after that program worked fine!

this was compiled in MS VC++ 2008



EDIT:
Also, arrays are 0-based, so the indices for board go from 0 to 8.
And this won't work: strcpy(board, "0123456789");
You're trying to store 11 characters (10 digits and the terminating \0) in an array with 9 elements.

But then again, why did my program compile and work fine?
Last edited on
But then again, why did my program compile and work fine?

Not all errors lead to compiler errors. Some lead to undefined behavior, which can result in anything from crashes to different and unexpected program behavior. Writing beyond array bounds is such an error, which causes memory corruption.

And this is likely a direct result from memory corruption:
Before I compiled this program for first time, there was if(isover). However, strange problem would happen after first move; while loop would instantly break. Thats why I added == true, and after that program worked fine!


Since isover comes directly after board, it's likely that your strcpy tramples all over these variables.
closed account (10oTURfi)

Not all errors lead to compiler errors. Some lead to undefined behavior, which can result in anything from crashes to different and unexpected program behavior. Writing beyond array bounds is such an error, which causes memory corruption.


Since isover comes directly after board, it's likely that your strcpy tramples all over these variables.

Thanks alot. Seems that was a problem.
closed account (10oTURfi)
This is v2 of tic tac toe.
Got rid of gotos, fixed array.
But still I dont understand how to simplify win conditions...

EDIT: Also, why does system("cls") work without windows.h ? I thought its defined in there.
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
#include <iostream>
#include <cctype>
#include <string>
//#include "windows.h"
using namespace std;

char board[11];
bool isover = false;
bool forever = false;
int input;
int counter;

void PrintBoard();
void Reset();
void CheckWinCondition();
void moveX();
void moveO();

int main()
{
	Reset();

	while(!forever)
	{
		PrintBoard();
		moveX();
		CheckWinCondition();
		if(isover) break;

		PrintBoard();
		moveO();
		CheckWinCondition();
		if(isover) break;
	}

	string DaNe;

	cout << "Jel oces opet igrat?" << endl; //Wish to play again?
	cin >> DaNe;
	if(DaNe == "da") main();

	return 0;
}

void PrintBoard()
{
	system("cls");
	cout << " " << board[1] << " | " << board[2] << " | " << board[3] <<  endl;
	cout << "---" << "+" << "---" << "+" << "---" << endl;
	cout << " " << board[4] << " | " << board[5] << " | " << board[6] << endl;
	cout << "---" << "+" << "---" << "+" << "---" << endl;
	cout << " " << board[7] << " | " << board[8] << " | " << board[9] << endl;

}

void Reset()
{
	strcpy(board, "0123456789");
}

void CheckWinCondition()
{
	//Check all possible win conditions?
	if((board[1] == 'X' && board[2] == 'X' && board[3] == 'X') || (board[1] == 'O' && board[2] == 'O' && board[3] == 'O'))
	{
		PrintBoard();
		cout << board[1] << " je pobjedio!" << endl;
		isover = true;
	}
	else if((board[4] == 'X' && board[5] == 'X' && board[6] == 'X') || (board[4] == 'O' && board[5] == 'O' && board[6] == 'O'))
	{
		PrintBoard();
		cout << board[4] << " je pobjedio!" << endl;
		isover = true;
	}
//Insert shit loads of win conditions here

	//Check if there were 9 moves and no winner
	else if(counter == 9)
	{
		cout << "Niko nije pobjedio..." << endl;
		isover = true;
	}
}

void moveX()
{
	while(!forever)
	{
		cin >> input;
		if(!isalpha(board[input])) break;
	}
	board[input] = 'X';
        counter++;
}

void moveO()
{
	while(!forever)
	{
		cin >> input;
		if(!isalpha(board[input])) break;
	}
	board[input] = 'O';
        counter++;
}
Last edited on
For my version of TicTacToe i used 3x3 array of integers to define game board (0=empty, 1=X, 2=O)
X(=1) starts the game and after each move game checks if CURRENT player has won and after that checks if board is full (game end with no winner).

To switch turns i just XOR'ed turn value by 3.

And oh.. the winning conditions:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
void game::gameCheck()
{
	// win by rows
	if (board[0][0]==turn && board[0][1]==turn && board[0][2]==turn)
		gameOver=true;
	if (board[1][0]==turn && board[1][1]==turn && board[1][2]==turn)
		gameOver=true;
	if (board[2][0]==turn && board[2][1]==turn && board[2][2]==turn)
		gameOver=true;
	// win by cols
	if (board[0][0]==turn && board[1][0]==turn && board[2][0]==turn)
		gameOver=true;
	if (board[0][1]==turn && board[1][1]==turn && board[2][1]==turn)
		gameOver=true;
	if (board[0][2]==turn && board[1][2]==turn && board[2][2]==turn)
		gameOver=true;
	// diagonals
	if (board[0][0]==turn && board[1][1]==turn && board[2][2]==turn)
		gameOver=true;
	if (board[0][2]==turn && board[1][1]==turn && board[2][0]==turn)
		gameOver=true;
}
Last edited on
closed account (10oTURfi)
These are mine.
Dont laugh. I know.
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
void CheckWinCondition()
{
	//  ---
	//  ---
	//  ---

	if((board[1] == 'X' && board[2] == 'X' && board[3] == 'X') || (board[1] == 'O' && board[2] == 'O' && board[3] == 'O'))
	{
		PrintBoard();
		cout << board[1] << " je pobjedio!" << endl;
		isover = true;
	}
	else if((board[4] == 'X' && board[5] == 'X' && board[6] == 'X') || (board[4] == 'O' && board[5] == 'O' && board[6] == 'O'))
	{
		PrintBoard();
		cout << board[4] << " je pobjedio!" << endl;
		isover = true;
	}
	else if((board[7] == 'X' && board[8] == 'X' && board[9] == 'X') || (board[7] == 'O' && board[8] == 'O' && board[9] == 'O'))
	{
		PrintBoard();
		cout << board[7] << " je pobjedio!" << endl;
		isover = true;
	}

	//  |||
	//  |||
	//  |||

	else if((board[1] == 'X' && board[4] == 'X' && board[7] == 'X') || (board[1] == 'O' && board[4] == 'O' && board[7] == 'O'))
	{
		PrintBoard();
		cout << board[1] << " je pobjedio!" << endl;
		isover = true;
	}
	else if((board[2] == 'X' && board[5] == 'X' && board[8] == 'X') || (board[2] == 'O' && board[5] == 'O' && board[8] == 'O'))
	{
		PrintBoard();
		cout << board[2] << " je pobjedio!" << endl;
		isover = true;
	}
	else if((board[3] == 'X' && board[6] == 'X' && board[9] == 'X') || (board[3] == 'O' && board[6] == 'O' && board[9] == 'O'))
	{
		PrintBoard();
		cout << board[3] << " je pobjedio!" << endl;
		isover = true;
	}

	//  \  /
	//   X
	//  /  \
	
	else if((board[1] == 'X' && board[5] == 'X' && board[9] == 'X') || (board[1] == 'O' && board[5] == 'O' && board[9] == 'O'))
	{
		PrintBoard();
		cout << board[1] << " je pobjedio!" << endl;
		isover = true;
	}
	else if((board[3] == 'X' && board[5] == 'X' && board[7] == 'X') || (board[3] == 'O' && board[5] == 'O' && board[7] == 'O'))
	{
		PrintBoard();
		cout << board[1] << " je pobjedio!" << endl;
		isover = true;
	}

	//Check if there were 9 moves and no winner
	else if(counter == 9)
	{
		cout << "Niko nije pobjedio..." << endl;
		isover = true;
	}
}
You should try to do this with classes... easier and more clean (and no global variables)
closed account (10oTURfi)
You should try to do this with classes... easier and more clean (and no global variables)

Uhh. I'm still beginner, started 3 weeks ago. ATM reading chapter 6 of Herbert Schildts guide, and still havent learned about classes.
How about returning something from the function itself?
Passing arguments to them?

That is enough to get rid of global variables :)

EDIT: Created small example thing told above:
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
#include <iostream>
using namespace std;	// Normally i don't use this...
			// Normally i REALLY use std::cout version
void init(int a[]);
void print(int a[]);

int main()
{
	// Create and initialize board
	int board[9];
	init(board);
// FOR DEMO PURPOSES!
board[2]=1;
board[5]=2;
board[6]=1;
	// print board
	print(board);
}

void init(int a[])
{
	for (int i=0; i<9; i++) 
		a[i]=0;
}

void print(int a[])
{
	for (int i=0; i<9; i++) {
		// print new row if necessary
		if (!(i%3))
			cout << endl << "|";
		// if value in board is 0 print " ", 
		// 1 print X and if 2 print O
		if (!a[i])
			cout << " |";
		else if (a[i]==1)
			cout << "X|";
		else
			cout << "O|";
	}
	// make sure we have clean row after returning to main()
	cout << endl;
}


Notice: no global variables

Output:
eraggo@bono:/tmp$ g++ tic.cpp && ./a.out 

| | |X|
| | |O|
|X| | |
Last edited on
My math teacher showed us a way that each grid can be assigend a number and a winning line -- either horizontal, vertical or diagonal -- has the sum of 15 for the three pieces. It sucks I forgot to save the notes.
Last edited on
closed account (10oTURfi)
@eraggo
I tried editing your code and managed to finish it without using any global variables. (How does my code look like to you?)
But why is it so bad to use global variables in such small program?
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
#include <iostream>
using namespace std;

void init(int a[]);
void print(int a[]);
bool CheckWinCondition(int a[]);

int main()
{
	// Create and initialize board
	int board[9];
	init(board);
	// print board
	print(board);

	//Krofna's edit begins here
	int input;
	bool forever = false;
	int counter = 0;
        bool isover;

	while(!forever)
	{
		cin >> input;

		if(counter==0)
		{
			board[input-1] = 1;
			print(board);
			counter = 1;
			isover = CheckWinCondition(board);
//Only X can win cuz im too lazy to make 16 possible win conditions
			if(isover)
			{
				cout << "X WINS!\n";
			}
		}
		else if(counter==1)
		{
			board[input-1] = 2;
			print(board);
			counter = 0;
		}
	}
	return 0;
	//Krofna's edit ends here
}

void init(int a[])
{
	for (int i=0; i<9; i++) 
		a[i]=0;
}

void print(int a[])
{
	for (int i=0; i<9; i++) {
		// print new row if necessary
		if (!(i%3))
			cout << endl << "|";
		// if value in board is 0 print " ", 
		// 1 print X and if 2 print O
		if (!a[i])
			cout << " |";
		else if (a[i]==1)
			cout << "X|";
		else
			cout << "O|";
	}
	// make sure we have clean row after returning to main()
	cout << endl;
}

bool CheckWinCondition(int a[]) //Demo win condition
{
	if((a[0] == 1 && a[1] == 1 && a[2] == 1))
		return true;
	return false;
}
Last edited on
Maybe it is farewell to use globals in small projects but some habits will stay. So, learning good practices is good for later on.

Have you compiled and runned your code succesfully?
About line 22: Where do you change state of forever-variable so loop can exit?

//Only X can win cuz im too lazy to make 16 possible win conditions
16? 3 ways to win by rows, 3 ways to win vertically, and 2 ways to win diagonally = 8 ways ;) Keep it simple.
Good(ish) prototype for checking win condition:
bool CheckWinCondition(int a[], int turn); // turn=who turn it is currently


closed account (10oTURfi)
I dont change state of forever. While loop should break when CheckWinCondition returns true value. Actually... I read the code again and it seems I forgot to break that loop.
1
2
3
4
5
if(isover)//Line 33
		{
			cout << "X WINS!\n";
                        break;
		}

Now its fixed.

By 16 win conditions I meant 8 X and 8 O.
if((a[0] == 1 && a[1] == 1 && a[2] == 1)||(a[0] == 2 && a[1] == 2 && a[2] == 2))//Thats 2


EDIT: On second thought, I think forever wasnt necessary.
1
2
3
4
5
6
7
8
9
10
11
12
13
bool isover = false;

	while(!isover)
	{
		cin >> input;

		if(counter==0)
		{
			board[input-1] = 1;
			print(board);
			counter = 1;
			isover = CheckWinCondition(board);
//... 
Last edited on
Topic archived. No new replies allowed.