Advertisement
zhangsongcui

3D Tic Tac Toe Console

Jun 27th, 2011
542
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 16.86 KB | None | 0 0
  1. //3D Tic Tac Toe
  2. //by Logic
  3. //Environment: Windows 7, Code::Blocks(settings: all default).
  4. #include <iostream>
  5. #include <sstream>
  6. #include <ctime>
  7. #include <cmath>
  8. #include <cstdio>
  9. #include <cstdlib>
  10. #include <cstring>
  11.  
  12. #ifndef _WIN32
  13. #   include <unistd.h>
  14. #   include <termios.h>
  15. #   include <cassert>
  16. #else
  17. #   include <windows.h>
  18. #   include <conio.h>
  19. #endif
  20.  
  21. using namespace std;
  22.  
  23. #ifndef _WIN32
  24. #   define clrscr() printf("\033[1H\033[2J");
  25. #   define Sleep(_t) usleep(_t)
  26. #else
  27. void clrscr(void)
  28. {
  29.     CONSOLE_SCREEN_BUFFER_INFO    csbiInfo;
  30.     HANDLE    hConsoleOut;
  31.     COORD    Home = {0,0};
  32.     DWORD    dummy;
  33.     hConsoleOut = GetStdHandle(STD_OUTPUT_HANDLE);
  34.     GetConsoleScreenBufferInfo(hConsoleOut,&csbiInfo);
  35.     FillConsoleOutputCharacter(hConsoleOut,' ',csbiInfo.dwSize.X * csbiInfo.dwSize.Y,Home,&dummy);
  36.     csbiInfo.dwCursorPosition.X = 0;
  37.     csbiInfo.dwCursorPosition.Y = 0;
  38.     SetConsoleCursorPosition(hConsoleOut,csbiInfo.dwCursorPosition);
  39. }
  40. #endif
  41.  
  42. #ifndef _WIN32
  43. //Copied from: http://topic.csdn.net/u/20080417/16/6f0b781c-287a-485c-b370-7c62953c2193.html
  44. int getch(void)
  45. {
  46.     int c=0;
  47.     struct termios org_opts, new_opts;
  48.     int res=0;
  49.     //-----  store old settings -----------
  50.     res=tcgetattr(STDIN_FILENO, &org_opts);
  51.     assert(res==0);
  52.     //---- set new terminal parms --------
  53.     memcpy(&new_opts, &org_opts, sizeof(new_opts));
  54.     new_opts.c_lflag &= ~(ICANON | ECHO | ECHOE | ECHOK | ECHONL | ECHOPRT | ECHOKE | ICRNL);
  55.     tcsetattr(STDIN_FILENO, TCSANOW, &new_opts);
  56.     c=getchar();
  57.     //------  restore old settings ---------
  58.     res=tcsetattr(STDIN_FILENO, TCSANOW, &org_opts);
  59.     assert(res==0);
  60.     return c;
  61. }
  62. int getche(void)
  63. {
  64.     int result=getch();
  65.     putchar(result);
  66.     return result;
  67. }
  68. #endif
  69.  
  70. const int CTRLC = 0x03;
  71. const double PRECISION = 1e-6;
  72. const int sleepTime = 300;
  73. const int floorOffset[] = { 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1 };
  74. const int xOffset[] = { 0, 1, 1, 1, 0, 0, 0, 1,-1, 1, 1,-1,-1 };
  75. const int yOffset[] = { 1, 0, 1,-1, 0, 1,-1, 0, 0, 1,-1, 1,-1 };
  76. const int mediumDepth = 1;
  77. const int hardDepth = 2;
  78. class Recorder
  79. {
  80. public:
  81.     enum ChessState { NotFilled = 0, FilledO, FilledX, DisplayStar };
  82.     enum Player { UserO, ComputerX, Neither };
  83. protected:
  84.     int o, x;
  85.     ChessState chessboard[3][3][3];
  86. private:
  87.     bool lines[3][3][3]; // only center lines may be recalculated.
  88.     bool exists(int floor, int x, int y)
  89.     {
  90.         if (floor >= 0 && floor < 3
  91.                 && x >= 0 && x < 3
  92.                 && y >= 0 && y < 3) return true;
  93.         return false;
  94.     }
  95.     bool IsANewLine(int floor, int x, int y, int o1, int o2, int o3, int first, int second)
  96.     {
  97.         int floor1 = floor + o1 * first, x1 = x + o2 * first, y1 = y + o3 * first;
  98.         int floor2 = floor + o1 * second, x2 = x + o2 * second, y2 = y + o3 * second;
  99.         if (floor1 == 1 && x1 == 1 && y1 == 1) swap(floor, floor1), swap(x, x1), swap(y, y1);
  100.         if (floor2 == 1 && x2 == 1 && y2 == 1) swap(floor, floor2), swap(x, x2), swap(y, y2);
  101.         if (GetChess(floor1, x1, y1) == chessboard[floor][x][y]
  102.                 && GetChess(floor2, x2, y2) == chessboard[floor][x][y]
  103.                 && ((floor == 1 && x == 1 && y == 1)
  104.                     ? (!lines[floor1][x1][y1] && (lines[floor1][x1][y1] = true, lines[floor2][x2][y2] = true))
  105.                     : true))
  106.             return true;
  107.         return false;
  108.     }
  109.     //This will be called only when there is a new line that is described by the arguments.
  110.     void PerformALine(int floor, int x, int y, int o1, int o2, int o3, int first, int second);
  111.     //This will be called only when chessboard[floor][x][y] != NotFilled
  112.     void CheckNewLines(int floor, int x, int y, bool performOnConsole)
  113.     {
  114.         for (int i = 0; i < (int)(sizeof floorOffset / sizeof(int)); ++i)
  115.         {
  116.             bool flag = false;
  117.             if (IsANewLine(floor, x, y, floorOffset[i], xOffset[i], yOffset[i], 1, 2))
  118.                 flag = true, performOnConsole ? PerformALine(floor, x, y, floorOffset[i], xOffset[i], yOffset[i], 1, 2) : (void)0;
  119.             else if (IsANewLine(floor, x, y, floorOffset[i], xOffset[i], yOffset[i], 1, -1))
  120.                 flag = true, performOnConsole ? PerformALine(floor, x, y, floorOffset[i], xOffset[i], yOffset[i], 1, -1) : (void)0;
  121.             else if (IsANewLine(floor, x, y, floorOffset[i], xOffset[i], yOffset[i], -1, -2))
  122.                 flag = true, performOnConsole ? PerformALine(floor, x, y, floorOffset[i], xOffset[i], yOffset[i], -1, -2) : (void)0;
  123.             if (flag && chessboard[floor][x][y] == FilledO) ++this->o;
  124.             else if (flag) ++this->x;
  125.         }
  126.     }
  127. public:
  128.     Recorder() : o(0), x(0)
  129.     {
  130.         memset(chessboard, 0, sizeof chessboard);
  131.         memset(lines, 0, sizeof lines);
  132.     }
  133.     virtual bool IsGameDone() = 0;
  134.     Player GetWinner()
  135.     {
  136.         if (o > x) return UserO;
  137.         if (o < x) return ComputerX;
  138.         return Neither;
  139.     }
  140.     int O()
  141.     {
  142.         return o;
  143.     }
  144.     int X()
  145.     {
  146.         return x;
  147.     }
  148.     virtual Recorder *Clone() = 0;
  149.     ChessState GetChess(int floor, int x, int y)
  150.     {
  151.         if (!exists(floor, x, y)) return DisplayStar;
  152.         return chessboard[floor][x][y];
  153.     }
  154.     void SetChess(int floor, int x, int y, ChessState state, bool performOnConsole);
  155.     void SetChess(int floor, int x, int y, ChessState state)
  156.     {
  157.         SetChess(floor, x, y, state, true);
  158.     }
  159.     void ABCDisplay (ostream &out)
  160.     {
  161.         out << "Player: " << o << '\t' << "Computer: " << x << endl;
  162.         char x = 'a';
  163.         for (int i = 0; i < 3; ++i)
  164.         {
  165.             out << "     _______" << endl;
  166.             for (int j = 0; j < 3; ++j)
  167.             {
  168.                 for (int k = 0; k < 4 - j - j; ++k) out << ' ';
  169.                 out<< '/';
  170.                 for (int k = 0; k < 3; ++k)
  171.                 {
  172.                     switch (chessboard[i][j][k])
  173.                     {
  174.                     case Recorder::NotFilled:
  175.                         if (i == 1 && j == 1 && k == 1) out << '.';
  176.                         else out << x++;
  177.                         break;
  178.                     case Recorder::FilledO:
  179.                         out << 'O';
  180.                         break;
  181.                     case Recorder::FilledX:
  182.                         out << 'X';
  183.                         break;
  184.                     default:
  185.                         break;
  186.                     }
  187.                     out << '/';
  188.                 }
  189.                 out << endl;
  190.             }
  191.         }
  192.     }
  193.     void SimpleDisplay(ostream &out)
  194.     {
  195.         out << "Player: " << O() << '\t' << "Computer: " << x << endl;
  196.         for (int i = 0; i < 3; ++i)
  197.         {
  198.             out << "     _______" << endl;
  199.             for (int j = 0; j < 3; ++j)
  200.             {
  201.                 for (int k = 0; k < 4 - j - j; ++k) out << ' ';
  202.                 out<< '/';
  203.                 for (int k = 0; k < 3; ++k)
  204.                 {
  205.                     switch (chessboard[i][j][k])
  206.                     {
  207.                     case DisplayStar:
  208.                         out << '*';
  209.                         break;
  210.                     case NotFilled:
  211.                         out << ' ';
  212.                         break;
  213.                     case FilledO:
  214.                         out << 'O';
  215.                         break;
  216.                     case FilledX:
  217.                         out << 'X';
  218.                         break;
  219.                     default:
  220.                         break;
  221.                     }
  222.                     out << '/';
  223.                 }
  224.                 out << endl;
  225.             }
  226.         }
  227.     }
  228. };
  229. void Recorder::SetChess(int floor, int x, int y, Recorder::ChessState state, bool performOnConsole)
  230. {
  231.     if (performOnConsole)
  232.     {
  233.         clrscr();
  234.         SimpleDisplay(cout);
  235.     }
  236.     chessboard[floor][x][y] = state;
  237.     if (performOnConsole)
  238.     {
  239.         Sleep(sleepTime);
  240.         clrscr();
  241.         SimpleDisplay(cout);
  242.         Sleep(sleepTime);
  243.     }
  244.     CheckNewLines(floor, x, y, performOnConsole);
  245. }
  246. void Recorder::PerformALine(int floor, int x, int y, int o1, int o2, int o3, int first, int second)
  247. {
  248.     stringstream s1, s2;
  249.     SimpleDisplay(s1);
  250.     ChessState state = chessboard[floor][x][y];
  251.     chessboard[floor][x][y] = DisplayStar;
  252.     chessboard[floor + o1 * first][x + o2 * first][y + o3 * first] = DisplayStar;
  253.     chessboard[floor + o1 * second][x + o2 * second][y + o3 * second] = DisplayStar;
  254.     SimpleDisplay(s2);
  255.     chessboard[floor][x][y] = state;
  256.     chessboard[floor + o1 * first][x + o2 * first][y + o3 * first] = state;
  257.     chessboard[floor + o1 * second][x + o2 * second][y + o3 * second] = state;
  258.     for (int i = 0; i < 3; ++i)
  259.     {
  260.         clrscr();
  261.         cout << s1.str();
  262.         Sleep(sleepTime);
  263.         clrscr();
  264.         cout << s2.str();
  265.         Sleep(sleepTime);
  266.     }
  267. }
  268. class SuperRecorder : public Recorder
  269. {
  270. public:
  271.     bool IsGameDone()
  272.     {
  273.         for (int i = 0; i < 3; ++i)
  274.             for (int j = 0; j < 3; ++j)
  275.                 for (int k = 0; k < 3; ++k)
  276.                     if (chessboard[i][j][k] == NotFilled) return false;
  277.         return true;
  278.     }
  279.     Recorder *Clone()
  280.     {
  281.         return new SuperRecorder(*this);
  282.     }
  283. };
  284. class ClassicRecorder : public Recorder
  285. {
  286. public:
  287.     bool IsGameDone()
  288.     {
  289.         if (o == 0 && x == 0)
  290.             for (int i = 0; i < 3; ++i)
  291.                 for (int j = 0; j < 3; ++j)
  292.                     for (int k = 0; k < 3; ++k)
  293.                         if (chessboard[i][j][k] == NotFilled) return false;
  294.         return true;
  295.     }
  296.     Recorder *Clone()
  297.     {
  298.         return new ClassicRecorder(*this);
  299.     }
  300. };
  301. typedef void (*ComputerPlay)(Recorder *);
  302. typedef struct _p
  303. {
  304.     int floor, x, y;
  305.     _p() : floor(0), x(0), y(0) { }
  306. } point;
  307. void EasyPlay(Recorder *rec)
  308. {
  309.     point usables[27];
  310.     point *p = usables;
  311.     if (rec->GetChess(1, 1, 1) != Recorder::FilledX)
  312.     {
  313.         usables[0].floor = 1;
  314.         usables[0].x = 1;
  315.         usables[0].y = 1;
  316.         ++p;
  317.     }
  318.     for (int i = 0; i < 3; ++i)
  319.         for (int j = 0; j < 3; ++j)
  320.             for (int k = 0; k < 3; ++k)
  321.                 if (i == 1 && j == 1 && k == 1) continue;
  322.                 else if (rec->GetChess(i, j, k) == Recorder::NotFilled) p->floor = i, p->x = j, p->y = k, ++p;
  323.     p = &usables[rand() % (p - usables)];
  324.     rec->SetChess(p->floor, p->x, p->y, Recorder::FilledX);
  325. }
  326. inline Recorder::ChessState GetAnotherTurn(Recorder::ChessState turn)
  327. {
  328.     return turn == Recorder::FilledO ? Recorder::FilledX : Recorder::FilledO;
  329. }
  330. double WayAssesment(Recorder *rec, int depth, Recorder::ChessState turn)
  331. {
  332.     double answer = 0.0;
  333.     int count = 0;
  334.     if (depth == 0 || rec->IsGameDone())
  335.     {
  336.         return rec->X() - rec->O();
  337.     }
  338.     point usables[27];
  339.     point *p = usables;
  340.     if (rec->GetChess(1, 1, 1) != turn)
  341.     {
  342.         usables[0].floor = 1;
  343.         usables[0].x = 1;
  344.         usables[0].y = 1;
  345.         ++p;
  346.     }
  347.     for (int i = 0; i < 3; ++i)
  348.         for (int j = 0; j < 3; ++j)
  349.             for (int k = 0; k < 3; ++k)
  350.                 if (i == 1 && j == 1 && k == 1) continue;
  351.                 else if (rec->GetChess(i, j, k) == Recorder::NotFilled) p->floor = i, p->x = j, p->y = k, ++p;
  352.     for (point *it = usables; p - it; ++it, ++count)
  353.     {
  354.         Recorder *r = rec->Clone();
  355.         r->SetChess(it->floor, it->x, it->y, turn, false);
  356.         answer += WayAssesment(r, depth - 1, GetAnotherTurn(turn));
  357.         delete r;
  358.     }
  359.     return answer / count;
  360. }
  361. void PlayTheBestWay(Recorder *rec, int depth)
  362. {
  363.     double maxval = -100.0;
  364.     point best;
  365.     int bestcount = 0;
  366.     point usables[27];
  367.     double assesments[27];
  368.     point *p = usables;
  369.     if (rec->GetChess(1, 1, 1) != Recorder::FilledX)
  370.     {
  371.         usables[0].floor = 1;
  372.         usables[0].x = 1;
  373.         usables[0].y = 1;
  374.         ++p;
  375.     }
  376.     for (int i = 0; i < 3; ++i)
  377.         for (int j = 0; j < 3; ++j)
  378.             for (int k = 0; k < 3; ++k)
  379.                 if (i == 1 && j == 1 && k == 1) continue;
  380.                 else if (rec->GetChess(i, j, k) == Recorder::NotFilled) p->floor = i, p->x = j, p->y = k, ++p;
  381.     for (point *it = usables; p - it; ++it)
  382.     {
  383.         Recorder *r = rec->Clone();
  384.         r->SetChess(it->floor, it->x, it->y, Recorder::FilledX, false);
  385.         assesments[it - usables] = WayAssesment(r, depth * 2 - 1, Recorder::FilledO);
  386.         delete r;
  387.         if (assesments[it - usables] - maxval > PRECISION) maxval = assesments[it - usables], bestcount = 1;
  388.         else if (fabs(assesments[it - usables] - maxval) <= PRECISION) ++bestcount;
  389.     }
  390.     int chosen = rand() % bestcount;
  391.     for (point *it = usables; p - it; ++it)
  392.     {
  393.         if (fabs(assesments[it - usables] - maxval) <= PRECISION && chosen-- == 0)
  394.         {
  395.             best = *it;
  396.             break;
  397.         }
  398.     }
  399.     rec->SetChess(best.floor, best.x, best.y, Recorder::FilledX);
  400. }
  401. void MediumPlay(Recorder *rec)
  402. {
  403.     clrscr();
  404.     rec->SimpleDisplay(cout);
  405.     cout << "Computer is thinking..." << endl;
  406.     PlayTheBestWay(rec, mediumDepth);
  407. }
  408. void HardPlay(Recorder *rec)
  409. {
  410.     clrscr();
  411.     rec->SimpleDisplay(cout);
  412.     cout << "Computer is thinking..." << endl;
  413.     PlayTheBestWay(rec, hardDepth);
  414. }
  415. ComputerPlay ChooseDifficuty()
  416. {
  417. choose:
  418.     clrscr();
  419.     cout << "Please choose the difficuty of the game:" << endl;
  420.     cout << "(E)asy" << endl
  421.          << "(M)edium" << endl
  422.          << "(H)ard" << endl;
  423.     cout << "Choose one by inputing the first letter: ";
  424.     char d = getche();
  425.     switch (d)
  426.     {
  427.     case 'E':
  428.     case 'e':
  429.         return EasyPlay;
  430.     case 'M':
  431.     case 'm':
  432.         return MediumPlay;
  433.     case 'H':
  434.     case 'h':
  435.         return HardPlay;
  436.     default:
  437.         break;
  438.     }
  439.     cout << endl << "Input is invalid." << endl
  440.          << "Press any key to input again..." << endl
  441.          << "Press Ctrl+C to end the game." << endl;
  442.     if (getch() == CTRLC) exit(0);
  443.     goto choose;
  444. }
  445. Recorder *ChooseGameType()
  446. {
  447. choose:
  448.     clrscr();
  449.     cout << "Please choose the type of 3D Tic Tac Toe you want to play:" << endl;
  450.     cout << "(S)uper" << endl
  451.          << "(C)lassic" << endl;
  452.     cout << "Choose one by inputing the first letter: ";
  453.     char t = getche();
  454.     if (t == 'S' || t == 's') return new SuperRecorder();
  455.     if (t == 'C' || t == 'c') return new ClassicRecorder();
  456.     cout << endl << "Input is invalid." << endl
  457.          << "Press any key to input again..." << endl
  458.          << "Press Ctrl+C to end the game." << endl;
  459.     if (getch() == CTRLC) exit(0);
  460.     goto choose;
  461. }
  462. bool PlayerPlay(Recorder *rec)
  463. {
  464.     clrscr();
  465.     rec->ABCDisplay(cout);
  466.     cout << "Please input the letter of the place you want to fill: ";
  467.     char x = getche();
  468.     char y = 'a';
  469.     if (x == '.')
  470.     {
  471.         if (rec->GetChess(1, 1, 1) == Recorder::FilledO) return false;
  472.         rec->SetChess(1, 1, 1, Recorder::FilledO);
  473.         return true;
  474.     }
  475.     if (!(('a' <= x && x <= 'z') || ('A' <= x && x <= 'Z'))) return false;
  476.     if ('A' <= x && x <= 'Z') x = x - 'A' + 'a';
  477.     int i, j, k;
  478.     for (i = 0; i < 3; ++i)
  479.     {
  480.         for (j = 0; j < 3; ++j)
  481.         {
  482.             for (k = 0; k < 3; ++k)
  483.             {
  484.                 if (i == 1 && j == 1 && k == 1) continue;
  485.                 if (rec->GetChess(i, j, k) == Recorder::NotFilled && y++ == x) goto exitfor;
  486.             }
  487.         }
  488.     }
  489. exitfor:
  490.     if (i == 3)
  491.         return false;
  492.     rec->SetChess(i, j, k, Recorder::FilledO);
  493.     return true;
  494. }
  495. int main()
  496. {
  497.     Recorder *recorder = ChooseGameType();
  498.     ComputerPlay computerPlay = ChooseDifficuty();
  499.     srand((unsigned)time(NULL));
  500.     bool IsPlayerFirst = rand() % 2;
  501.     clrscr();
  502.     if (IsPlayerFirst)
  503.         cout << "Player first." << endl;
  504.     else cout << "Computer first." << endl;
  505.     cout << "Press any key to continue..." << endl;
  506.     getch();
  507.     clrscr();
  508.     while (!recorder->IsGameDone())
  509.     {
  510.         if (!IsPlayerFirst)
  511.         {
  512.             computerPlay(recorder);
  513.             if (recorder->IsGameDone()) break;
  514.         }
  515.         while (!PlayerPlay(recorder))
  516.         {
  517.             cout << endl << "Input is invalid." << endl
  518.                  << "Press any key to input again..." << endl;
  519.             getch();
  520.         }
  521.         if (recorder->IsGameDone()) break;
  522.         if (IsPlayerFirst) computerPlay(recorder);
  523.     }
  524.     clrscr();
  525.     recorder->SimpleDisplay(cout);
  526.     cout << endl;
  527.     switch (recorder->GetWinner())
  528.     {
  529.     case Recorder::Neither:
  530.         cout << ":-| Tie." << endl;
  531.         break;
  532.     case Recorder::UserO:
  533.         cout << ":-) Congratulations! You won!" << endl;
  534.         break;
  535.     case Recorder::ComputerX:
  536.         cout << ":'( Sorry... You lost." << endl;
  537.         break;
  538.     default:
  539.         break;
  540.     }
  541.     delete recorder;
  542.     return 0;
  543. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement