Sign in to follow this  

Optimize Tic Tac Toe

This topic is 2813 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Today I finished my first game in console with AI - Tic Tac Toe, however to finish it I had to write bunch of code in AImove function. Can anyones suggest me what simple techniques to use to shorten this code(especially AImove function)? You don't have to write code what I need, just suggest me something, I need to think about this :) Here's the code:
#include<iostream>
#include<vector>

using namespace std;

const char O = 'O';
const char X = 'X';
const char E = ' ';

void humanMove(vector<char> &b);
void AImove(vector<char> &b);
void inits();
bool win(vector<char> &b, char m);
void drawBoard(vector<char> &b);
int main()
{
	vector <char> board(9,' ');
	inits();
	while(win(board,O) == false)
	{
		humanMove(board);
		drawBoard(board);
		win(board,X);
		AImove(board);
		drawBoard(board);
		win(board,O);
	}
}
void inits()
{
	cout <<"\t\tWelcome to ultimate Tic-Tac-Toe challenge!\n\n";
	cout <<"To do your turn follow these instructions:\n";
	cout <<" 0 || 1 || 2\n 3 || 4 || 5\n 6 || 7 || 8\n";
}
void humanMove(vector<char> &b)
{
	int move;
m:
	cout <<"Enter your move: ";
	cin >> move;
	cout <<"\n";
	//check if move is legal
	if(b[move] != O)
    b[move] = X;	
	else
	{
		cout <<"Move is\n illegal!\n"; goto m;
	}
}
void drawBoard(vector<char> &b)
{
	cout <<"\n\n";
	cout << " "<<b[0]<<"||"<<b[1]<<"||"<<b[2]<<"\n";
	cout <<"--------\n";
	cout << " "<<b[3]<<"||"<<b[4]<<"||"<<b[5]<<"\n";
	cout <<"--------\n";
	cout << " "<<b[6]<<"||"<<b[7]<<"||"<<b[8]<<"\n";
}
bool win(vector<char> &b, char m)
{
	//check for win
	if(b[0] == m && b[1] == m && b[2] == m)
	{
		cout << m <<" wins!!!"; return true;
	}
	else if(b[3] == m && b[4] == m && b[5] == m)
	{
		cout << m <<" wins!!!"; return true;
	}
	else if(b[6] == m && b[7] == m && b[8] == m)
	{
		cout << m <<" wins!!!"; return true;
	}
	else if(b[0] == m && b[3] == m && b[6] == m)
	{
		cout << m <<" wins!!!"; return true;
	}
	else if(b[1] == m && b[4] == m && b[7] == m)
	{
		cout << m <<" wins!!!"; return true;
	}
	else if(b[2] == m && b[5] == m && b[8] == m)
	{
		cout << m <<" wins!!!"; return true;
	}
	else if(b[2] == m && b[4] == m && b[6] == m)
	{
		cout << m <<" wins!!!"; return true;
	}
	else if(b[0] == m && b[4] == m && b[8] == m)
	{
		cout << m <<" wins!!!"; return true;
	}
	//chec for tie
	else if(b[0] !=E && b[1] !=E && b[2] !=E && b[3] !=E && b[4] !=E && b[5] !=E && b[6] !=E && b[7] !=E && b[8] !=E)
	{
		cout << " TIE!!!"; return true;
	}
	return false;
}
void AImove(vector<char> &b)
{
	//if can win in one turn
	//012
	if(b[0] == O && b[1] == O && b[2] == E){
		b[2] = O; return;}
	else if(b[0] == O && b[2] == O && b[1] == E){
		b[1] = O; return;}
	else if(b[2] == O && b[1] == O && b[0] == E){
		b[0] = O; return;}
	//345
	else if(b[3] == O && b[4] == O && b[5] == E){
		b[5] = O; return;}
	else if(b[3] == O && b[5] == O && b[4] == E){
		b[4] = O; return;}
	else if(b[5] == O && b[4] == O && b[3] == E){
		b[3] = O; return;}
	//678
	else if(b[6] == O && b[7] == O && b[8] == E){
		b[8] = O; return;}
	else if(b[6] == O && b[8] == O && b[7] == E){
		b[7] = O; return;}
	else if(b[8] == O && b[7] == O && b[6] == E){
		b[6] = O; return;}
	//036
	else if(b[0] == O && b[3] == O && b[6] == E){
		b[6] = O; return;}
	else if(b[0] == O && b[6] == O && b[3] == E){
		b[3] = O; return;}
	else if(b[3] == O && b[6] == O && b[0] == E){
		b[0] = O; return;}
	//147
	else if(b[0] == O && b[1] == O && b[2] == E){
		b[2] = O; return;}
	else if(b[0] == O && b[1] == O && b[2] == E){
		b[2] = O; return;}
	else if(b[0] == O && b[1] == O && b[2] == E){
		b[2] = O; return;}
	//258
	else if(b[2] == O && b[5] == O && b[8] == E){
		b[8] = O; return;}
	else if(b[2] == O && b[8] == O && b[5] == E){
		b[5] = O; return;}
	else if(b[8] == O && b[5] == O && b[2] == E){
		b[2] = O; return;}
	//246
	else if(b[4] == O && b[6] == O && b[2] == E){
		b[2] = O; return;}
	else if(b[2] == O && b[4] == O && b[6] == E){
		b[6] = O; return;}
	else if(b[6] == O && b[2] == O && b[4] == E){
		b[4] = O; return;}
	//048
	else if(b[0] == O && b[4] == O && b[8] == E){
		b[8] = O; return;}
	else if(b[0] == O && b[8] == O && b[4] == E){
		b[4] = O; return;}
	else if(b[8] == O && b[4] == O && b[0] == E){
		b[0] = O; return;}
	//if human can win in one turn
	//012
	if(b[0] == X && b[1] == X && b[2] == E){
		b[2] = O; return;}
	else if(b[0] == X && b[2] == X && b[1] == E){
		b[1] = O; return;}
	else if(b[2] == X && b[1] == X && b[0] == E){
		b[0] = O; return;}
	//345
	else if(b[3] == X && b[4] == X && b[5] == E){
		b[5] = O; return;}
	else if(b[3] == X && b[5] == X && b[4] == E){
		b[4] = O; return;}
	else if(b[5] == X && b[4] == X && b[3] == E){
		b[3] = O; return;}
	//678
	else if(b[6] == X && b[7] == X && b[8] == E){
		b[8] = O; return;}
	else if(b[6] == X && b[8] == X && b[7] == E){
		b[7] = O; return;}
	else if(b[8] == X && b[7] == X && b[6] == E){
		b[6] = O; return;}
	//036
	else if(b[0] == X && b[3] == X && b[6] == E){
		b[6] = O; return;}
	else if(b[0] == X && b[6] == X && b[3] == E){
		b[3] = O; return;}
	else if(b[3] == X && b[6] == X && b[0] == E){
		b[0] = O; return;}
	//147
	else if(b[0] == X && b[1] == X && b[2] == E){
		b[2] = O; return;}
	else if(b[0] == X && b[1] == X && b[2] == E){
		b[2] = O; return;}
	else if(b[0] == X && b[1] == X && b[2] == E){
		b[2] = O; return;}
	//258
	else if(b[2] == X && b[5] == X && b[8] == E){
		b[8] = O; return;}
	else if(b[2] == X && b[8] == X && b[5] == E){
		b[5] = O; return;}
	else if(b[8] == X && b[5] == X && b[2] == E){
		b[2] = O; return;}
	//246
	else if(b[4] == X && b[6] == X && b[2] == E){
		b[2] = O; return;}
	else if(b[2] == X && b[4] == X && b[6] == E){
		b[6] = O; return;}
	else if(b[6] == X && b[2] == X && b[4] == E){
		b[4] = O; return;}
	//048
	else if(b[0] == X && b[4] == X && b[8] == E){
		b[8] = O; return;}
	else if(b[0] == X && b[8] == X && b[4] == E){
		b[4] = O; return;}
	else if(b[8] == X && b[4] == X && b[0] == E){
		b[0] = O; return;}
	//other turns
	else if(b[4] == E){
		b[4] = O; return;}
	else if(b[0] == E){
		b[0] = O; return;}
	else if(b[2] == E){
		b[2] = O; return;}
	else if(b[6] == E){
		b[6] = O; return;}
	else if(b[8] == E){
		b[8] = O; return;}
	else if(b[1] == E){
		b[1] = O; return;}
	else if(b[3] == E){
		b[3] = O; return;}
	else if(b[5] == E){
		b[5] = O; return;}
	else if(b[7] == E){
		b[7] = O; return;}
}

Share this post


Link to post
Share on other sites
The win function has a lot of code duplication. Here is a first attempt to make it shorter:

#include <algorithm>

bool win(vector<char> &b, char m)
{
static int lines[][3] = {
{0, 1, 2}, {3, 4, 5}, {6, 7, 8},
{0, 3, 6}, {1, 4, 7}, {2, 5, 8},
{0, 4, 8}, {2, 4, 6}
};

for (int i = 0; i < 8; ++i)
{
if (b[lines[i][0]] == m
&& b[lines[i][1]] == m
&& b[lines[i][2]] == m)
{
cout << m << " wins!!!";
return true;
}
}

if (std::count(b.begin(), b.end(), E) == 0)
{
cout << " TIE!!!";
return true;
}

return false;
}


By the way, the human player shouldn't be able to make the same move twice ;)

Share this post


Link to post
Share on other sites
And here is a more "STL-oriented" approach:

#include <algorithm>

class line_finder
{
vector<char>& b;
char m;

public:

line_finder(vector<char>& b, char m) : b(b), m(m) {}

bool operator()(int* line)
{
return b[line[0]] == m
&& b[line[1]] == m
&& b[line[2]] == m;
}
};

bool win(vector<char> &b, char m)
{
static int lines[][3] = {
{0, 1, 2}, {3, 4, 5}, {6, 7, 8},
{0, 3, 6}, {1, 4, 7}, {2, 5, 8},
{0, 4, 8}, {2, 4, 6}
};

if (std::find_if(lines, lines + 8, line_finder(b, m)) != lines + 8)
{
cout << m << " wins!!!";
return true;
}

if (std::count(b.begin(), b.end(), E) == 0)
{
cout << " TIE!!!";
return true;
}

return false;
}


Sooner or later, Zahlman will jump in and tell you about const correctness ;)

Share this post


Link to post
Share on other sites
I don't want it to be initialized every time the win function is entered. I could have made it a global variable, but then every other function would unnecessarily have access to it.

Share this post


Link to post
Share on other sites
ok, now AImove function is way shorter :)


void AImove(vector<char> &b)
{
static int lines[][3] = {
{0, 1, 2}, {3, 4, 5}, {6, 7, 8},
{0, 3, 6}, {1, 4, 7}, {2, 5, 8},
{0, 4, 8}, {2, 4, 6}
};
//if AI can win in one turn
for (int i = 0; i < 8; ++i)
{
if (b[lines[i][0]] == O
&& b[lines[i][1]] == O
&& b[lines[i][2]] == E)
{
b[lines[i][2]] = O; return;
}
else if(b[lines[i][0]] == E
&& b[lines[i][1]] == O
&& b[lines[i][2]] == O)
{
b[lines[i][0]] = O; return;
}
else if(b[lines[i][0]] == O
&& b[lines[i][1]] == E
&& b[lines[i][2]] == O)
{
b[lines[i][1]] = O; return;
}
}
//if human can win in one turn
for (int i = 0; i < 8; ++i)
{
if (b[lines[i][0]] == X
&& b[lines[i][1]] == X
&& b[lines[i][2]] == E)
{
b[lines[i][2]] = O; return;
}
else if(b[lines[i][0]] == E
&& b[lines[i][1]] == X
&& b[lines[i][2]] == X)
{
b[lines[i][0]] = O; return;
}
else if(b[lines[i][0]] == X
&& b[lines[i][1]] == E
&& b[lines[i][2]] == X)
{
b[lines[i][1]] = O; return;
}
}
//other turns
if(b[4] == E){
b[4] = O; return;}
else if(b[0] == E){
b[0] = O; return;}
else if(b[2] == E){
b[2] = O; return;}
else if(b[6] == E){
b[6] = O; return;}
else if(b[8] == E){
b[8] = O; return;}
else if(b[1] == E){
b[1] = O; return;}
else if(b[3] == E){
b[3] = O; return;}
else if(b[5] == E){
b[5] = O; return;}
else if(b[7] == E){
b[7] = O; return;}
}

Share this post


Link to post
Share on other sites
It has nothing to do with speed. There's just no point to declare the same data twice. By the way, it's also a good idea to make the array const, so you can't modify it by mistake.

const int lines[][3] = {
{0, 1, 2}, {3, 4, 5}, {6, 7, 8},
{0, 3, 6}, {1, 4, 7}, {2, 5, 8},
{0, 4, 8}, {2, 4, 6}
};

bool win(vector<char> &b, char m)
{
// etc.

Share this post


Link to post
Share on other sites
Instead of making the move within the function, you can have it return the move to make. Then you only have to write the "put the AI's move into the array" part once: at the place where you call the function.

Also, there are a bunch more ways to avoid repeating work. Searching for a spot where the AI can win is the same as searching for a spot where the player can win, basically, so we make a function for that. If there are no such places, the logic is basically picking the first available spot from a list of options, so we can simplify that by making the list and searching through it. Checking whether a row has one empty spot and two matching symbols can also be done with a loop; we loop over the indices to check for an empty spot, and look for matching symbols in the other spots.


typedef int line_type[3];

const line_type lines[] = {
{0, 1, 2}, {3, 4, 5}, {6, 7, 8},
{0, 3, 6}, {1, 4, 7}, {2, 5, 8},
{0, 4, 8}, {2, 4, 6}
};
int line_count = sizeof(lines) / sizeof(lines[0]);

int can_win(vector<char>& b, char who) {
// Return a spot where the indicated player can win immediately, if there
// is one; otherwise return -1.
for (int i = 0; i < line_count; ++i) {
line_type& line = lines[i];
for (int j = 0; j < 3; ++j) {
if (
b[line[j]] == E &&
b[line[(j + 1) % 3]] == who &&
b[line[(j + 2) % 3]] == who
) { return j; }
}
}
return -1;
}

int AImove(vector<char> &b) {
// If AI can move in one turn
int AI_winning_spot = can_win(b, O);
if (AI_winning_spot != -1) { return AI_winning_spot; }
//if human can win in one turn
int human_winning_spot = can_win(b, X);
if (human_winning_spot != -1) { return human_winning_spot; }

// Otherwise, pick the first spot in the preference list.
int move_preferences[] = { 4, 0, 2, 6, 8, 1, 3, 5, 7 };
for (int i = 0; i < 9; ++i) {
int move = move_preferences[i];
if (b[move] == E) { return move; }
}

assert(false); // should not be possible to get here
}


Share this post


Link to post
Share on other sites

This topic is 2813 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this