Sign in to follow this  

My Tic-Tac-Toe

This topic is 4717 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

Hello there everyone. Here is my tic-tac-toe game. It is all finished, except the graphics and the AI, It wont work as of now because of both hehe. Take a look at my code and give me some feedback please.
#include <iostream>
//globals
bool playing;
bool myTurn;
bool placed;
int x, y, ox, oy; //ox and oy are where the opponent is placing a piece.
bool win, lose;
//cell class
class cell {
  public:
    enum token {EMPTY, X, O};
    token cellStatus;
    cell() : cellStatus(EMPTY) {};
};
//board class
class board {
  private:
    cell cells[3][3];
  public:
    int checkStatus(int x, int y);
    void setStatusX(int x, int y);
    void setStatusO(int x, int y);
    void printCannotPlace();
};
//class definitions
int board::checkStatus(int x, int y) {
  return cells[x][y].cellStatus;
}
void board::setStatusX(int x, int y) {
  if(cells[x][y].cellStatus == cell::EMPTY)
    cells[x][y].cellStatus = cell::X;
  else
    printCannotPlace();
}
void board::setStatusO(int x, int y) {
  if(cells[x][y].cellStatus == cell::EMPTY)
    cells[x][y].cellStatus = cell::O;
  else
    printCannotPlace();
}
void board::printCannotPlace() {
  std::cout<<"Cannot place a piece there."<<std::endl;
}
//function declarations
void gameLoop();
int checkGameStatus();
void getInput();
void opponentAI();
board myBoard;
int main() {
//notice how theres nothing here, poor main =( 
}
void getInput() {
  std::cout<<"Where do you want to place?  X/Y\n"
  std::cin>>x>>y;
}
void gameLoop() {
  playing = true;
  while(playing == true) {
    myTurn = true;
    while(myTurn == true) {
      getInput();
      myBoard.placeX(x, y);
      myTurn = false;
    }
    while(myTurn == false) {
      opponentAI();
      myBoard.placeO(ox, oy);
      myTurn = true;
    }
    checkGameStatus();
    if(win == true) {
      std::cout<<"You won! Good job!\n";
      playing = false;
    }
    if(lose == true) {
      std::cout<<"You lose =(. Too bad.\n";
      playing = false;
    }
  }
}
int checkGameStatus() {
  if(board.cells[0][0] == X && board.cells[0][1] == X && board.cells[0][2] == X)
    win = true;
  if(board.cells[0][0] == O && board.cells[0][1] == O && board.cells[0][2] == O)
    lose = true;
  if(board.cells[1][0] == X && board.cells[1][1] == X && board.cells[0][2] == X)
    win = true;
  if(board.cells[1][0] == O && board.cells[1][1] == O && board.cells[0][2] == O)
    lose = true;
  if(board.cells[2][0] == X && board.cells[2][1] == X && board.cells[2][2] == X)
    win = true;
  if(board.cells[2][0] == O && board.cells[2][1] == O && board.cells[2][2] == O)
    lose = true;
  if(board.cells[0][0] == X && board.cells[1][0] == X && board.cells[2][0] == X)
    win = true;
  if(board.cells[0][0] == O && board.cells[1][0] == O && board.cells[2][0] == O)
    lose = true;
  if(board.cells[0][1] == X && board.cells[1][1] == X && board.cells[2][1] == X)
    win = true;
  if(board.cells[0][1] == O && board.cells[1][1] == O && board.cells[2][1] == O)
    lose = true;
  if(board.cells[0][2] == X && board.cells[1][2] == X && board.cells[2][2] == X)
    win = true;
  if(board.cells[0][2] == O && board.cells[1][2] == O && board.cells[2][2] == O)
    lose = true;
  if(board.cells[0][0] == X && board.cells[1][1] == X && board.cells[2][2] == X)
    win = true;
  if(board.cells[0][0] == O && board.cells[1][1] == O && board.cells[2][2] == O)
    lose = true;
  if(board.cells[0][2] == X && board.cells[1][1] == X && board.cells[2][0] == X)
    win = true;
  if(board.cells[0][2] == O && board.cells[1][1] == O && board.cells[2][0] == O)
    lose = true;
  else
    win = false;
    lose = false;
}

Share this post


Link to post
Share on other sites
while(myTurn == true) {
getInput();
myBoard.placeX(x, y);
myTurn = false;
}
while(myTurn == false) {
opponentAI();
myBoard.placeO(ox, oy);
myTurn = true;
}


The above while loops are uneeded. They will never loop.
// my turn
getInput();
myBoard.placeX(x, y);

//opponents turn
opponentAI();
myBoard.placeO(ox, oy);

would work the same way and save you some code.

Share this post


Link to post
Share on other sites
In your board checking routine:

if(board.cells[0][0] == X && board.cells[0][1] == X && board.cells[0][2] == X)
win = true;
if(board.cells[0][0] == O && board.cells[0][1] == O && board.cells[0][2] == O)
lose = true;
if(board.cells[1][0] == X && board.cells[1][1] == X && board.cells[0][2] == X)
win = true;
if(board.cells[1][0] == O && board.cells[1][1] == O && board.cells[0][2] == O)
lose = true;

You check the entire board, even if you found a winner. You should return from this function as soon as a winner is found.

Share this post


Link to post
Share on other sites
In your class:

class cell {
public:
enum token {EMPTY, X, O};
token cellStatus;
cell() : cellStatus(EMPTY) {};
};


You have no private members. You are defeating the purpose of a class. Make this a struct or give it some sort of accessor methods. This is more of a syntactical thing really.

Share this post


Link to post
Share on other sites
Quote:
can I write a constructor in a struct? I never use them...

Absolutely, structs and classes are the same animal, but classes default to private and structs default to public access.


Quote:
so return 1 and it will go out of the function right?

return 1 when you detect a win and 0 when you detect a loss.


Sorry if I seem overly critical, I don't mean to be. Just trying to give you some feedback :)

Share this post


Link to post
Share on other sites
Updated Code: Still without graphics or AI.

#include <iostream>
//globals
bool playing;
int x, y, ox, oy;
bool win, lose;
//cell class
class cell {
public:
enum token {EMPTY, X, O};
token cellStatus;
cell() : cellStatus(EMPTY) {};
};
//board class
class board {
private:
cell cells[3][3];
public:
int checkStatus(int x, int y);
void setStatusX(int x, int y);
void setStatusO(int x, int y);
void printCannotPlace();
};
//class definitions
int board::checkStatus(int x, int y) {
return cells[x][y].cellStatus;
}
void board::setStatusX(int x, int y) {
if(cells[x][y].cellStatus == cell::EMPTY)
cells[x][y].cellStatus = cell::X;
else
printCannotPlace();
}
void board::setStatusO(int x, int y) {
if(cells[x][y].cellStatus == cell::EMPTY)
cells[x][y].cellStatus = cell::O;
else
printCannotPlace();
}
void board::printCannotPlace() {
std::cout<<"Cannot place a piece there."<<std::endl;
}
//function declarations
void gameLoop();
int checkGameStatus();
void getInput();
void opponentAI();
board myBoard;
int main() {

}
void getInput() {
std::cout<<"Where do you want to place? X/Y\n";
std::cin>>x>>y;
}
void gameLoop() {
playing = true;
while(playing == true) {
getInput();
myBoard.setStatusX(x, y);
myTurn = false;
opponentAI();
myBoard.setStatusO(ox, oy);
checkGameStatus();
if(win == true) {
std::cout<<"You won! Good job!\n";
playing = false;
}
if(lose == true) {
std::cout<<"You lose =(. Too bad.\n";
playing = false;
}
}
}
int checkGameStatus() {
if(myBoard.checkStatus(0, 0) == cell::X && myBoard.checkStatus(0, 1) == cell::X && myBoard.checkStatus(0, 2) == cell::X) {
win = true;
return 1; }
if(myBoard.checkStatus(0, 0) == cell::O && myBoard.checkStatus(0, 1) == cell::O && myBoard.checkStatus(0, 2) == cell::O) {
lose = true;
return 0; }
if(myBoard.checkStatus(1, 0) == cell::X && myBoard.checkStatus(1, 1) == cell::X && myBoard.checkStatus(0, 2) == cell::X) {
win = true;
return 1; }
if(myBoard.checkStatus(1, 0) == cell::O && myBoard.checkStatus(1, 1) == cell::O && myBoard.checkStatus(0, 2) == cell::O) {
lose = true;
return 0; }
if(myBoard.checkStatus(2, 0) == cell::X && myBoard.checkStatus(2, 1) == cell::X && myBoard.checkStatus(2, 2) == cell::X) {
win = true;
return 1; }
if(myBoard.checkStatus(2, 0) == cell::O && myBoard.checkStatus(2, 1) == cell::O && myBoard.checkStatus(2, 2) == cell::O) {
lose = true;
return 0; }
if(myBoard.checkStatus(0, 0) == cell::X && myBoard.checkStatus(1, 0) == cell::X && myBoard.checkStatus(2, 0) == cell::X) {
win = true;
return 1; }
if(myBoard.checkStatus(0, 0) == cell::O && myBoard.checkStatus(1, 0) == cell::O && myBoard.checkStatus(2, 0) == cell::O) {
lose = true;
return 0; }
if(myBoard.checkStatus(0, 1) == cell::X && myBoard.checkStatus(1, 1) == cell::X && myBoard.checkStatus(2, 1) == cell::X) {
win = true;
return 1; }
if(myBoard.checkStatus(0, 1) == cell::O && myBoard.checkStatus(1, 1) == cell::O && myBoard.checkStatus(2, 1) == cell::O) {
lose = true;
return 0; }
if(myBoard.checkStatus(0, 2) == cell::X && myBoard.checkStatus(1, 2) == cell::X && myBoard.checkStatus(2, 2) == cell::X) {
win = true;
return 1; }
if(myBoard.checkStatus(0, 2) == cell::O && myBoard.checkStatus(1, 2) == cell::O && myBoard.checkStatus(2, 2) == cell::O) {
lose = true;
return 0; }
if(myBoard.checkStatus(0, 0) == cell::X && myBoard.checkStatus(1, 1) == cell::X && myBoard.checkStatus(2, 2) == cell::X) {
win = true;
return 1; }
if(myBoard.checkStatus(0, 0) == cell::O && myBoard.checkStatus(1, 1) == cell::O && myBoard.checkStatus(2, 2) == cell::O) {
lose = true;
return 0; }
if(myBoard.checkStatus(0, 2) == cell::X && myBoard.checkStatus(1, 1) == cell::X && myBoard.checkStatus(2, 0) == cell::X) {
win = true;
return 1; }
if(myBoard.checkStatus(0, 2) == cell::O && myBoard.checkStatus(1, 1) == cell::O && myBoard.checkStatus(2, 0) == cell::O) {
lose = true;
return 0; }
else {
win = false;
lose = false; }
}


Share this post


Link to post
Share on other sites
void gameLoop() {
playing = true;
while(playing == true) {
getInput();
myBoard.setStatusX(x, y);
myTurn = false;
opponentAI();
myBoard.setStatusO(ox, oy);
checkGameStatus();
if(win == true) {
std::cout<<"You won! Good job!\n";
playing = false;
}
if(lose == true) {
std::cout<<"You lose =(. Too bad.\n";
playing = false;
}
}
}
==============================================
myTurn = false is not needed.
You need to check the status of the board after each turn, not just after the opponent's turn.
Also, you return 1 or 0 from CheckGameStatus(), so you don't need the vars 'win' or 'lose'.

if(CheckGameStatus() == 1)
{
//win
}
else
{
//lose
}

Share this post


Link to post
Share on other sites
Don't use global variables 'win' and 'lose' to check the game status. Instead, use the return value from the checkGameStatus() to communicate this information. That's what return values are for. Of course, there are four possible results (won, lost, draw, and game-still-on), but you have plenty of room to handle that with an 'int' return.

Although maybe you should use an enumeration for that return value instead:


enum game_status {PLAYING, WON, LOST, DRAW};

game_status checkGameStatus() {
// logic goes here...
}

void gameLoop() {
// OK, we need a variable to save the game status for the loop logic.
// But we don't need it to be global, and we only need one. There is only
// one "game state" (playing/won/lost/draw) which was being scattered across
// several booleans before; that's bad. After all, you wouldn't represent your
// game board squares with a pair of booleans "has_x" and "has_o", right?
game_status status = PLAYING;
while(status == PLAYING) {
// Got rid of the unneeded while loops as bit64 was talking about.
getInput();
myBoard.placeX(x, y);
opponentAI();
myBoard.placeO(ox, oy);
// Reset the status; this will cause us to break out of the loop if
// the game is over for any reason.
status = checkGameStatus();
}
// If we got to this point, the status is something other than PLAYING.
switch(status) {
case WON: std::cout<<"You won! Good job!\n"; break;
case LOST: std::cout<<"You lose =(. Too bad.\n"; break;
case DRAW: std::cout<<"Cat's game!\n"; break;
default: assert(false); // can't happen
}
}



Similarly, you don't need globals to communicate between the move-getters (getInput, opponentAI) and the move-makers (myBoard.placeX, myboard.placeO). I know what you're thinking, "how am I going to pass back both coordinates?" Don't. (Don't try; there are ways, but they're not called for here, and they're error-prone.) Instead, pass the myBoard to those functions, and have them do the placing. As a free bonus, getInput() for example can use error checking from the placeX() to help determine valid moves. (You will need this when you want to prevent moving on a square that's already occupied. Right now, you will print the error message, but then you don't get to re-input the move. The while loop that bit64 claims is unneeded is actually just misplaced; you need that logic in the getInput - while(move is invalid) { ask again }.)

I know I tend to drown beginners in information here when it comes to fixing their code :) I'm making a new year's resolution to stop that. So. When you get the stuff I mentioned working, report back, and I'll check what you have and give you the next installment of work ;)

Share this post


Link to post
Share on other sites
Quote:
The while loop that bit64 claims is unneeded is actually just misplaced; you need that logic in the getInput - while(move is invalid) { ask again }.)


Doh! Good catch Zahlman.

Share this post


Link to post
Share on other sites

This topic is 4717 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