Sign in to follow this  
agm_ultimatex

cant seem to get players decision working - tictactoe

Recommended Posts

This feels like a dumb logical error that im missing, but I cannot seem to figure it out at the moment. I have a 2d array that is filled with 1-9 as a tic tac toe board. I loop through it with two for loops, and compare the number the user entered in with, with the value. However, this if statement to set the new value of the board does not ever meet a condition. It would be at the end of TakeATurn function Heres my code:
#include <iostream>
#include <string>
#include <vector>
#include <sstream>

typedef std::string TicTacToeBoard[3][3];

class Game 
{
public:
	Game(){}
	
	void FillTheBoard();
	void DrawBoard();
	void StartGame();
	void TakeATurn();
	void SetIsPlaying(bool isPlaying);
	void CheckForAWinner();
	void GiveInstructions();
	bool CheckAvailability(int place);
	void ComputerTakeTurn();

private:
	TicTacToeBoard theBoard;
	bool m_isPlaying;
};

void Game::StartGame() 
{
	std::cout << "Welcome to another Tic-Tac-Toe Clone!\n";
	std::cout << "To play: specify the space in the board where you wish to place your move\n";
	FillTheBoard();
	GiveInstructions();
	do
	{
		TakeATurn();
		//ComputerTakeTurn();
		DrawBoard();
		CheckForAWinner();
	} while(m_isPlaying);
}

void Game::FillTheBoard()
{
	int counter = 1;
	for(int i = 0; i < 3; i++)
	{
		for(int j = 0; j < 3; j++)
		{
			std::stringstream stm;
			stm << counter;
			theBoard[i][j] = stm.str();
			counter++;
		}
	}
}
void Game::DrawBoard()
{
	std::cout << "\n " << theBoard[0][0] << " | " << theBoard[0][1] << " | " << theBoard[0][2] << " " << "\n";
	std::cout << "-----------\n";
	std::cout << " " << theBoard[1][0] << " | " << theBoard[1][1] << " | " << theBoard[1][2] << " " << "\n";
	std::cout << "-----------\n";
	std::cout << " " << theBoard[2][0] << " | " << theBoard[2][1] << " | " << theBoard[2][2] << " " << "\n";
}

void Game::SetIsPlaying(bool isPlaying)
{
	m_isPlaying = isPlaying;
}

void Game::TakeATurn()
{
	bool validSpot = false;
	int placeAt = 0;
	do 
	{
		std::cout << "Please specify a value of where you wish to place X\n";
		std::string input;
		std::getline(std::cin, input);
		int placeAt = atoi(input.c_str());
		validSpot = CheckAvailability(placeAt);
		if(!validSpot)
		{
			std::cout << "Please enter a spot that has not been taken yet.\n";
		}
	} while(((placeAt < 1) && (placeAt > 9)) || !validSpot);

	// add the user's choice to the board
	for(int i = 0; i < 3; i++)
	{
		for(int j = 0; j < 3; j++)
		{
			if(atoi(theBoard[i][j].c_str()) == placeAt) 
			{
				theBoard[i][j] = "X";
			}
		}
	}	
}

void Game::CheckForAWinner() 
{

}

void Game::GiveInstructions() 
{
	std::cout << "To put a piece at the first spot on row 1, type 1. For the next one, type 2, than 3.\n";
	std::cout << "For the second row, first one type 4, the second one 5, and etc.\n";
	std::cout << "such as below:\n";
	DrawBoard();
}

bool Game::CheckAvailability(int place)
{
	bool isAvailable = false;
	for(int i = 0; i < 3; i++)
	{
		for(int j = 0; j < 3; j++)
		{
			if(atoi(theBoard[i][j].c_str()) == place) 
			{
				isAvailable = true;
			}
		}
	}
	return isAvailable;
}

void Game::ComputerTakeTurn() 
{
	for(int i = 0; i < 3; i++)
	{
		for(int j = 0; j < 3; j++)
		{
			if(theBoard[i][j] != "X") 
			{
				theBoard[i][j] = "O";
				break;
			}
		}
	}
}

int main() 
{
	Game g;
	g.StartGame();
}


Share this post


Link to post
Share on other sites
Edit: I need to read the code a little better, =p

if(atoi(theBoard[i][j].c_str()) == place)

your checking the string if its equal to a int, if the previous section you take the string and convert it to a int, as the poster below said this is probably the cause of your problem.

[Edited by - yewbie on June 20, 2009 7:26:51 PM]

Share this post


Link to post
Share on other sites
just as a question why are you bothering with atoi? surely you can compare all the required characters as strings instead of converting some to int. I'm a noob but that seems to add an unnecessary level of complexity to the code that could be causing your issue. Please do explain your reason I would love to know.

Share this post


Link to post
Share on other sites
tbh, i really dont know lol. I guess it just kind of made sense that they enter a number placement of where to put it, so i put the variable types as integers.

But yeah, I fixed it now. I think something was wrong with my CheckAvailability()

Share this post


Link to post
Share on other sites
No, (placeAt > 1) && (placeAt < 9) is wrong. Basically it's a loop that checks to see if the user's input is valid. If it's not, it continues. But the while statement is wrong. You should be using ||, not &&, so it should be (placeAt < 1) || (placeAt > 9) || !validSpot.

The rest looks ok, but you should seriously reconsider you usage of atoi. Why do you set each board tile equal to it's numerical number? You have a design flaw, but I'm too rushed to help more. I'd be happy to later.

Share this post


Link to post
Share on other sites
Thanks mike.

I initially had them all set to a hyphen. Then i decided to populate them with their value, rather than have another function that displays the instruction board (shows all the numbers so the user know what one to type). It's also easier to have an if statement to check values, rather than coordinate the one number they enter with the 2 dimensional array (at least I think so anyways).

Share this post


Link to post
Share on other sites
Quote:
Original post by agm_ultimatex
Thanks mike.

I initially had them all set to a hyphen. Then i decided to populate them with their value, rather than have another function that displays the instruction board (shows all the numbers so the user know what one to type). It's also easier to have an if statement to check values, rather than coordinate the one number they enter with the 2 dimensional array (at least I think so anyways).

You can have their numbers there and still not use atoi. If a space has been used, it will be either 'X' or 'O'. So you can just do theBoard[i][j] == "X" || theBoard[i][j] == "O" and get rid of messy atoi-ness.

Share this post


Link to post
Share on other sites
yeah i did, it is a lot nicer now actually. Currently trying to think of the best way to determine a winner. I made a local array of integers storing all the possible winning scenarios. Its an 8x6 due to the fact 2 numbers determine a spot on the board, and there are 8 possible ways to win. However, im trying to get my head around how to loop through it, as a nested for loop will only bring back 1 number per iteration, i need 2. I realize I can probably store it, but I imagine there's a better way.

Heres my CheckForWinner() function as is, and the rest of the code afterwards


void Game::CheckForAWinner()
{
int winningCombos[8][6] = {
{0, 0, 0, 1, 0, 2},
{1, 0, 1, 1, 1, 2},
{2, 0, 2, 1, 2, 2},
{0, 0, 1, 0, 2, 0},
{0, 1, 1, 1, 2, 1},
{0, 2, 1, 2, 2, 2},
{0, 0, 1, 1, 2, 2},
{0, 2, 1, 1, 2, 0}
};
}




#include <iostream>
#include <string>
#include <vector>
#include <sstream>

typedef std::string TicTacToeBoard[3][3];

class Game
{
public:
Game(){}

void FillTheBoard();
void DrawBoard();
void StartGame();
void TakeATurn();
void SetIsPlaying(bool isPlaying);
void CheckForAWinner();
void GiveInstructions();
bool CheckAvailability(std::string place);
void ComputerTakeTurn();

private:
TicTacToeBoard theBoard;
bool m_isPlaying;
};

void Game::StartGame()
{
std::cout << "Welcome to another Tic-Tac-Toe Clone!\n";
std::cout << "To play: specify the space in the board where you wish to place your move\n";
FillTheBoard();
GiveInstructions();
do
{
TakeATurn();
ComputerTakeTurn();
DrawBoard();
CheckForAWinner();
} while(m_isPlaying);
}

void Game::FillTheBoard()
{
int counter = 1;
for(int i = 0; i < 3; i++)
{
for(int j = 0; j < 3; j++)
{
std::stringstream stm;
stm << counter;
theBoard[i][j] = stm.str();
counter++;
}
}
}
void Game::DrawBoard()
{
std::cout << "\n " << theBoard[0][0] << " | " << theBoard[0][1] << " | " << theBoard[0][2] << " " << "\n";
std::cout << "-----------\n";
std::cout << " " << theBoard[1][0] << " | " << theBoard[1][1] << " | " << theBoard[1][2] << " " << "\n";
std::cout << "-----------\n";
std::cout << " " << theBoard[2][0] << " | " << theBoard[2][1] << " | " << theBoard[2][2] << " " << "\n";
}

void Game::SetIsPlaying(bool isPlaying)
{
m_isPlaying = isPlaying;
}

void Game::TakeATurn()
{
bool validSpot = false;
std::string input;
do
{
std::cout << "Please specify a value of where you wish to place X\n";
std::getline(std::cin, input);
validSpot = CheckAvailability(input);
if(!validSpot)
{
std::cout << "Please enter a spot that has not been taken yet.\n";
}
} while (((atoi(input.c_str()) < 1) || (atoi(input.c_str()) > 9)) || !validSpot);

// add the user's choice to the board
for(int i = 0; i < 3; i++)
{
for(int j = 0; j < 3; j++)
{
if(theBoard[i][j] == input)
{
theBoard[i][j] = "X";
}
}
}
}

void Game::CheckForAWinner()
{
int winningCombos[8][6] = {
{0, 0, 0, 1, 0, 2},
{1, 0, 1, 1, 1, 2},
{2, 0, 2, 1, 2, 2},
{0, 0, 1, 0, 2, 0},
{0, 1, 1, 1, 2, 1},
{0, 2, 1, 2, 2, 2},
{0, 0, 1, 1, 2, 2},
{0, 2, 1, 1, 2, 0}
};
}

void Game::GiveInstructions()
{
std::cout << "To put a piece at the first spot on row 1, type 1. For the next one, type 2, than 3.\n";
std::cout << "For the second row, first one type 4, the second one 5, and etc.\n";
std::cout << "such as below:\n";
DrawBoard();
}

bool Game::CheckAvailability(std::string place)
{
bool isAvailable = false;
for(int i = 0; i < 3; i++)
{
for(int j = 0; j < 3; j++)
{
if(theBoard[i][j] == place)
{
isAvailable = true;
}
}
}
return isAvailable;
}

void Game::ComputerTakeTurn()
{
for(int i = 0; i < 3; i++)
{
for(int j = 0; j < 3; j++)
{
if(theBoard[i][j] != "X" && theBoard[i][j] != "O")
{
theBoard[i][j] = "O";
break;
}
}
break;
}
}

int main()
{
Game g;
g.StartGame();
}


Share this post


Link to post
Share on other sites

for (i = 0; i &lt; 3; i++)
{
if (board[i][0] == "X" && board[i][1] == "X" && board[i][2] == "X")
playerWins = true;
}
for (i = 0; i &lt; 3; i++)
{
if (board[0][i] == "X" && board[1][i] == "X" && board[2][i] == "X")
plyaerWin = true;
}
if (board[0][0] == "X" && board[1][1] == "X" && board[2][2] == "X")
playerWin = true;
if (board[0][2] == "X" && board[1][1] == "X" && board[2][0] == "X")
playerWin = true;



then change conditions for "O" win for the computer and run it again

Share this post


Link to post
Share on other sites
Thanks, that works.

Heres my finished code (it seems impossible to lose, id like to actually have an smart computer for this rather than just putting an O in the first available spot). Feel free to comment for suggestions on how to improve it and so forth practice wise and what not. I mainly did this as a quick test, as I have done more complicated games than this before.


#include <iostream>
#include <string>
#include <vector>
#include <sstream>

typedef std::string TicTacToeBoard[3][3];

class Game
{
public:
Game(){}

void FillTheBoard();
void DrawBoard();
void StartGame();
void TakeATurn();
void SetIsPlaying(bool isPlaying);
bool IsThereAWinner();
void GiveInstructions();
bool CheckAvailability(std::string place);
void ComputerTakeTurn();
void PlayAgain();
private:
TicTacToeBoard theBoard;
bool m_isPlaying;
};

void Game::StartGame()
{
std::cout << "Welcome to another Tic-Tac-Toe Clone!\n";
std::cout << "To play: specify the space in the board where you wish to place your move\n";
FillTheBoard();
GiveInstructions();
do
{
bool gameInPlay = true;
int iterations = 0;
do
{
TakeATurn();
// the user can only put up to 5 times, so count it
iterations++;
ComputerTakeTurn();
DrawBoard();
gameInPlay = IsThereAWinner();
} while(gameInPlay || iterations == 5);
if(iterations == 5 && gameInPlay)
{
std::cout << "Stalemate!\n";
}
PlayAgain();
}while(m_isPlaying);
}

void Game::FillTheBoard()
{
int counter = 1;
for(int i = 0; i < 3; i++)
{
for(int j = 0; j < 3; j++)
{
std::stringstream stm;
stm << counter;
theBoard[i][j] = stm.str();
counter++;
}
}
}
void Game::DrawBoard()
{
std::cout << "\n " << theBoard[0][0] << " | " << theBoard[0][1] << " | " << theBoard[0][2] << " " << "\n";
std::cout << "-----------\n";
std::cout << " " << theBoard[1][0] << " | " << theBoard[1][1] << " | " << theBoard[1][2] << " " << "\n";
std::cout << "-----------\n";
std::cout << " " << theBoard[2][0] << " | " << theBoard[2][1] << " | " << theBoard[2][2] << " " << "\n";
}

void Game::SetIsPlaying(bool isPlaying)
{
m_isPlaying = isPlaying;
}

void Game::TakeATurn()
{
bool validSpot = false;
std::string input;
do
{
std::cout << "Please specify a value of where you wish to place X\n";
std::getline(std::cin, input);
validSpot = CheckAvailability(input);
if(!validSpot)
{
std::cout << "Please enter a spot that has not been taken yet.\n";
}
} while (((atoi(input.c_str()) < 1) || (atoi(input.c_str()) > 9)) || !validSpot);

// add the user's choice to the board
for(int i = 0; i < 3; i++)
{
for(int j = 0; j < 3; j++)
{
if(theBoard[i][j] == input)
{
theBoard[i][j] = "X";
}
}
}
}

bool Game::IsThereAWinner()
{
/*int winningCombos[8][6] = {
{0, 0, 0, 1, 0, 2},
{1, 0, 1, 1, 1, 2},
{2, 0, 2, 1, 2, 2},
{0, 0, 1, 0, 2, 0},
{0, 1, 1, 1, 2, 1},
{0, 2, 1, 2, 2, 2},
{0, 0, 1, 1, 2, 2},
{0, 2, 1, 1, 2, 0}
}; */


bool playerWins = false;
bool computerWins = false;

for (int i = 0; i < 3; i++)
{
if (theBoard[i][0] == "X" && theBoard[i][1] == "X" && theBoard[i][2] == "X")
{
playerWins = true;
}
}
for (int i = 0; i < 3; i++)
{
if (theBoard[0][i] == "X" && theBoard[1][i] == "X" && theBoard[2][i] == "X")
{
playerWins = true;
}
}
if (theBoard[0][0] == "X" && theBoard[1][1] == "X" && theBoard[2][2] == "X")
{
playerWins = true;
}
else if (theBoard[0][2] == "X" && theBoard[1][1] == "X" && theBoard[2][0] == "X")
{
playerWins = true;
}
// checks if computer won
for (int i = 0; i < 3; i++)
{
if (theBoard[i][0] == "O" && theBoard[i][1] == "O" && theBoard[i][2] == "O")
{
computerWins = true;
}
}
for (int i = 0; i < 3; i++)
{
if (theBoard[0][i] == "O" && theBoard[1][i] == "O" && theBoard[2][i] == "O")
{
computerWins = true;
}
}
if (theBoard[0][0] == "O" && theBoard[1][1] == "O" && theBoard[2][2] == "O")
{
computerWins = true;
}
else if (theBoard[0][2] == "O" && theBoard[1][1] == "O" && theBoard[2][0] == "O")
{
computerWins = true;
}

if(playerWins)
{
std::cout << "You won the game!\n";
return false;
}
else if(computerWins)
{
std::cout << "The computer beat you, too bad :( \n";
return false;
}
else
{
return true;
}
}

void Game::GiveInstructions()
{
std::cout << "To put a piece at the first spot on row 1, type 1. For the next one, type 2, than 3.\n";
std::cout << "For the second row, first one type 4, the second one 5, and etc.\n";
std::cout << "such as below:\n";
DrawBoard();
}

bool Game::CheckAvailability(std::string place)
{
bool isAvailable = false;
for(int i = 0; i < 3; i++)
{
for(int j = 0; j < 3; j++)
{
if(theBoard[i][j] == place)
{
isAvailable = true;
}
}
}
return isAvailable;
}

void Game::ComputerTakeTurn()
{
for(int i = 0; i < 3; i++)
{
for(int j = 0; j < 3; j++)
{
if(theBoard[i][j] != "X" && theBoard[i][j] != "O")
{
theBoard[i][j] = "O";
i = 3;
j = 3;
}
}
}
}

void Game::PlayAgain()
{
std::string ans;
do
{
std::cout << "Play again?y/n\n";
std::getline(std::cin, ans);
} while(ans != "n" && ans != "N" && ans != "y" && ans != "Y");
if(ans == "n" || ans == "N")
{
m_isPlaying = false;
}
else
{
FillTheBoard();
}
}

int main()
{
Game g;
g.StartGame();
}

Share this post


Link to post
Share on other sites
short of developing a simple AI a random move is better, just randomly generate a number 1-9 then check to see if it is free if not free regenerate number etc

Share this post


Link to post
Share on other sites
1) An important general design principle: Put easy-to-work-with values in your underlying data, and convert them into easy-to-look-at values in the I/O, instead of trying to work with easy-to-look-at values directly.

In this case, that means:

- Use an enumeration for the cell type instead of std::string: a value to indicate X, a value to indicate O, and a value to indicate an empty cell.

- When you draw the board, look up the value for a given cell and translate it into the appropriate character to draw: 'X' or 'O' for played cells, and a digit '1' through '9' - according to what position the cell is in - for empty cells.

- When you get the player's input, translate it into a cell index (by converting it into an integer - and use stringstream for this, please), but then when you look at the corresponding cell, you just have to check if it's empty or not.

2) Similarly, to avoid your problems with the check-for-win function, try just making the array 1-dimensional (of size 9), and inserting the line breaks at the appropriate places when you draw the board.

Share this post


Link to post
Share on other sites

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