Tic tac toe efficiency challenge

Hi there. I don't study c++. I like to preface my posts with that statement, because it's frowned upon to ask for code on this forum. But i'll cut to the chase, i perfected this tic tac toe program today (I made it yesterday and added functions today) so that it's just under 100 lines of code. Here's the code.

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
#include <iostream>

int printboard(char board[9]);
int wincheckfunc(char board[9], int wincheck,int playmore); 
int input(int errorfree,int placement, char board[9]);
void endgame(int playmore, char board[9], int turn);
int main() {

	char board[9] = {'0','1','2','3','4','5','6','7','8'};
	
	int wincheck = 0;
	int playmore = 1;
	int placement = 0;
	int turn = 1;
	int errorfree = 1;
	printboard(board);
	
	for (int i = 0; i < 4; i++)
	{
	std::cout << "Player 1's turn! Where do you want to place your X?" << "\n";	
	placement = input(errorfree,placement,board);	
	errorfree = 1;															   // Resets 'errorchecking' variable so that the next input will (also) be checked.
	board[placement] = 'X';
	playmore = wincheckfunc(board,wincheck,playmore);
	if (playmore == 0) break;
	turn++;																	   // This turn counter that ticks back and forth between 1 and 2, determines which player will be said to have won.
	printboard(board);

	if (i == 4) break;

	std::cout << "Player 2's turn! Where do you want to place your O?" << "\n";
	placement = input(errorfree,placement,board);	
	errorfree = 1;															   //  Resets 'errorchecking' variable so that the next input will also be checked.
	board[placement] = 'O';
	playmore = wincheckfunc(board,wincheck,playmore);
	if (playmore == 0) break;
	turn--;																	   // This turn counter that ticks back and forth between 1 and 2, determines which player will be said to have won.
	printboard(board);
	}

	endgame(playmore,board,turn);
	system ("pause");
	return 0;
}
int printboard(char board[9])
{
	system("cls");
	std::cout << "\n";	
	std::cout << "  " << board[0] << "  " << board[1] << "  " << board[2] << "  " << "\n";
	std::cout << "\n";
	std::cout << "  " << board[3] << "  " << board[4] << "  " << board[5] << "  " << "\n";
	std::cout << "\n";
	std::cout << "  " << board[6] << "  " << board[7] << "  " << board[8] << "  " << "\n";
	std::cout << "\n";
	return 0;
}
int wincheckfunc(char board[9], int wincheck, int playmore)
{

	if (board[0] == board[1] && board[1] == board[2])      // Horizontal 1
		playmore = 0;
	else if (board[3] == board[4] && board[4] == board[5]) // Horizontal 2
		playmore = 0;
	else if (board[6] == board[7] && board[7] == board[8]) // Horizontal 3
		playmore = 0;
	else if (board[0] == board[3] && board[3] == board[6]) // Vertical 1
		playmore =0;
	else if (board[1] == board[4] && board[4] == board[7]) // Vertical 2
		playmore = 0;
	else if (board[2] == board[5] && board[5] == board[8]) // Vertical 3
		playmore = 0;
	else if (board[0] == board[4] && board[4] == board[8]) // Diagonal 1
		playmore = 0;
	else if (board[2] == board[4] && board[4] == board[6]) // Diagonal 2
		playmore = 0;
								
	else if (board[0] != '0' && board[1] != '1' && board[2] != '2' && board[3] != '3' && board[4] != '4' && board[5] != '5' && board[6] != '6 '&& board[7] != '7' && board[8] != '8') // Draw
	playmore = 0;

	return playmore;
	}
int input(int errorfree,int placement, char board[9])
{
	while (errorfree == 1)																	// "std::cin placement;" with errorchecking
	{																					   // that nothing is placed there from earlier turns.
	std::cin >> placement;																  //
	errorfree = 0;																		 //
	if (board[placement] == 'X') {												        //
	errorfree = 1;																	   //
	std::cout << "Error. You have already placed an X there! Try again." << "\n"; }   //
	else if (board[placement] == 'O') {												 //
	errorfree = 1;																	//
	std::cout << "Error. You have already placed an O there! Try again." << "\n"; }//
	}	
	
	return placement;
}
void endgame(int playmore, char board[9], int turn)
{
	printboard(board);
	if (playmore == 0) 
	{
	std::cout << "\n";
	std::cout << "Congratulations, player " << turn << " wins!" << "\n"; 
	}
	else std::cout << "It's a draw. Game over." << "\n";
}


Are there anything that you professionals are thinking might be insufficient?
Any different ways to do anything here?
PS: I don't like to use namespace std; having that as a habit might mess you up later in more complex code.
Hmm, passing an array to a function. I didn't think you needed to include the 9 in there since all you are doing is passing the address of the array itself (or the first element.) What it looks like you are doing is passing the address of the 9th element. Does this actually compile and work?
Hi Terje Gundersen ,

Quite a good effort with your code - Well done !!

Just a couple of minor things:

You could improve this part:
else if (board[0] != '0' && board[1] != '1' && board[2] != '2' && board[3] != '3' && board[4] != '4' && board[5] != '5' && board[6] != '6 '&& board[7] != '7' && board[8] != '8') // Draw

By using a for loop. Find out what the ASCII value of the zero char is and use that along with the counter variable to help do the comparisons. This makes use of the fact that a char is just a small int. It also scalable to a larger board, like Connect 4 (There's a good challenge!!)

The other thing was to make use of bool variables, instead of 0 & 1- but that is a very minor thing.

With the std:: stuff - I do this for things I use a lot:

1
2
3
4
using std::cout;
using std::cin;
using std::endl;


Then you can use cin , cout, endl without qualification.

For other things that are not used as much I prepend the std::

Hope all goes well :)
Last edited on
It does compile and work, however when i send it without the [9] it doesn't work. Weird, right? I'm gonna go see if i can send "board[]" and see if that'll work because it might not confuse people in the future.

Using VS12 by the way.

Edit: Yes, sending "board[]" works as well. "board" however, does not work.
Last edited on
@Theideasman

using std::cout;
using std::cin;
using std::endl;

Is helpful to me, since i only knew that
using namespace std;
would help you achieve the same result. I hear using the above line
is bad because you will be royally f*cked one day if you for instance want to
access std::map while you have a variable called map, using namespace std.

I assume you mean something along the lines of this? (Although this one assings values and doesn't check variables for values.)

1
2
3
4
for (i = 0; i < 9; i++)
{
board[i] = i + 1;
}


My problem understanding how you'd check the variables with a for loop comes where the dual ampersands are (&&). How would you implement that in a for loop?
Last edited on
Well, board is actually a pointer to the first element of the array, when passed to the function. So you would have to make your functions so that they would accept a pointer to an array. Fortunately, for an array, that would look like array[]. Alternatively, char * array works as well. That's why board itself doesn't work when passed to the function. I'm not sure why it works when passing board[9] though. That is odd and beyond my understanding. :) I wonder if it's actually passing board[] by value in those cases, or rather, passing an entire copy of the board instead of the address (which is a value too.) Hmm.. Ah nm, I see, you are passing board TO the functions, but I'm going to guess that with char board[9] in the function declaration it is actually creating another array and filling it with the values from the array you passed to it. I'm going to try something. If it works I'll post back soon.
Last edited on
I suppose since you state
 
int printboard(char board[9]);

with the identifier of "board", it doesnt pass a single value, but
the entire array to the function, merely because the identifier is 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
int printboard(char * board)
{
	system("cls");
	std::cout << "\n";
	std::cout << "  " << board[0] << "  " << board[1] << "  " << board[2] << "  " << "\n";
	std::cout << "\n";
	std::cout << "  " << board[3] << "  " << board[4] << "  " << board[5] << "  " << "\n";
	std::cout << "\n";
	std::cout << "  " << board[6] << "  " << board[7] << "  " << board[8] << "  " << "\n";
	std::cout << "\n";
	return 0;
}
int wincheckfunc(char * board, int wincheck, int playmore)
{

	if (board[0] == board[1] && board[1] == board[2])      // Horizontal 1
		playmore = 0;
	else if (board[3] == board[4] && board[4] == board[5]) // Horizontal 2
		playmore = 0;
	else if (board[6] == board[7] && board[7] == board[8]) // Horizontal 3
		playmore = 0;
	else if (board[0] == board[3] && board[3] == board[6]) // Vertical 1
		playmore =0;
	else if (board[1] == board[4] && board[4] == board[7]) // Vertical 2
		playmore = 0;
	else if (board[2] == board[5] && board[5] == board[8]) // Vertical 3
		playmore = 0;
	else if (board[0] == board[4] && board[4] == board[8]) // Diagonal 1
		playmore = 0;
	else if (board[2] == board[4] && board[4] == board[6]) // Diagonal 2
		playmore = 0;

	else if (board[0] != '0' && board[1] != '1' && board[2] != '2' && board[3] != '3' && board[4] != '4' && board[5] != '5' && board[6] != '6 '&& board[7] != '7' && board[8] != '8') // Draw
	playmore = 0;

	return playmore;
	}
int input(int errorfree,int placement, char * board)
{
	while (errorfree == 1)																	// "std::cin placement;" with errorchecking
	{																					   // that nothing is placed there from earlier turns.
	std::cin >> placement;																  //
	errorfree = 0;																		 //
	if (board[placement] == 'X') {												        //
	errorfree = 1;																	   //
	std::cout << "Error. You have already placed an X there! Try again." << "\n"; }   //
	else if (board[placement] == 'O') {												 //
	errorfree = 1;																	//
	std::cout << "Error. You have already placed an O there! Try again." << "\n"; }//
	}

	return placement;
}
void endgame(int playmore, char * board, int turn)
{
	printboard(board);
	if (playmore == 0)
	{
	std::cout << "\n";
	std::cout << "Congratulations, player " << turn << " wins!" << "\n";
	}
	else std::cout << "It's a draw. Game over." << "\n";
}


Welp, this works for me too. This way you are passing the array by pointers. I'm honestly not sure how your code was passing them. It worked, but I have a suspicion it's uses more memory. Not really a big deal for such a small program, but you asked for ideas. :) And like TIM said, well done. The program is very concise, not sure if I could have gotten this down to 100 lines myself! I especially like how you make the wincheckfunc player neutral by comparing each element not to x or 0 but to it's neighbors. That's clever!
Last edited on
@Reazzor
Thanks!
My problem understanding how you'd check the variables with a for loop comes where the dual ampersands are (&&). How would you implement that in a for loop?


1
2
3
4
5
6
7
8
9

bool Draw = true;
char zero = '0';
for(char Spot = 0;Spot < 9;Spot++) {
    if  (board[Spot] == zero + Spot) //if any of them are equal it is not a draw
                Draw = false;
}



See if that works - it relies on the character set being sequential ( zero to nine are sequential). That is probably not ideal in itself, however it is scalable.

What attracts my attention is when I see long patterns (sequential or otherwise) in conditions. And this prompts the question - What would you do if you had a board that was 8 by 8 (like in Connect 4) ? Are you going to have 64 conditions?

Have fun !! :)
This will promt another problem; having to return both "playmore" and "draw" variable, and then stating [Code] Else if (draw == true) std::cout << "draw" << "\n"; [/code]

This also highlights the fact that my checking for a draw is irrelevant. It cannot be a draw until there are 9 round played, and at that point, if (playmore == 1), it will assume a draw game anyway. The current code is actually flawd, that makes it in case of a draw, tell you that the person making the turn would lose. Bad code in general.

By changing my for loop to this (This was an error all along, it was meant to be 5)
for (int i = 0; i < 5; i++)
(Edit: This is so that the for loop would run 5 times, but this allows for players to make 10 turns doesn't it? That's when the if (i == 4) it breaks after the first player takes his last turn, leaving the max number of turns at 9)

and my wincheck function to this ( removing the draw check)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
int wincheckfunc(char board[], int wincheck, int playmore)
{

	if (board[0] == board[1] && board[1] == board[2])      // Horizontal 1
		playmore = 0;
	else if (board[3] == board[4] && board[4] == board[5]) // Horizontal 2
		playmore = 0;
	else if (board[6] == board[7] && board[7] == board[8]) // Horizontal 3
		playmore = 0;
	else if (board[0] == board[3] && board[3] == board[6]) // Vertical 1
		playmore =0;
	else if (board[1] == board[4] && board[4] == board[7]) // Vertical 2
		playmore = 0;
	else if (board[2] == board[5] && board[5] == board[8]) // Vertical 3
		playmore = 0;
	else if (board[0] == board[4] && board[4] == board[8]) // Diagonal 1
		playmore = 0;
	else if (board[2] == board[4] && board[4] == board[6]) // Diagonal 2
		playmore = 0;
								
	

	return playmore;
	}


The program should run perfectly. Just a minor errorfix.

Last edited on
Topic archived. No new replies allowed.