// File: tictactoe.cxx #include // Provides fill_n #include // Provides assert macro #include // Provides isdigit #include // Provides setw #include // Provides cin, cout #include // Provides queue class #include // Provides string #include // Winbgim #include "tictactoe.h" // Provides definition of tictactoe class (derived from game) using namespace std; namespace main_savitch_14 { // Private static member constants, defined here. The COLUMN_DISPLAY_WIDTH // is the number of characters to use in the display( ) function for each // column in the output display. The FOUR_VALUE is the value returned by // the value function when it finds four-in-a-row. For the current // implementation of evaluate to work, the FOUR_VALUE should be at least // 24 times as large as the total number of spots on the board. // MOVE_STRINGS is an array of all possible moves, which must be strings // corresponding to the integers 0 through COLUMNS-1. const string tictactoe::MOVE_STRINGS[3][3] = { { "A", "B", "C" }, { "D", "E", "F" }, { "G", "H", "I" } }; tictactoe::tictactoe( ) : game( ) { initwindow(300, 300, "Tic Tac Toe", 0, 0, true); restart( ); } game* tictactoe::clone( ) const { // Return a pointer to a copy of myself, made with the automatic copy // constructor. If I used dynamic memory, I would have to alter this // copy so that it used its own dynamic memory instead of mine. But // the tictactoe class does not use any dynamic memory, so this is ok: return new tictactoe(*this); } void tictactoe::compute_moves(queue& moves) const { if (data[0][0] == ' ') moves.push("A"); if (data[0][1] == ' ') moves.push("B"); if (data[0][2] == ' ') moves.push("C"); if (data[1][0] == ' ') moves.push("D"); if (data[1][1] == ' ') moves.push("E"); if (data[1][2] == ' ') moves.push("F"); if (data[2][0] == ' ') moves.push("G"); if (data[2][1] == ' ') moves.push("H"); if (data[2][2] == ' ') moves.push("I"); } void tictactoe::display_status( ) const { int row, column; clearviewport( ); line(0, 100, 299, 100); line(0, 200, 299, 200); line(100, 0, 100, 299); line(200, 0, 200, 299); for (row = 0; row < 3; ++row) { for (column = 0; column < 3; ++column) { bgiout << data[row][column]; outstreamxy(column*100 + 50, row*100 + 50); } } swapbuffers( ); } string tictactoe::get_user_move( ) const { string answer; cout << "Please type your move from this pattern: " << endl; cout << "ABC" << endl; cout << "DEF" << endl; cout << "GHI" << endl; cin >> answer; return answer; } bool tictactoe::is_game_over( ) const { int evaluation = evaluate( ); return moves_completed( ) == 9 // all nine spots are filled || evaluation <= -1000 // the human has won || evaluation >= +1000; // the computer has won } bool tictactoe::is_legal(const string& move) const { if (move.length( ) != 1) return false; switch(move[0]) { case 'A': return (data[0][0] == ' '); case 'B': return (data[0][1] == ' '); case 'C': return (data[0][2] == ' '); case 'D': return (data[1][0] == ' '); case 'E': return (data[1][1] == ' '); case 'F': return (data[1][2] == ' '); case 'G': return (data[2][0] == ' '); case 'H': return (data[2][1] == ' '); case 'I': return (data[2][2] == ' '); default: return false; } } void tictactoe::make_move(const string& move) { char mark; mark = (last_mover( ) == COMPUTER) ? 'X' : 'O'; switch (move[0]) { case 'A': data[0][0] = mark; break; case 'B': data[0][1] = mark; break; case 'C': data[0][2] = mark; break; case 'D': data[1][0] = mark; break; case 'E': data[1][1] = mark; break; case 'F': data[1][2] = mark; break; case 'G': data[2][0] = mark; break; case 'H': data[2][1] = mark; break; case 'I': data[2][2] = mark; break; } game::make_move(move); } void tictactoe::restart( ) { int row, column; game::restart( ); for (row = 0; row < 3; ++row) for (column = 0; column < 3; ++column) data[row][column] = ' '; } int tictactoe::evaluate( ) const { // NOTE: Positive answer is good for the computer. // Computer has won = +1000 or more // Human has won = -1000 or less int answer = 0; answer += value(0, 0, 0, +1); answer += value(1, 0, 0, +1); answer += value(2, 0, 0, +1); answer += value(0, 0, +1, 0); answer += value(0, 1, +1, 0); answer += value(0, 2, +1, 0); answer += value(0, 0, +1, +1); answer += value(0, 2, +1, -1); return answer; } int tictactoe::value(int row, int column, int delta_r, int delta_c) const { // NOTE: Positive return value is good for the computer. // Computer has won = +1000 or more // Human has won = -1000 or less int countx = 0, counto = 0; if (data[row][column] == 'O') ++counto; else if (data[row][column] == 'X') ++countx; row += delta_r; column += delta_c; if (data[row][column] == 'O') ++counto; else if (data[row][column] == 'X') ++countx; row += delta_r; column += delta_c; if (data[row][column] == 'O') ++counto; else if (data[row][column] == 'X') ++countx; if (countx == 3) return -1010; if (counto == 3) return +1010; if (countx == 0 && counto == 0) return 0; if (counto == 0) return -1; if (countx == 0) return +1; return 0; } }