New to C++, need some help regarding effiency.

Hey everyone, first time posting. I've recently started learning how to code (about a month ago for C; and a week ago for C++), and am curious on how to vastly improve the inefficiency of my code, specifically my is_game_over function. Also, sense I originally learned C, I might be coding in a way that is too 'C-like', and would be happy to receive tips on how to modify to a C++ method. Anyway here is the code. It's a simple game of tic tac toe:

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
/*
 * A simple game of tic-tac-toe
 */

#include <time.h>
#include <iostream>
using namespace std;
#define BOARD 3
#define TRUE 1
#define FALSE 0
#define MULTIPLAYER 1

void init_board ( char game_board[BOARD][BOARD + 1] );
int get_x_coordinate ( void );
int get_y_coordinate ( void );
void create_gameboard_string_p1 (int x, int y, char game_board[BOARD][BOARD + 1]);
void create_gameboard_string_p2 (int x, int y, char game_board[BOARD][BOARD + 1]);
void draw_game_board( char game_board[BOARD][BOARD + 1] );
int is_game_over_test ( char game_board[BOARD][BOARD + 1] );
int computer_get_random_coordinate ( void );
void create_gameboard_string_computer_easy (int x, int y, char game_board[BOARD][BOARD + 1]);
void computer_easy_game ( char game_board[BOARD][BOARD + 1] );

int main ( void )
{
	char game_board [BOARD][BOARD + 1];
	int x;
	int y;
	int count = 0;
	int is_game_over = FALSE;
	int player_choice;

	cout << "Welcome to Tic Tac Toe Beta v1.5 \n";
	cout << "The game is played by using 0, 1, and 2 as input commands. \nPressing other integers, will simply skip your turn \n\n";
	cout << "Please choose one the game type: \n\t 0 - Single Player \n\t 1 - Multiplayer \n";
	cin >> player_choice;
	cin.ignore();

	init_board (game_board);

	if(player_choice == MULTIPLAYER)
	{
		cout << "Beginning Multiplayer gametype !\n\n";
		while(is_game_over == FALSE)
		{
			if( count % 2 == 0)
			{
				cout << "Go player 1 ! \n";
				x = get_x_coordinate();
				y = get_y_coordinate();
				create_gameboard_string_p1 ( x, y, game_board);
				draw_game_board (game_board);
			}
			else
			{
				cout << "Go player 2 ! \n";
				x = get_x_coordinate();
				y = get_y_coordinate();
				create_gameboard_string_p2 ( x, y, game_board);
				draw_game_board (game_board);
			}
			count++;
			is_game_over = is_game_over_test ( game_board );
		}
	
	}
	else
	{
		cout << "Please choose computer difficulty: \n\t 0 - Easy - We're pretty sure hes just guessing \n\t [still in build] - Medium - Smart... half the time \n\t [still in build] - Hard - Good luck... \n";
		cin >> player_choice;
		cin.ignore();
		cout << "Beginning Single Player gametype !\n\n";
		if (player_choice == 0)
			computer_easy_game ( game_board );
	}
	cout << "Winner ! \n\n\n\n";
	cout << "Thank you for playing.   \nBe sure to download Tic Tac Toe Beta v2.0, with an intelligent AI engine ! \n\n - Matt";
	cin.get();
	return 0;
}

void init_board ( char game_board[BOARD][BOARD + 1] )
{
	int count;
	int row;
	for(row = 0; row < BOARD; row++)
	{
		for(count = 0; count < BOARD; count++)
		{
			game_board [row][count] = '-';
			cout<<"\t"<<game_board [row][count];
		}
		cout<<"\n\n";
	}
}
int get_y_coordinate ( void )
{
	int y;
	cout << "Enter the the y coordinate of where you would like to place (0-2): ";
	cin >> y;
	cin.ignore();
cout << "\n";
	return y;
}
int get_x_coordinate ( void )
{
	int x;
	cout << "Enter the the x coordinate of where you would like to place (0-2): ";
	cin >> x;
	cin.ignore();
	cout << "\n";
	return x;
}
void create_gameboard_string_p1 (int x, int y, char game_board[BOARD][BOARD + 1])
{
	int is_spot_taken = TRUE;
	while( is_spot_taken == TRUE)
	{
		if( game_board [y][x] != '-')
		{ 
			cout << "Error, this spot is already taken ! \n";
			x = get_x_coordinate ();
			y = get_y_coordinate ();
		}
		else
		{
			game_board [y][x] = 'X';
			is_spot_taken = FALSE;
		}
	}
}
void draw_game_board ( char game_board[BOARD][BOARD + 1] )
{
	int count;
	int row;
	for(row = 0; row < BOARD; row++)
	{
		for(count = 0; count < BOARD; count++)
		{
			cout<<"\t"<<game_board [row][count];
		}
		cout<<"\n\n";
	}
}
int is_game_over_test ( char game_board[BOARD][BOARD + 1]  )
{
	if ( game_board [0][0] == 'X' && game_board [1][0] == 'X' && game_board[2][0] == 'X' ) // vert 1
		return TRUE;
	if ( game_board [0][1] == 'X' && game_board [1][1] == 'X' && game_board [2][1] == 'X' ) // vert 2
		return TRUE; 
	if ( game_board [0][2] == 'X' && game_board [1][2] == 'X' && game_board [2][2] == 'X' ) // vert 3
		return TRUE;
	if ( game_board [0][0] == 'X' && game_board [0][1] == 'X' && game_board [0][2] == 'X' ) // hor 1
		return TRUE;
	if ( game_board [1][0] == 'X' && game_board [1][1] == 'X' && game_board [1][2] == 'X' ) // hor 2
		return TRUE;
	if ( game_board [2][0] == 'X' && game_board [2][1] == 'X' && game_board [2][2] == 'X' ) //hor 3
		return TRUE;
	if ( game_board [0][0] == 'X' && game_board [1][1] == 'X' && game_board [2][2] == 'X' ) //slant 1
		return TRUE;
	if ( game_board [2][0] == 'X' && game_board [1][1] == 'X' && game_board [0][2] == 'X' ) // slant 2
		return TRUE;
	if ( game_board [0][0] == 'O' && game_board [1][0] == 'O' && game_board[2][0] == 'O' ) // vert 1
		return TRUE;
	if ( game_board [0][1] == 'O' && game_board [1][1] == 'O' && game_board [2][1] == 'O' ) // vert 2
		return TRUE; 
	if ( game_board [0][2] == 'O' && game_board [1][2] == 'O' && game_board [2][2] == 'O' ) // vert 3
		return TRUE;
	if ( game_board [0][0] == 'O' && game_board [0][1] == 'O' && game_board [0][2] == 'O' ) // hor 1
		return TRUE;
	if ( game_board [1][0] == 'O' && game_board [1][1] == 'O' && game_board [1][2] == 'O' ) // hor 2
		return TRUE;
	if ( game_board [2][0] == 'O' && game_board [2][1] == 'O' && game_board [2][2] == 'O' ) //hor 3
		return TRUE;
	if ( game_board [0][0] == 'O' && game_board [1][1] == 'O' && game_board [2][2] == 'O' ) //slant 1
		return TRUE;
	if ( game_board [2][0] == 'O' && game_board [1][1] == 'O' && game_board [0][2] == 'O' ) // slant 2
		return TRUE;
	return FALSE;
}
void create_gameboard_string_p2 (int x, int y, char game_board[BOARD][BOARD + 1])
{
	int is_spot_taken = TRUE;
	while( is_spot_taken == TRUE)
	{
		if( game_board [y][x] != '-')
		{ 
			cout << "Error, this spot is already taken ! \n";
			x = get_x_coordinate ();
			y = get_y_coordinate ();
		}
		else
		{
			game_board [y][x] = 'O';
			is_spot_taken = FALSE;
		}
	}
}
int computer_get_random_coordinate ( void )
{
	int random_number;

	srand (time(NULL));
	random_number = rand() % (BOARD);
	return random_number;
}
void create_gameboard_string_computer_easy (int x, int y, char game_board[BOARD][BOARD + 1])
{
	int is_spot_taken = TRUE;
	while( is_spot_taken == TRUE)
	{
		if( game_board [y][x] != '-')
		{ 
			x = computer_get_random_coordinate();
			y = computer_get_random_coordinate();
		}
		else
		{
			game_board [y][x] = 'O';
			is_spot_taken = FALSE;
		}
	}
}
void computer_easy_game ( char game_board[BOARD][BOARD + 1]  )
{
	int x;
	int y;
	int count = 0;
	int is_game_over = FALSE;
	while(is_game_over == FALSE)
	{
			if( count % 2 == 0)
			{
				cout << "Go player 1 ! \n";
				x = get_x_coordinate();
				y = get_y_coordinate();
				create_gameboard_string_p1 ( x, y, game_board);
				draw_game_board (game_board);
			}
			else
			{
				cout << "Computers turn.... \n\n";
				x = computer_get_random_coordinate ();
				y = computer_get_random_coordinate();
				create_gameboard_string_computer_easy ( x, y, game_board);
				draw_game_board (game_board);
			}
			count++;
			is_game_over = is_game_over_test ( game_board );;
	
	}
}
Last edited on
Consider using const when defining global variables. They are type safe, and the compiler can spot errors during the build.

Game board could be a class.
The first two things that stick out at me is the use of macros #define and the use of ints to act as booleans. In C++, you try to stay away from macros, the preprocessor is nice, but gets away from the coding aspect of it. There are some great advantages of using macros, but you'll learn them later on. Instead, constant global variables are used. These essentially do the same as what you had, but it's "proper" if you will.

The thing about ints and being true or false...well C++ has a built in type called bool which is exactly 1 bit in size and is either 1 (true) or 0 (false). This way you specifically what values are allowed. An int can be a large range of numbers, and only one of them can result in false (0). Everything else is evaluated as true.

Not specific to C++, as it's in C as well, but you use a lot of while loops with separate variables that increment. Typically in that situation, you'd use a for loop.

Another shortcut is that instead of writing while (is_game_over == FALSE) you can simply write while (!is_game_over). The variable is first evaluated, if it's 0 it returns false, if it's anything else, it's true. Then the opposite of that value (indicated by the ! symbol) and is passed to the while condition. As long as that entire thing evaluates to true, the loop continues. Once it reaches false, it will stop.

Going along the same lines as above, instead of using a separate variable, you can call the function directly in the while condition while (is_game_over_test(game_board)) It will have the same exact effect as what you have now, but one extra call, overall, to the function.

That actual function is a little sloppy. This is where for loops will come in handy. Here is my Winner function (Note: I used a 1D array instead of a 2D array, but similar concepts):
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
int Winner(int board[]) {
   for(int i = 0; i < 3; i ++)
      // Horizontal Victory
      if (board[(i * 3) + 0] == board[(i * 3) + 1]
       && board[(i * 3) + 1] == board[(i * 3) + 2])
         return board[(i * 3) + 0];

      // Vertical Victory
      else if (board[(0 * 3) + i] == board[(1 * 3) + i]
            && board[(1 * 3) + i] == board[(2 * 3) + i])
         return board[(0 * 3) + i];

   // Diagonal Victory
   if (board[0] == board[4] && board[4] == board[8])
      return board[0];
   if (board[2] == board[4] && board[4] == board[6])
      return board[2];
   // Draw
   int moves = 0;
   for (int i = 0; i < 9; i ++)
      if (board[i])
         moves ++;
   if (moves == 9)
      return 3;
   return 0;
}


It's not the prettiest thing out there, but it attempts to tidy up a bunch of if statements that only one letter, number, bracket could be off on and throw your entire function out of wack.

Back in high school, I made a Tic-Tac-Toe game where you could play 2 player, or against a computer, like you're doing. Easy randomly guessed, Medium looked for two consecutive blocks and either moved to win, or if that wasn't possible, moved to block. Hard was programmed so that it'd never lose (manually coded every possible move with favors always on the computers side). My next step was to implement network play, but I abandoned that to improve my menu function, which is floating around on this site, and to build Tetris on the console. A senior made Tetris for his final project and inspired me to make a better one. He was kind enough to share his source and after about 2 weeks of reviewing it, I hand wrote 11 pages of source code. Needless to say, I was impressed and shared my source with him, my class, and my teacher. I corrected a lot of his bugs, including clipping of existing pieces during a spin, and his 0 gravity trick (move side to side fast enough, the piece will never land). I also implemented unique colors for each piece, and used the block character to simulate an almost perfect Tetris Clone. The only issue I had on mine was the scoring wasn't 100% on par with Tetris (I hated the game and refused to do research aside from finding out the colors).

Sorry to ramble, just haven't told the story for a while. But after only knowing C/C++ for a short time, your program isn't bad. I'll have to load it up and see if I notice anything.
Wow thanks for the unbelievable advice. I honestly had no idea about the bool type oddly enough, but wow that will come in handy in the future. Also, I'm just learning classes, but I will definitely look into how to go about that.

Also, if you can tell me how to go about getting to designing a game such as Tetris I would love to learn how to go about it (I realize that I would in no way understand how to code that with my current knowledge). Simply knowing the steps I have to take to learn the knowledge to design Tetris would be extremely interesting to me (I'm especially interested in how to create an object and am constantly overwhelmed by Google searches that often involve complex terminology).
bighat360 wrote:
Also, if you can tell me how to go about getting to designing a game such as Tetris I would love to learn how to go about it

Research and plenty of notes.

As with any program, you need to do some homework (yeah, I know, coding is the fun part, why don't we just jump in and code?). Usually this entails a design approach, how everything will interact and so on. When it comes to games, the best place to start is to either understand the rules (of an existing game) or to create very specific rules (if making your own). You have to figure out what's valid input and how to check. Let's saying you're making a number guessing game, you don't want the user to enter letters or anything, so you have to write that down.

You also want to learn at this point how everything should interact. Should each piece be allowed to change color, or should it be a fixed color? Should the board know what a piece is, or only the fact that there is a grid, some elements filled, some empty. OOP is the best way to go when approaching any game, however, I didn't know any OOP back then, so it's possible to create something good without knowing that (look at your Tic-Tac-Toe game). Once you figure out how everything works (sometimes you need to learn a few things about C++ before you can understand what's possible and what's not), it's a lot easier to jump in.

Start by defining your classes and each of it's methods, let's say we have pieces, board, and the overall game. The game knows all, so it has a board object, and controls piece objects. The piece is only relevant for when the user is moving it, and before placing it on the board. You can also implement a preview window that displays the next piece. A piece doesn't do much, but should be able to share it's information with other objects. The board should be able to return if there is an object at x,y so you know if the spin is going to be valid or if it would clip the object.

As you program anything, especially something like Tetris, you'll run into a ton of issues that you didn't foresee (until you learn more about programming) and might have to rethink some things. It's a tough process, but you'll learn a lot more about programming by just making, what appears to be, a simple game.

I will mention this now, C++ isn't designed to be used in this manner, consoles aren't designed to have colors, and C++ isn't meant to handle keyboard input. With that being said, if you're determined to violate these standard rules, there are workarounds (depending on the OS).

I suggest at least learning the basics of OOP before attempting Tetris. Learn classes and methods (inheritance isn't necessarily important here nor is abstraction). I'd also suggest into looking into console coloring (I have a few posts on this forum, as do other members, about possible ways to achieve this), creating a sleep method (sleep() exists in the windows.h header if you're using windows OS), and a way to accept keyboard presses (avoid conio if possible, windows.h has an alternative (there are some posts regarding using windows to accept keyboard commands and return the key pressed)), and if you want to do it correctly, learn threads (it's an advanced concept, but will fix timing/input issues).

You can pick and choose what you want to learn, but if you learn it all, you'll thank yourself later on (I still have yet to learn how to properly learn threads) as it will make your life much easier. I believe my final code was about 800 lines with little to no comments. I designed and planned and coded for a total time span of almost 2 months, but I had a lot of spare time back them (I didn't do school work, just worked on my program).

If you want some advice, I'm more than happy to help, as are other members here. Just remember, a lot of stuff that's included in your code won't be standard (making any kind of game will have several non standard features). I have been tossing around the idea of recreating my, back then, masterpiece, but I keep looking at what is going to be involved and have decided that I have too much to do to deal with the headaches that will be involved. Don't let this deter you, however, as once you actually get it working, you're going to be so happy and feel fulfilled.

If you have any more specific questions, you can reply here, create a new thread, or PM me (I get PM's in my email so I tend to respond to them fastest).

In regards to your Tic-Tac-Toe game, there are a few things that can be improved upon, like OOP as pogrady suggested, but I'd also suggest making the code a little more module. Create a Game function that handles the entire game, pass parameters such as playername if you want, player 1 should always be x, so no need to pass any characters, but you could pass a bool for single player or multiplayer. Once you take a step back from your physical code and grab a pen/pencil and paper, you can start thinking in a modular form. If you notice that certain code is repeated a lot, make it a function.

I've been rambling on, but that should give you a few things to read and think about. If your intention is to build games, I'd suggest reading some topics on game programming (doesn't need to be C++ specific, but does help) so that you can understand what thinking processes you need to have during the design process and what issues you'll run into. Most articles will be unbiased towards any language, but the author will likely post code, if any, in their favorite language.
@Volatile Pulse

Thanks for the unbelievable response. The time and care you put in to the response is more than appreciated. I appreciate the help in simply finding a direction, and will definitely be looking up some OOP tutorials, and mastering concepts that I feel much less comfortable with (such as classes). You've given me a lot to think about, and a good strategy to follow looking forward. If I have troubles in my research I'd love to talk and PM you. If you also feel like rambling about your own coding experience, I'd be more than happy to here them, as they gave me insight and inspiration. Thanks a ton.
@Volatile Pulse
Sorry to nit pick, but a bool is 1 byte not 1 bit.
You're right. But a bool can be declared as 1 bit and still have full functionality.
But a bool can be declared as 1 bit and still have full functionality.
I assume your referring to bit fields, but even then size of the bool will always be 1 byte (and a bit field will always be at least 1 byte).
naraku wrote:
and a bit field will always be at least 1 byte
.
Only if used alone. Take a struct, for example, you can pack them, up to 8 - 1 bit bools, into a single byte.

I always thought bools were only 1 bit, but they're not defined any differently than an int from what I could see. It makes sense to me that bools should only be 1 bit, but I'm assuming there is a good reason. I thought at least bool myArray[8]; would have a size of 1, but that's not the case.
I't's because bool data type is defined as 1 byte, which is also the smallest addressable memory size.
Topic archived. No new replies allowed.