#include #include using namespace std; class Board { int boardstate[9]; // describes the state of the board int boardhistory[9]; // note: 0 is a board position, so 9 is 'no event' public: Board() { for(int i = 0; i < 9; i++) { boardstate[i] = 0; boardhistory[i] = 9; } } void reset() { for(int i = 0; i < 9; i++) { boardstate[i] = 0; boardhistory[i] = 9; } } void draw(); int history(int index) { if (index >= 0 && index < 9) { return boardhistory[index]; } else { return 9; } } int last(); int state(int coord) { if (coord >= 0 && coord < 9) { return boardstate[coord]; } else {return 0; } } void change(int player, int coord) { boardstate[coord] = player; boardhistory[this->last() + 1] = coord; } bool wouldwin(int, int); int endgame(); void undo(); }; class Player { bool is_ai; int player_num, player_score; public: Player(int n): player_num(n), player_score(0), is_ai(false) {} int score() { return player_score; } void won(); void set_ai(bool ai) { is_ai = ai; } void go(Board&); // ask for valid move, perform move }; inline char mark(int num) { switch (num) { case 1: return 'X'; break; case 2: return 'O'; break; default: return ' '; break; } } int main() { Board board; Player player1(1), player2(2); bool again = true; string play_again; int games = 1; do { // New game system("clear"); cout << "Tic-Tac-Toe\n\nGame " << games << endl; cout << "Player 1 score: " << player1.score() << endl; cout << "Player 2 score: " << player2.score() << endl << endl; // Determine if any players are AI int ai = 0; player1.set_ai(false); player2.set_ai(false); do { cout << "How many human players? "; cin >> ai; } while (ai < 1 || ai > 2); if (ai == 1) { do { cout << "Which player is AI? "; cin >> ai; } while (ai < 1 || ai > 2); if (ai == 1) { player1.set_ai(true); } else { player2.set_ai(true); } } // Begin game loop while (board.endgame() == 0) { // go Player 1 board.draw(); player1.go(board); if (board.endgame() != 0) { break; } // go Player 2 board.draw(); player2.go(board); } board.draw(); switch ( board.endgame() ) { case 1: player1.won(); break; case 2: player2.won(); break; case 3: cout << "Draw!\n"; break; } games++; board.reset(); cout << "New game? "; cin >> play_again; if (play_again[0] == 'n' || play_again[0] == 'N') { again = false; } } while (again); return 0; } void Board::draw() { system("clear"); cout << " " << mark(boardstate[0]) << " | " << mark(boardstate[1]) << " | " << mark(boardstate[2]) << endl << "-----------" << endl << " " << mark(boardstate[3]) << " | " << mark(boardstate[4]) << " | " << mark(boardstate[5]) << endl << "-----------" << endl << " " << mark(boardstate[6]) << " | " << mark(boardstate[7]) << " | " << mark(boardstate[8]) << endl << endl; } int Board::endgame() { /* returns... 0 if unfinished 1 if player 1 won 2 if player 2 won 3 if draw */ // check horizontals and verticals for(int i = 0; i < 3; i++) { if ( ( (boardstate[3*i] == boardstate[3*i+1]) && (boardstate[3*i] == boardstate[3*i+2]) && (boardstate[3*i] != 0) ) || ( (boardstate[i] == boardstate[i+3] ) && (boardstate[i] == boardstate[i+6]) && (boardstate[i] != 0) ) ) { return boardstate[4*i]; } } // check diagonals if ( ( (boardstate[0] == boardstate[4]) && (boardstate[0] == boardstate[8]) && (boardstate[0] != 0) ) || ( (boardstate[2] == boardstate[4]) && (boardstate[2] == boardstate[6]) && (boardstate[2] != 0) ) ) { return boardstate[4]; } // check for draw bool draw = true; for (int i = 0; i < 9; i++) { if (boardstate[i] == 0) { draw = false; } } if (draw) { return 3; } // if none of these conditions are met, the game is not over return 0; } bool Board::wouldwin(int player, int coord) { Board hypothetical = *this; hypothetical.change(player, coord); if (hypothetical.endgame() == player) { return true; } return false; } int Board::last() { int last = 0; for(int i = 0; i < 9; i++) { if (boardhistory[i] == 9) { last = i - 1; break; } } return last; } void Board::undo() { if (last() > 0) { boardstate[ boardhistory[this->last()] ] = 0; boardhistory[this->last()] = 9; boardstate[ boardhistory[this->last()] ] = 0; boardhistory[this->last()] = 9; } } void Player::go(Board &board) { if (is_ai) { // check for obvious situations: winning moves and blocking moves for(int i = 0; i < 9; i++) { if (board.state(i) == 0 && board.wouldwin(player_num, i)) { board.change(player_num, i); return; } } // check if any coordinate will block for(int i = 0; i < 9; i++) { if (board.state(i) == 0 && board.wouldwin(3 - player_num, i)) { board.change(player_num, i); return; } } // check for special situations if (player_num == 1) { if (board.last() == -1) { board.change(1, 4); return; } else if (board.last() == 1) { int lastmove = board.history(1); if (lastmove % 2 == 0 && lastmove != 4) { board.change(1, 8 - lastmove); return; } else { board.change(1, (lastmove < 4 ? 0 : 8)); return; } } else if (board.last() == 3) { if (board.history(3) % 2 == 0 || board.state(4) == 1) { if (board.state(0) == 1) { board.change(1, 1 + board.state(1)); return; } if (board.state(2) == 1) { board.change(1, 1 + 2 * board.state(1)); return; } if (board.state(6) == 1) { board.change(1, 3 + 2 * board.state(3)); return; } if (board.state(8) == 1) { board.change(1, 5 + board.state(5)); return; } } } } else { if (board.last() == 0) { if (board.state(4) == 1) { board.change(2, 0); return; } else { board.change(2, 4); return; } } else if (board.last() == 2) { if (board.state(4) == 1 && board.state(8) == 1) { board.change(2, 2); return; } if ((board.state(0) == 1 && board.state(8) == 1) || (board.state(2) == 1 && board.state(6) == 1)) { board.change(2, 1); return; } if (board.state(0) == 1 && (board.state(5) == 1 || board.state(7) == 1)) { board.change(2, 8); return; } if (board.state(2) == 1 && (board.state(3) == 1 || board.state(7) == 1)) { board.change(2, 6); return; } if (board.state(6) == 1 && (board.state(1) == 1 || board.state(5) == 1)) { board.change(2, 2); return; } if (board.state(8) == 1 && (board.state(1) == 1 || board.state(3) == 1)) { board.change(2, 0); return; } if (board.state(1) == 1 && board.state(3) == 1) { board.change(2, 0); return; } if (board.state(1) == 1 && board.state(5) == 1) { board.change(2, 2); return; } if (board.state(3) == 1 && board.state(7) == 1) { board.change(2, 6); return; } if (board.state(5) == 1 && board.state(7) == 1) { board.change(2, 8); return; } } } // special conditions out of the way, proceed with threatening for(int i = 0; i < 9; i++) { if (board.state(i) == 0 && board.state(8 - i) == 0) { board.change(player_num, i); return; } } // all strategy aside, pick any spot for(int i = 0; i < 9; i++) { if (board.state(i) == 0) { board.change(player_num, i); return; } } } else { bool again; string input; const char *r_p, *c_p; int row = 0, col = 0; do { again = false; do { cout << "Player " << player_num << ", \"row,col\": "; cin >> input; if (input == "undo") { break; } if (input.size() > 1) { r_p = input.substr(0, 1).c_str(); row = (int)*r_p - 48; c_p = input.substr(2, 1).c_str(); col = (int)*c_p - 48; } } while ( row < 1 || row > 3 || col < 1 || col > 3 ); // take care of "undo" if (input == "undo") { board.undo(); board.draw(); again = true; } else { if (board.state(3*(row-1)+col-1) == 0) { board.change(player_num, 3*(row-1)+col-1); } else { board.draw(); again = true; } } } while (again); } } void Player::won() { player_score++; cout << "Player " << player_num << " (" << mark(player_num) << ") wins!\n"; }