Tic Tac Toe Expert needed

Hi Everyone,

/////////////////////////////////////////////////////////////////////

simple tic tac toe game. player inputs, computer chooses a random empty square, and places an 'O'.

///////////////////////////////////////////////////////////////////////


Need a little help with my Tic Tac Toe game. It's only a few minor issues.

1. on line 117, if a player wants to start a new game, how do I clear the old object and start fresh?

2. I have not had much experience with random numbers, but sometimes around line 55 I get a division by zero error message. No idea why this happens.

3. The most frustrating, but so close. Line 150! When a player or computer chooses a square, it goes into a memory array, then the program checks if the number exists in the array and if it does, does not allow user to enter. It works most of the time, but sometimes it skips and does not work properly. I spent a while on this but with no luck. Please assist.

4. Looking for criticism. If I am doing something wrong, or if you have a better way of doing things, would love to hear it so I can become a better programer. Please keep in mind I am still a noob.

5. If you have any good ideas for AI, something more advanced than my random, would like to hear it as well.

Thanks,

Mike


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
#include "stdafx.h"
#include <iostream>
#include <conio.h>
#include <cstdlib>
#include <iomanip>
#include <cmath>
#include <string>
#include <cctype>
#include <vector>
#include <fstream>
#include <stdlib.h>
#include <locale>
#include <iomanip>
#include <sstream>
using namespace std;
using std::ifstream;
using std::ofstream;
using std::endl;
using std::ios;

const int MAX=3;

class TTT
{
public:
	TTT()
	{
		board[0][0]='7';
		board[0][1]='8';
		board[0][2]='9';
		board[1][0]='4';
		board[1][1]='5';
		board[1][2]='6';
		board[2][0]='1';
		board[2][1]='2';
		board[2][2]='3';

		game_over=false;
		turn=0;
		
		for(int i=0;i<9;i++)
			memory[i]='0';
	}

	friend ostream& operator<<(ostream& out, TTT& t);
	friend istream& operator>>(istream& in,TTT& t);

	bool get_game_status(){return game_over;}
	int get_turn(){return turn;};
	bool get_winner(){return winner;}
	
	char get_place(vector<char> a){
		srand(time(0));
		int number=a.size();
		int random=rand()%number;//!!!!!!!divison by zero problem here?!!!!!!!!!ééééééééééééééééééé
		return (a[random]);
	}

	void computer_turn(){
		bool placed=false;
		vector<char> v;
		int i=0, j=0;

		for(i=0;i<MAX;i++){
			for(j=0;j<MAX;j++){
				if(isdigit(board[i][j]))
					v.push_back(board[i][j]);	
			}
		
		}
		char temp=get_place(v);
		for(i=0;i<MAX;i++){
			for(j=0;j<MAX;j++){
				if(board[i][j]==temp){
					board[i][j]='O';
				check_win();
				}
			}
		}
		memory[turn++]=temp;//load computer into memmory
	}
	void check_win(){
		if(board[0][0]=='X' && board[0][1]=='X' && board[0][2]=='X'){game_over=true;winner=true;}
		if(board[1][0]=='X' && board[1][1]=='X' && board[1][2]=='X'){game_over=true;winner=true;}
		if(board[2][0]=='X' && board[2][1]=='X' && board[2][2]=='X'){game_over=true;winner=true;}

		if(board[0][0]=='X' && board[1][0]=='X' && board[2][0]=='X'){game_over=true;winner=true;}
		if(board[0][1]=='X' && board[1][1]=='X' && board[2][1]=='X'){game_over=true;winner=true;}
		if(board[0][2]=='X' && board[1][2]=='X' && board[2][2]=='X'){game_over=true;winner=true;}

		if(board[0][0]=='X' && board[1][1]=='X' && board[2][2]=='X'){game_over=true;winner=true;}
		if(board[0][2]=='X' && board[1][1]=='X' && board[2][0]=='X'){game_over=true;winner=true;}

		if(board[0][0]=='O' && board[0][1]=='O' && board[0][2]=='O'){game_over=true;winner=false;}
		if(board[1][0]=='O' && board[1][1]=='O' && board[1][2]=='O'){game_over=true;winner=false;}
		if(board[2][0]=='O' && board[2][1]=='O' && board[2][2]=='O'){game_over=true;winner=false;}

		if(board[0][0]=='O' && board[1][0]=='O' && board[2][0]=='O'){game_over=true;winner=false;}
		if(board[0][1]=='O' && board[1][1]=='O' && board[2][1]=='O'){game_over=true;winner=false;}
		if(board[0][2]=='O' && board[1][2]=='O' && board[2][2]=='O'){game_over=true;winner=false;}

		if(board[0][0]=='O' && board[1][1]=='O' && board[2][2]=='O'){game_over=true;winner=false;}
		if(board[0][2]=='O' && board[1][1]=='O' && board[2][0]=='O'){game_over=true;winner=false;}
	}
private:
	char board[MAX][MAX];
	bool game_over, winner;
	char memory[9];
	int turn;
};
int main()
{
	char choice;
	TTT game;
	do{
		//!!!!!!!!!!When I wnat to play another game, how do I clear the constructor, and create a brand new object??/////////////!!!!!!
		cout<<"Welcome to Tic Tac Toe, here'sthe world!\n";
		cout<<game;
		do{
			cin>>game;
			game.computer_turn();
			cout<<game;

			if(game.get_turn()>5)
				!game.get_game_status();
			
		}while(!game.get_game_status());
		if(game.get_winner())
			cout<<"Congratulations, you beat the computer!\n";
		if(!game.get_winner())
			cout<<"Congratulations, you suck!\n";

		cout<<"\n\nAgain(y/n): ";
		cin>>choice;
	}while(choice=='y' || choice=='Y');


	cout<<"Have a nice day!";
	exit(1);
	_getch();	
    return 0;
}
istream& operator>>(istream& in,TTT& t){
	char choice;
	cout<<"\n\tEnter a square choice: ";
	in>>choice;

	for(int i=0;i<9;i++){
		while(choice==t.memory[i]){
			cout<<"Occupied, enter again: ";//!!!!!!!!!!!!!!check to see if space is occupied, does not always work!!!!!!!!!!//////
			in>>choice;
		}
	}
	t.memory[t.turn++]=choice;

	for(int i=0;i<MAX;i++){
		for(int j=0;j<MAX;j++){
			if(choice==t.board[i][j])
				t.board[i][j]='X';
		}
	}
	t.check_win();
	return in;
}
ostream& operator<<(ostream& out, TTT& t){
	for(int i=0;i<MAX;i++){
		out<<"\t";
		for(int j=0;j<MAX;j++){
			out<<t.board[i][j]<<" ";
		}
	out<<endl;
	}
	return out;
}

1. on line 117, if a player wants to start a new game, how do I clear the old object and start fresh?


An object can only be constructed once. However if you want to "reset" an object, you can destruct it and reassign it to a new one. This is easily accomplished with this:

game = TTT();

TTT(); makes a new object, and assigning it to game copies the new object to game (erasing/destructing the previous game).

2. I have not had much experience with random numbers, but sometimes around line 55 I get a division by zero error message. No idea why this happens.


% gives you the remainder after division. If you say rand() % 5... rand() gives you a random number, and % 5 takes that number, divides it by 5, and gives you back the remainder effectively giving you a random number between [0..4] (since no number larger than 4 could possibly be the remainder of /5)

If do rand() % 0, that's a division by zero. If you're getting that error on line 55, then that means your vector is empty. It looks like this could happen if you call computer_turn when the board is completely full.



3. The most frustrating, but so close. Line 150! When a player or computer chooses a square, it goes into a memory array, then the program checks if the number exists in the array and if it does, does not allow user to enter. It works most of the time, but sometimes it skips and does not work properly. I spent a while on this but with no luck. Please assist.


What purpose does the for loop on line 148 serve? Why do you need to check every square when you know which square the user is trying to set?

There's no need for the nested loop there. Get rid of the for loop and simply check the one square the user is asking about.


EDIT:

okay I just realized why this might be tricky.

The user is inputting his choice in the form of a char, and you need to check to see if the spot on the board that char represents contains that char. The problem is you can't use the char as-is to index your array, because the array needs to take a zero based index.

The trick here is that you can easily convert a numerical char (ie, '6', '3', etc) to an integral value (ie 6, 3, etc) by subtracting the character '0':

1
2
3
4
5
char one = '1';
cout << one;  // prints the character '1'

int numone = one - '0';
cout << numone;  // prints the number '1' 


So if the user inputs '5', you can subtract '0' from that to get 5, which you can then use to index your board array. HOWEVER, since user input starts at 1, but the board indexes start at 0, you probably want to subtract '1' instead (to convert '5' to 4, making the index zero based).

Also, be sure to watch for out of bounds input. IE: after conversion, you should have a value between [0..8]

/EDIT

4. Looking for criticism. If I am doing something wrong, or if you have a better way of doing things, would love to hear it so I can become a better programer. Please keep in mind I am still a noob.


Overall I'd say it's very good for a beginner program. Very well organized, easy to follow, and to the point.

The only parts that strike me as 'bad' are in check_win:

a) duplicating the win conditions for X and O is "blech". I would make a separate function that checks for a win and call it twice... once for X and once for O.

b) Not every 2-state variable should be a boolean. It's unintuitive that winner=true means X won and winner=false means O won. If I see winner=false, I would think that means the game is a tie. A more sensible approach would have winner be a char set to X or O to indicate the winner. Or better yet, change it to a game status with 4 states:
X = game over, X won
O = game over, O won
T = game over, tie
[space] = game still in progress

Or even better yet, use an enum rather than magic characters.

5. If you have any good ideas for AI, something more advanced than my random, would like to hear it as well.


A minimally intelligent AI would complete its 3rd move for the win if it has 2 in a row and an open 3rd space. It also probably would want to look for the player having 2 in a row and then block him.

If you want to get really smart, you can have it look for possible "forks"/"traps" (ie: getting 2 in a row in multiple ways, so that even if the player blocks one, it can take the other).
Last edited on
Thanks a lot for the very detailed help. I will take everything you said into consideration and attempt the rewrite the program. I will try to code the "AI" as you suggest as well. However, I am leaving town for three days so I will have to pick this up again on Tuesday.

Thanks a lot again, much appreciated!!

Mike
Also on a side note, do you really need ALL of those headers? And if you declare using namespace std; you don't need the other using statements.
Those are from other exercises programs etc I have attempted. I always keep this just in case but you are right, I can do without.

Thanks
Topic archived. No new replies allowed.