home *** CD-ROM | disk | FTP | other *** search
/ QBasic & Borland Pascal & C / Delphi5.iso / C / BC_502 / TTTGAME.PAK / TTT.CPP < prev    next >
Encoding:
C/C++ Source or Header  |  1997-05-06  |  12.0 KB  |  540 lines

  1. //----------------------------------------------------------------------------
  2. // ObjectWindows
  3. // Copyright (c) 1992, 1995 by Borland International, All Rights Reserved
  4. //
  5. //   TicTacToe Demo Program
  6. //
  7. //   Plays a game of TicTacToe with the user.
  8. //
  9. //   TTTTGameApp - Main TicTacToe application, derived from TApplication
  10. //   TGameWindow - Main window for the app, derived from TWindow
  11. //   Square - Game squares (windows), derived from TWindow
  12. //   TGameAboutBox - A TDialog box for info about TicTacToe
  13. //   TGameOptionsBox - A TDialog box for setting TicTacToe options
  14. //   YouMeRadioButton - A radio button which controls game settings
  15. //   XORadioButton - A radio button which controls game settings
  16. //----------------------------------------------------------------------------
  17. #include <owl/pch.h>
  18. #include <owl/applicat.h>
  19. #include <owl/framewin.h>
  20. #include <owl/dialog.h>
  21. #include <owl/dc.h>
  22. #include <owl/button.h>
  23. #include <owl/static.h>
  24. #include <owl/radiobut.h>
  25. #include <owl/groupbox.h>
  26. #include "ttt.h"
  27.  
  28. const uint TGW_WIDTH = 280, TGW_HEIGHT = 300;
  29. const uint TGAB_WIDTH = 250, TGAB_HEIGHT = 300;
  30. const uint TGAB_INITIAL_X = 5, TGAB_INITIAL_Y = 10;
  31.  
  32. const uint SW_SIZE = 40;
  33. const uint BOARDXOFFSET = 30+SW_SIZE+3;
  34. const uint BOARDYOFFSET = 10+SW_SIZE+2;
  35.  
  36. enum TSquareStatus { SqsUnoccupied, SqsX, SqsO };
  37.  
  38.  
  39. static const int freeMasks[] = {
  40.   0x006, 0x005, 0x003, 0x030, 0x028, 0x018, 0x180, 0x140, 0x0C0, // row
  41.   0x048, 0x041, 0x009, 0x090, 0x082, 0x012, 0x120, 0x104, 0x024, // col
  42.   0x110, 0x101, 0x011, 0x050, 0x044, 0x014                       // diagonal
  43. };
  44.  
  45. static const int winningMasks[] = {
  46.   0x1C0, 0x038, 0x007, 0x124, 0x092, 0x049, 0x111, 0x054
  47. };
  48.  
  49.  
  50. //
  51. //
  52. //
  53. static void
  54. freeSquare(int mask, int i, int *row, int *col)
  55. {
  56.   int mode = i/9; // row, col, or diag
  57.   if (mode == 0)
  58.     mask ^= 0x007 << (i/3)*3;
  59.   else if (mode == 1)
  60.     mask ^= 0x049 << (i%9)/3;
  61.   else if (((i%9)/3) == 0)
  62.     mask ^= 0x111;
  63.   else
  64.     mask ^= 0x054;
  65.  
  66.   int j = 0;
  67.   for (int test = 1; test; test <<= 1, j++)
  68.     if (test & mask)
  69.       break;
  70.   *row = j/3;
  71.   *col = j%3;
  72. }
  73.  
  74. //----------------------------------------------------------------------------
  75.  
  76. class TSquare;
  77.  
  78. //
  79. //
  80. //
  81. class TGame {
  82.   public:
  83.     TGame(TWindow* window);
  84.  
  85.     int      UserBoardMap;
  86.     int      ComputerBoardMap;
  87.     TSquareStatus UserSide;
  88.     bool     ComputerGoesFirst;
  89.     bool     Playing;
  90.     TSquare* Board[3][3];
  91.  
  92.     void ComputerTurn(TWindow* window);
  93.     bool GameOver() {return (UserBoardMap|ComputerBoardMap) == 0x1FF;}
  94.     bool IsWon();
  95.  
  96.   private:
  97.     void MakeMyMark(int r, int c);
  98.     bool IsAWinner(int r, int c);
  99. };
  100.  
  101. //
  102. //
  103. //
  104. class TSquare : public TWindow {
  105.   public:
  106.     TSquare(TWindow*, int, int, TGame* game);
  107.     void MakeAnX() {SquareStatus = SqsX; Invalidate();}
  108.     void MakeAnO() {SquareStatus = SqsO; Invalidate();}
  109.     void Reset() {SquareStatus = SqsUnoccupied;Invalidate();}
  110.  
  111.    void EvLButtonDown(uint, TPoint&);
  112.    void EvPaint();
  113.    TSquareStatus SquareStatus;
  114.  
  115.   private:
  116.     int Row;
  117.     int Col;
  118.     TGame* Game;
  119.  
  120.   DECLARE_RESPONSE_TABLE(TSquare);
  121. };
  122.  
  123. DEFINE_RESPONSE_TABLE1(TSquare, TWindow)
  124.  EV_WM_PAINT,
  125.  EV_WM_LBUTTONDOWN,
  126. END_RESPONSE_TABLE;
  127.  
  128. //
  129. //
  130. //
  131. TSquare::TSquare(TWindow* parent, int x, int y, TGame* game)
  132. :
  133.   TWindow(parent, ""),
  134.   SquareStatus(SqsUnoccupied),
  135.   Row(x), Col(y),
  136.   Game(game)
  137. {
  138.   Attr.W = Attr.H = SW_SIZE;
  139.   Attr.X = BOARDXOFFSET + y*(SW_SIZE+3); // x and y are reversed because
  140.   Attr.Y = BOARDYOFFSET + x*(SW_SIZE+3); // of screen coordinates
  141.   Attr.Style = WS_VISIBLE|WS_CHILD;
  142. }
  143.  
  144. //
  145. //
  146. //
  147. void
  148. TSquare::EvLButtonDown(uint, TPoint&)
  149. {
  150.   if (Game->Playing && SquareStatus == SqsUnoccupied) {
  151.     if (Game->UserSide == SqsX) {
  152.       MakeAnX();
  153.       Game->UserBoardMap |= 1 << (Row*3+Col);
  154.     }
  155.     else {
  156.       MakeAnO();
  157.       Game->UserBoardMap |= 1 << (Row*3+Col);
  158.     }
  159.     if (Game->IsWon()) {
  160.       Game->Playing = false;
  161.       Parent->MessageBox("You won!", "", MB_OK);
  162.     }
  163.     else if (Game->GameOver()) {
  164.       Game->Playing = false;
  165.       Parent->MessageBox("Scratch", "", MB_OK);
  166.     }
  167.     else
  168.       Game->ComputerTurn(Parent);
  169.   }
  170.   else
  171.     Parent->PostMessage(WM_COMMAND, CM_GAMENEW);
  172. }
  173.  
  174. //
  175. //
  176. //
  177. void
  178. TSquare::EvPaint()
  179. {
  180.   TPaintDC paintDC(HWindow);
  181.   if (SquareStatus == SqsO)
  182.     paintDC.Ellipse(0,0,SW_SIZE,SW_SIZE);
  183.  
  184.   else if (SquareStatus == SqsX) {
  185.     paintDC.MoveTo(0,0);
  186.     paintDC.LineTo(SW_SIZE,SW_SIZE);
  187.     paintDC.MoveTo(SW_SIZE,0);
  188.     paintDC.LineTo(0,SW_SIZE);
  189.   }
  190. }
  191.  
  192. //----------------------------------------------------------------------------
  193.  
  194. //
  195. //
  196. //
  197. TGame::TGame(TWindow* window)
  198. {
  199.   UserSide = SqsX;
  200.   ComputerGoesFirst = false;
  201.   Playing = true;
  202.   UserBoardMap = ComputerBoardMap = 0;
  203.   for (int i = 0; i < 3; i++)
  204.     for (int j = 0; j < 3; j++)
  205.       Board[i][j] = new TSquare(window, i, j, this);
  206. }
  207.  
  208. //
  209. //
  210. //
  211. void
  212. TGame::MakeMyMark(int row, int col)
  213. {
  214.   if (UserSide == SqsX)
  215.     Board[row][col]->MakeAnO();
  216.   else
  217.     Board[row][col]->MakeAnX();
  218.   ComputerBoardMap |= 1 << (row*3+col);
  219. }
  220.  
  221. //
  222. //
  223. //
  224. bool
  225. TGame::IsAWinner(int row, int col)
  226. {
  227.   int map = ComputerBoardMap | (1 << (row*3+col));
  228.   for (int i = 0; i < 8; i++)
  229.     if ((map & winningMasks[i]) == winningMasks[i])
  230.       return true;
  231.   return false;
  232. }
  233.  
  234. //
  235. // The following routine can be improved upon.  Write your own
  236. // version of this routine so that the machine always wins or draws the game.
  237. //
  238. void
  239. TGame::ComputerTurn(TWindow* window)
  240. {
  241.   bool madeMove = false;
  242.  
  243.   // Look for a winning move first.
  244.   //
  245.   {
  246.     for (int i = 0; i < 9; i++) {
  247.       if (Board[i/3][i%3]->SquareStatus==SqsUnoccupied && IsAWinner(i/3,i%3)) {
  248.         MakeMyMark(i/3,i%3);
  249.         madeMove = true;
  250.         break;
  251.       }
  252.     }
  253.   }
  254.  
  255.   // Look for a blocking move
  256.   //
  257.   if (!madeMove) {
  258.     for (int i = 0; i < 24; i++)
  259.       if ((UserBoardMap & freeMasks[i]) == freeMasks[i]) {
  260.         int targetRow, targetCol;
  261.         freeSquare(freeMasks[i], i, &targetRow, &targetCol);
  262.         if (ComputerBoardMap & (1 << (targetRow*3+targetCol)))
  263.           continue;
  264.         MakeMyMark(targetRow,targetCol);
  265.         madeMove = true;
  266.         break;
  267.       }
  268.   }
  269.  
  270.   // Nothing to block, go to the middle if empty, else look for a 'good' move
  271.   //
  272.   if (!madeMove) {
  273.     if (!((ComputerBoardMap|UserBoardMap) & 0x010)) {
  274.       MakeMyMark(1,1);
  275.     }
  276.     else {
  277.       int i = 0;
  278.       for (int mask = UserBoardMap|ComputerBoardMap; mask & 1; mask >>= 1)
  279.         i++;
  280.       MakeMyMark(i/3,i%3);
  281.     }
  282.   }
  283.  
  284.   // See if the game is voer now.
  285.   //
  286.   if (IsWon()) {
  287.     Playing = false;
  288.     window->MessageBox("I won!", "", MB_OK);
  289.   }
  290.   else if (GameOver()) {
  291.     Playing = false;
  292.     window->MessageBox("Scratch", "", MB_OK);
  293.   }
  294. }
  295.  
  296. //
  297. //
  298. //
  299. bool
  300. TGame::IsWon()
  301. {
  302.   for (int i = 0; i < 8; i++)
  303.     if ((UserBoardMap & winningMasks[i]) == winningMasks[i] ||
  304.           (ComputerBoardMap & winningMasks[i]) == winningMasks[i])
  305.       return true;
  306.   return false;
  307. }
  308.  
  309. //----------------------------------------------------------------------------
  310.  
  311. //
  312. //
  313. //
  314. class TGameAboutBox : public TDialog {
  315.   public:
  316.     TGameAboutBox::TGameAboutBox(TWindow* parent);
  317. };
  318.  
  319. //
  320. //
  321. //
  322. TGameAboutBox::TGameAboutBox(TWindow* parent)
  323. :
  324.   TDialog(parent, "ABOUT")
  325. {
  326.   char aboutstr[] = "Tic Tac Toe\n(c) Borland International\n 1992, 1995";
  327.   TStatic* textPtr = new TStatic(this, IDSTATIC, aboutstr, 23, 20, 190,
  328.                                  45, strlen(aboutstr));
  329.   textPtr->Attr.Style |= SS_CENTER;
  330.   new TButton(this, IDOK, "Ok", 80, 90, 40, 40, true);
  331. }
  332.  
  333. //----------------------------------------------------------------------------
  334.  
  335. //
  336. //
  337. //
  338. class TGameOptionsBox : public TDialog {
  339.   public:
  340.     TGameOptionsBox::TGameOptionsBox(TWindow* parent, TGame* game);
  341.     virtual void SetupWindow();
  342.  
  343.     TGroupBox     *YouMeGroup, *XOGroup;
  344.     TRadioButton  *You, *Me, *X, *O;
  345.     TGame*         Game;
  346.  
  347.     void HandleYouMeGroupMsg(uint);
  348.     void HandleXOGroupMsg(uint);
  349.  
  350.   DECLARE_RESPONSE_TABLE(TGameOptionsBox);
  351. };
  352.  
  353. DEFINE_RESPONSE_TABLE1(TGameOptionsBox, TDialog)
  354.   EV_CHILD_NOTIFY_ALL_CODES(IDYOUMEGROUP, HandleYouMeGroupMsg),
  355.   EV_CHILD_NOTIFY_ALL_CODES(IDXOGROUP, HandleXOGroupMsg),
  356. END_RESPONSE_TABLE;
  357.  
  358. //
  359. //
  360. //
  361. TGameOptionsBox::TGameOptionsBox(TWindow* parent, TGame* game)
  362. :
  363.   TDialog(parent, "OPTIONS"),
  364.   Game(game)
  365. {
  366.   new TButton(this, IDOK, "Ok", 30, 240, 40, 40, true);
  367.   new TButton(this, IDCANCEL, "Cancel", 150, 240, 40, 40, true);
  368.   YouMeGroup = new TGroupBox(this, IDYOUMEGROUP, "Who goes first?", 15,
  369.                      30, 200, 20);
  370.   You = new TRadioButton(this, IDYOU, "You (Human)", 15, 55, 150, 20, YouMeGroup);
  371.   Me = new TRadioButton(this, IDME, "Me (Computer)", 15, 80, 150, 20, YouMeGroup);
  372.   XOGroup = new TGroupBox(this, IDXOGROUP, "I am playing", 15, 120, 200, 20);
  373.   X = new TRadioButton(this, IDX, "X\'s", 15, 145, 50, 20, XOGroup);
  374.   O = new TRadioButton(this, IDO, "O\'s", 15, 170, 50, 20, XOGroup);
  375. }
  376.  
  377. //
  378. //
  379. //
  380. void
  381. TGameOptionsBox::SetupWindow()
  382. {
  383.   TDialog::SetupWindow();
  384.   if (Game->ComputerGoesFirst)
  385.     Me->Check();
  386.   else
  387.     You->Check();
  388.  
  389.   if (Game->UserSide == SqsX)
  390.     O->Check();
  391.   else
  392.     X->Check();
  393. }
  394.  
  395. //
  396. //
  397. //
  398. void
  399. TGameOptionsBox::HandleYouMeGroupMsg(uint)
  400. {
  401.   Game->ComputerGoesFirst = (You->GetCheck() == BF_CHECKED) ? false : true;
  402. }
  403.  
  404. //
  405. //
  406. //
  407. void
  408. TGameOptionsBox::HandleXOGroupMsg(uint)
  409. {
  410.   Game->UserSide = (X->GetCheck() == BF_CHECKED) ? SqsO : SqsX;
  411.   Game->Playing = false;
  412.   Parent->PostMessage(WM_COMMAND, CM_GAMENEW);
  413. }
  414.  
  415. //----------------------------------------------------------------------------
  416.  
  417. //
  418. //
  419. //
  420. class TGameWindow : public TFrameWindow {
  421.   public:
  422.     TGameWindow(TWindow* parent, const char* title);
  423.     ~TGameWindow() { delete Game; }
  424.  
  425.     void EvPaint();
  426.     void CmGameNew();
  427.     void CmGameOptions();
  428.     void CmAbout();
  429.  
  430.     TGame* Game;
  431.  
  432.   DECLARE_RESPONSE_TABLE(TGameWindow);
  433. };
  434.  
  435. DEFINE_RESPONSE_TABLE1(TGameWindow, TFrameWindow)
  436.   EV_WM_PAINT,
  437.   EV_COMMAND(CM_GAMENEW, CmGameNew),
  438.   EV_COMMAND(CM_GAMEOPTIONS, CmGameOptions),
  439.   EV_COMMAND(CM_ABOUT, CmAbout),
  440. END_RESPONSE_TABLE;
  441.  
  442. //
  443. //
  444. //
  445. TGameWindow::TGameWindow(TWindow* parent, const char* title)
  446. :
  447.   TFrameWindow(parent, title),
  448.   TWindow(parent, title),
  449.   Game(0)
  450. {
  451.   AssignMenu("COMMANDS");
  452.   Attr.X = TGAB_INITIAL_X;
  453.   Attr.Y = TGAB_INITIAL_Y;
  454.   Attr.W = TGW_WIDTH;
  455.   Attr.H = TGW_HEIGHT;
  456.  
  457.   Game = new TGame(this);
  458. }
  459.  
  460. //
  461. //
  462. //
  463. void
  464. TGameWindow::EvPaint()
  465. {
  466.   TPaintDC paintDC(HWindow);
  467.   paintDC.MoveTo(BOARDXOFFSET+SW_SIZE+2, BOARDYOFFSET);
  468.   paintDC.LineTo(BOARDXOFFSET+SW_SIZE+2, BOARDYOFFSET+SW_SIZE*3+6);
  469.   paintDC.MoveTo(BOARDXOFFSET+SW_SIZE*2+5, BOARDYOFFSET);
  470.   paintDC.LineTo(BOARDXOFFSET+SW_SIZE*2+5, BOARDYOFFSET+SW_SIZE*3+6);
  471.   paintDC.MoveTo(BOARDXOFFSET, BOARDYOFFSET+SW_SIZE+2);
  472.   paintDC.LineTo(BOARDXOFFSET+SW_SIZE*3+6, BOARDYOFFSET+SW_SIZE+2);
  473.   paintDC.MoveTo(BOARDXOFFSET, BOARDYOFFSET+SW_SIZE*2+5);
  474.   paintDC.LineTo(BOARDXOFFSET+SW_SIZE*3+6, BOARDYOFFSET+SW_SIZE*2+5);
  475. }
  476.  
  477. //
  478. //
  479. //
  480. void
  481. TGameWindow::CmGameNew()
  482. {
  483.   Game->Playing = true;
  484.   Game->UserBoardMap = 0;
  485.   Game->ComputerBoardMap = 0;
  486.   for (int i = 0; i < 3; i++)
  487.     for (int j = 0; j < 3; j++)
  488.       Game->Board[i][j]->Reset();
  489.   if (Game->ComputerGoesFirst)
  490.     Game->ComputerTurn(this);
  491. }
  492.  
  493. //
  494. //
  495. //
  496. void
  497. TGameWindow::CmGameOptions()
  498. {
  499.   TGameOptionsBox(this, Game).Execute();
  500. }
  501.  
  502. //
  503. //
  504. //
  505. void TGameWindow::CmAbout()
  506. {
  507.   TGameAboutBox(this).Execute();
  508. }
  509.  
  510. //----------------------------------------------------------------------------
  511.  
  512. //
  513. //
  514. //
  515. class TTTTGameApp : public TApplication {
  516.   public:
  517.     TTTTGameApp() : TApplication() {}
  518.  
  519.     void InitMainWindow();
  520. };
  521.  
  522. //
  523. //
  524. //
  525. void
  526. TTTTGameApp::InitMainWindow()
  527. {
  528.   EnableBWCC();
  529.   MainWindow = new TGameWindow(0, "TicTacToe");
  530. }
  531.  
  532. //
  533. //
  534. //
  535. int
  536. OwlMain(int /*argc*/, char* /*argv*/ [])
  537. {
  538.   return TTTTGameApp().Run();
  539. }
  540.