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

  1. //----------------------------------------------------------------------------
  2. // ObjectWindows - (C) Copyright 1992,1994 by Borland International
  3. //
  4. //   TicTacToe Demo Program
  5. //
  6. //   Plays a game of TicTacToe with the user.
  7. //
  8. //   TTTTGameApp - Main TicTacToe application, derived from TApplication
  9. //   TGameView -   View window for the game, derived from TToolBox
  10. //   Square - Game squares (gadgets), derived from TButtonGadget
  11. //   TGameAboutBox - A TDialog box for info about TicTacToe
  12. //   TGameOptionsBox - A TDialog box for setting TicTacToe options
  13. //   YouMeRadioButton - A radio button which controls game settings
  14. //   XORadioButton - A radio button which controls game settings
  15. //
  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 <owl/buttonga.h>
  27. #include <owl/toolbox.h>
  28. #include <owl/celarray.h>
  29. #include <owl/buttonga.h>
  30. #include "ttt.h"
  31.  
  32. #define WM_VIEWSYNC WM_USER + 100
  33.  
  34. static const int freeMasks[] = {
  35.   0x006, 0x005, 0x003, 0x030, 0x028, 0x018, 0x180, 0x140, 0x0C0, // row
  36.   0x048, 0x041, 0x009, 0x090, 0x082, 0x012, 0x120, 0x104, 0x024, // col
  37.   0x110, 0x101, 0x011, 0x050, 0x044, 0x014                       // diagonal
  38. };
  39.  
  40. static const int winningMasks[] = {
  41.   0x1C0, 0x038, 0x007, 0x124, 0x092, 0x049, 0x111, 0x054
  42. };
  43.  
  44. //
  45. //
  46. //
  47. static void
  48. freeSquare(int mask, int i, int& square)
  49. {
  50.   int mode = i/9; // row, col, or diag
  51.   if (mode == 0)
  52.     mask ^= 0x007 << (i/3)*3;
  53.   else if (mode == 1)
  54.     mask ^= 0x049 << (i%9)/3;
  55.   else if (((i%9)/3) == 0)
  56.     mask ^= 0x111;
  57.   else
  58.     mask ^= 0x054;
  59.   int j, test;
  60.   for (j = 0, test = 1; test; test <<= 1, j++)
  61.     if (test & mask)
  62.       break;
  63.   square = j;
  64. }
  65.  
  66. //----------------------------------------------------------------------------
  67.  
  68. enum TSquareState {
  69.   SqsEmpty,
  70.   SqsX,
  71.   SqsO,
  72.   SqsCount
  73. };
  74.  
  75. class TGame {
  76.   public:
  77.     TGame(int dim);
  78.  
  79.     void New();
  80.     void SetView(TWindow* view) {View = view;}
  81.  
  82.     int  GetDimension() const {return Dim;}  // Dim x Dim game
  83.     bool IsOver();// const;
  84.     bool IsWon();// const;
  85.     bool IsPlaying() const {return Playing;}
  86.     TSquareState GetState(int sq) const;
  87. //    TSquareState GetState(int row, int col) const
  88. //        {return GetState(row*GetDimension + col);}
  89.  
  90.     void UserTurn(int sq);
  91.     void ComputerTurn();
  92.  
  93.   private:
  94.     int          Dim;
  95.     int          UserBoardMap;
  96.     int          ComputerBoardMap;
  97.     TSquareState UserSide;
  98.     TSquareState ComputerSide;
  99.     bool         ComputerGoesFirst;
  100.     bool         Playing;
  101.     TWindow*     View;
  102.     friend class TGameOptionsBox; // sleazy
  103.  
  104.   private:
  105.     void SetState(int sq, TSquareState state);
  106.     void MakeAMark(int sq, TSquareState mark);
  107.     bool IsAWinner(int sq) const;
  108. };
  109.  
  110. TGame::TGame(int dim)
  111. {
  112.   UserSide = SqsX;
  113.   ComputerSide = SqsO;
  114.   ComputerGoesFirst = false;
  115.   Playing = true;
  116.   UserBoardMap = ComputerBoardMap = 0;
  117.   Dim = dim;
  118. }
  119.  
  120. void
  121. TGame::New()
  122. {
  123.   Playing = true;
  124.   UserBoardMap = 0;
  125.   ComputerBoardMap = 0;
  126.   ComputerSide = UserSide==SqsX ? SqsO : SqsX;
  127.  
  128.   if (ComputerGoesFirst)
  129.     ComputerTurn();
  130.  
  131.   if (View)
  132.    View->PostMessage(WM_VIEWSYNC, 0xFFFF);
  133. }
  134.  
  135. void
  136. TGame::MakeAMark(int sq, TSquareState mark)
  137. {
  138.   SetState(sq, mark);
  139.   if (View)
  140.     View->PostMessage(WM_VIEWSYNC, sq);
  141. }
  142.  
  143. bool
  144. TGame::IsOver()
  145. {
  146.   if ((UserBoardMap|ComputerBoardMap) == 0x1FF)
  147.     Playing = false;
  148.   return !Playing;
  149. }
  150.  
  151. bool
  152. TGame::IsWon()
  153. {
  154.   for (int i = 0; i < 8; i++)
  155.     if ((UserBoardMap & winningMasks[i]) == winningMasks[i] ||
  156.         (ComputerBoardMap & winningMasks[i]) == winningMasks[i]) {
  157.       Playing = false;
  158.       return true;
  159.     }
  160.   return false;
  161. }
  162.  
  163. TSquareState
  164. TGame::GetState(int sq) const
  165. {
  166.   int mask = 1 << sq;
  167.  
  168.   if (ComputerBoardMap & mask)
  169.     return ComputerSide;
  170.   if (UserBoardMap & mask)
  171.     return UserSide;
  172.   return SqsEmpty;
  173. }
  174.  
  175. void
  176. TGame::SetState(int sq, TSquareState state)
  177. {
  178.   int mask = 1 << sq;
  179.  
  180.   if (state == SqsEmpty) {
  181.     ComputerBoardMap &= ~mask;
  182.     UserBoardMap &= ~mask;
  183.  
  184.   } else if (state == UserSide) {
  185.     ComputerBoardMap &= ~mask;
  186.     UserBoardMap |= mask;
  187.  
  188.   } else {
  189.     ComputerBoardMap |= mask;
  190.     UserBoardMap &= ~mask;
  191.   }
  192. }
  193.  
  194. bool
  195. TGame::IsAWinner(int sq) const
  196. {
  197.   int map = ComputerBoardMap | (1 << sq);
  198.   for (int i = 0; i < 8; i++)
  199.     if ((map & winningMasks[i]) == winningMasks[i])
  200.       return true;
  201.   return false;
  202. }
  203.  
  204. //
  205. //
  206. //
  207. void
  208. TGame::UserTurn(int sq)
  209. {
  210.   if (GetState(sq) == SqsEmpty)
  211.     MakeAMark(sq, UserSide);
  212. }
  213.  
  214. //
  215. //
  216. //
  217. void
  218. TGame::ComputerTurn()
  219. {
  220.   bool madeMove = false;
  221.  
  222.   // Look for a winning move first.
  223.   //
  224.   {
  225.     for (int i = 0; i < Dim*Dim; i++) {
  226.       if (GetState(i) == SqsEmpty && IsAWinner(i)) {
  227.         MakeAMark(i, ComputerSide);
  228.         madeMove = true;
  229.         break;
  230.       }
  231.     }
  232.   }
  233.  
  234.   // Look for a blocking move. This is the key to not losing the game!
  235.   //
  236.   if (!madeMove) {
  237.     for (int i = 0; i < 24; i++)
  238.       if ((UserBoardMap & freeMasks[i]) == freeMasks[i]) {
  239.         int sq;
  240.         freeSquare(freeMasks[i], i, sq);
  241.         if (GetState(sq) == ComputerSide)
  242.           continue;
  243.         MakeAMark(sq, ComputerSide);
  244.         madeMove = true;
  245.         break;
  246.       }
  247.   }
  248.  
  249.   // Nothing to block, go to the middle if empty, else first open corner, else
  250.   // first open edge
  251.   // Game can be beaten if user knows how computer plays: try 5,7,8, & 2 to win
  252.   //
  253.   if (!madeMove) {
  254.     int mask = UserBoardMap|ComputerBoardMap;
  255.     if (!(mask & 0x010)) {
  256.       MakeAMark(4, ComputerSide);    // #4 is the middle of 3x3
  257.  
  258.     }
  259.     else if ((mask & 0x145) != 0x145) {  // corner mask
  260.       int sq;
  261.       if (!(mask & 0x001))
  262.         sq = 0;
  263.       else if (!(mask & 0x004))
  264.         sq = 2;
  265.       else if (!(mask & 0x040))
  266.         sq = 6;
  267.       else  // 0x100
  268.         sq = 8;
  269.       MakeAMark(sq, ComputerSide);    // #4 is the middle of 3x3
  270.  
  271.     }
  272.     else {
  273.       int i;
  274.       for (i = 0; mask & 1; mask >>= 1)
  275.         i++;
  276.       MakeAMark(i, ComputerSide);
  277.     }
  278.   }
  279.  
  280.   // See if the game is over now.
  281.   //
  282.   if (IsWon() || IsOver())
  283.     Playing = false;
  284. }
  285.  
  286. //----------------------------------------------------------------------------
  287.  
  288. class TSquare : public TButtonGadget {
  289.   public:
  290.     TSquare(int id);
  291.    ~TSquare();
  292.  
  293.     void      SetGlyph(int g);
  294.     void      Paint(TDC& dc);
  295.  
  296.   protected:
  297.     void      Activate(TPoint& p);
  298.     int       Glyph;
  299.  
  300.   private:
  301.     static TCelArray*  StateCels[3];
  302. };
  303. typedef TSquare* PTSquare;
  304.  
  305. TCelArray* TSquare::StateCels[SqsCount]; // entry for each state
  306. static TResId StateResId[SqsCount] = { IDB_EMPTY, IDB_X, IDB_O };
  307.  
  308. TSquare::TSquare(int id)
  309. :
  310.   TButtonGadget(IDB_EMPTY, id, NonExclusive, true, Up, false),
  311.   Glyph(0)
  312. {
  313. }
  314.  
  315. TSquare::~TSquare()
  316. {
  317.   CelArray = 0;
  318. }
  319.  
  320. void
  321. TSquare::SetGlyph(int g)
  322. {
  323.   Glyph = g;
  324.   SetButtonState(Glyph ? Down : Up);
  325.   Invalidate();
  326. }
  327.  
  328. void
  329. TSquare::Paint(TDC& dc)
  330. {
  331.   // Fool button gadget into making our special state cel arrays
  332.   //
  333.   TSize test;
  334.   test = dc.GetWindowExt();
  335.   test = dc.GetViewportExt();
  336.   test = dc.GetWindowOrg();
  337.   test = dc.GetViewportOrg();
  338.  
  339.   delete CelArray;   // delete any that might have been created
  340.   for (int i = 0; i < SqsCount; i++)
  341.     if (!StateCels[i]) {
  342.       ResId = StateResId[i];
  343.       CelArray = 0;     
  344.       BuildCelArray();
  345.       StateCels[i] = CelArray;
  346.     }
  347.  
  348.   // Slip in the correct cel array just before painting
  349.   //
  350.   CelArray = StateCels[Glyph];
  351.   TButtonGadget::Paint(dc);
  352.   CelArray = 0;
  353.   for(int i=0; i<3; i++)  {
  354.    delete StateCels[i];
  355.    StateCels[i] = 0;
  356.   }
  357. }
  358.  
  359. //
  360. // Modify activate behavior to make the buttons 'sticky'
  361. //
  362. void
  363. TSquare::Activate(TPoint& pt)
  364. {
  365.   if (!Glyph) {
  366.     TButtonGadget::Activate(pt);
  367.     Window->PostMessage(WM_COMMAND, GetId()); // post to owner view too
  368.   } else
  369.     CancelPressed(pt);
  370. }
  371.  
  372. //----------------------------------------------------------------------------
  373.  
  374. class TGameView : public TToolBox {
  375.   public:
  376.     TGameView(TGame* game);
  377.    ~TGameView();
  378.  
  379.   private:
  380.     void      SetupWindow();
  381.     TResult   EvCommand(uint id, HWND hWndCtl, uint notifyCode);
  382.     void      EvCommandEnable(TCommandEnabler& ce);
  383.     TResult   EvViewSync(TParam1 param1, TParam2 param2);
  384.  
  385.     enum TEndReason { Scratch, UserWon, ComputerWon };
  386.     void      GameOver(TEndReason why);
  387.  
  388.     TGame*    Game;
  389.     TSquare** Board;
  390.     int       Dim;
  391.  
  392.   DECLARE_RESPONSE_TABLE(TGameView);
  393. };
  394.  
  395. DEFINE_RESPONSE_TABLE1(TGameView, TToolBox)
  396.   EV_MESSAGE(WM_VIEWSYNC, EvViewSync),
  397. END_RESPONSE_TABLE;
  398.  
  399. TGameView::TGameView(TGame* game)
  400. :
  401.   TToolBox(0, game->GetDimension()),
  402.   Game(game)
  403. {
  404.   Dim = Game->GetDimension();
  405.   Board = new PTSquare[Dim*Dim];
  406.  
  407.   for (int i = 0; i < Dim; i++)
  408.     for (int j = 0; j < Dim; j++) {
  409.       TSquare* sq = new TSquare(CM_SQUARE + i*Dim + j);
  410.       Board[i*Dim + j] = sq;
  411.       Insert(*sq);
  412.     }
  413. }
  414.  
  415. TGameView::~TGameView()
  416. {
  417.   delete[] Board;
  418. }
  419.  
  420. void
  421. TGameView::SetupWindow()
  422. {
  423.   TWindow::SetupWindow();
  424. }
  425.  
  426. void
  427. TGameView::GameOver(TEndReason why)
  428. {
  429.   static char* msgs[] = { "Scratch", "You won!", "I won!" };
  430.   MessageBox(msgs[why], "Game Over", MB_OK);
  431. }
  432.  
  433. TResult
  434. TGameView::EvCommand(uint id, HWND hWndCtl, uint notifyCode)
  435. {
  436.   if (id >= CM_SQUARE
  437.       && id < CM_SQUARE+Game->GetDimension()*Game->GetDimension()
  438.       && !hWndCtl) {
  439.  
  440.     int square = id - CM_SQUARE;
  441.  
  442.     if (Game->GetState(square) != SqsEmpty)// || !Game->IsPlaying())
  443.       return 0;
  444.  
  445.     Game->UserTurn(square);
  446.  
  447.     if (Game->IsWon())
  448.       GameOver(UserWon);
  449.  
  450.     else if (Game->IsOver())
  451.       GameOver(Scratch);
  452.  
  453.     else {
  454.       Game->ComputerTurn();
  455.  
  456.       // See if the game is over now.
  457.       //
  458.       if (Game->IsWon())
  459.         GameOver(ComputerWon);
  460.  
  461.       else if (Game->IsOver())
  462.         GameOver(Scratch);
  463.     }
  464.     return 0;
  465.   }
  466.   return TToolBox::EvCommand(id, hWndCtl, notifyCode);
  467. }
  468.  
  469. void
  470. TGameView::EvCommandEnable(TCommandEnabler& ce)
  471. {
  472.   if (ce.Id >= CM_SQUARE
  473.       && ce.Id < CM_SQUARE+Game->GetDimension()*Game->GetDimension())
  474.     ce.Enable(Game->IsPlaying());
  475.   else
  476.     TToolBox::EvCommandEnable(ce);
  477. }
  478.  
  479. LRESULT
  480. TGameView::EvViewSync(WPARAM wParam, LPARAM)
  481. {
  482.   if (wParam == 0xFFFF)
  483.     for (int i = 0; i < Dim*Dim; i++)
  484.       Board[i]->SetGlyph(Game->GetState(i));
  485.  
  486.   else
  487.     Board[wParam]->SetGlyph(Game->GetState(wParam));
  488.  
  489.   return 0;
  490. }
  491.  
  492. //----------------------------------------------------------------------------
  493.  
  494. class TGameAboutBox : public TDialog {
  495.   public:
  496.     TGameAboutBox::TGameAboutBox(TWindow* parent);
  497. };
  498.  
  499. TGameAboutBox::TGameAboutBox(TWindow* parent)
  500. :
  501.   TDialog(parent, "IDD_ABOUT")
  502. {
  503.   char aboutstr[] = "Tic Tac Toe\n(C) Borland International\n 1992,1994";
  504.   TStatic* textPtr = new TStatic(this, IDSTATIC, aboutstr, 23, 20, 190,
  505.         45, strlen(aboutstr));
  506.   textPtr->Attr.Style |= SS_CENTER;
  507.   new TButton(this, IDOK, "Ok", 80, 90, 40, 40, true);
  508. }
  509.  
  510. //----------------------------------------------------------------------------
  511.  
  512. class TGameOptionsBox : public TDialog {
  513.   public:
  514.     TGameOptionsBox(TWindow* parent, TGame* game);
  515.  
  516.   private:
  517.     void SetupWindow();
  518.  
  519.     TGroupBox     *YouMeGroup, *XOGroup;
  520.     TRadioButton  *You, *Me, *X, *O;
  521.     TGame*         Game;
  522.  
  523.     void HandleYouMeGroupMsg(UINT);
  524.     void HandleXOGroupMsg(UINT);
  525.  
  526.   DECLARE_RESPONSE_TABLE(TGameOptionsBox);
  527. };
  528. DEFINE_RESPONSE_TABLE1(TGameOptionsBox, TDialog)
  529.   EV_CHILD_NOTIFY_ALL_CODES(IDYOUMEGROUP, HandleYouMeGroupMsg),
  530.   EV_CHILD_NOTIFY_ALL_CODES(IDXOGROUP, HandleXOGroupMsg),
  531. END_RESPONSE_TABLE;
  532.  
  533. TGameOptionsBox::TGameOptionsBox(TWindow* parent, TGame* game)
  534. :
  535.   TDialog(parent, "IDD_OPTIONS"),
  536.   Game(game)
  537. {
  538.   new TButton(this, IDOK, "Ok", 30, 240, 40, 40, true);
  539.   new TButton(this, IDCANCEL, "Cancel", 150, 240, 40, 40, true);
  540.   YouMeGroup = new TGroupBox(this, IDYOUMEGROUP, "Who goes first?", 15,
  541.                      30, 200, 20);
  542.   You = new TRadioButton(this, IDYOU, "You (Human)", 15, 55, 150, 20, YouMeGroup);
  543.   Me = new TRadioButton(this, IDME, "Me (Computer)", 15, 80, 150, 20, YouMeGroup);
  544.   XOGroup = new TGroupBox(this, IDXOGROUP, "I am playing", 15, 120, 200, 20);
  545.   X = new TRadioButton(this, IDX, "X\'s", 15, 145, 50, 20, XOGroup);
  546.   O = new TRadioButton(this, IDO, "O\'s", 15, 170, 50, 20, XOGroup);
  547. }
  548.  
  549. void
  550. TGameOptionsBox::SetupWindow()
  551. {
  552.   TDialog::SetupWindow();
  553.   if (Game->ComputerGoesFirst)
  554.     Me->Check();
  555.   else
  556.     You->Check();
  557.  
  558.   if (Game->UserSide == SqsX)
  559.     O->Check();
  560.   else
  561.     X->Check();
  562. }
  563.  
  564. void
  565. TGameOptionsBox::HandleYouMeGroupMsg(uint)
  566. {
  567.   Game->ComputerGoesFirst = (You->GetCheck() == BF_CHECKED) ? false : true;
  568. }
  569.  
  570. void
  571. TGameOptionsBox::HandleXOGroupMsg(uint)
  572. {
  573.   Game->UserSide = (X->GetCheck() == BF_CHECKED) ? SqsO : SqsX;
  574.   Game->Playing = false;
  575.   Parent->PostMessage(WM_COMMAND, CM_GAMENEW);
  576. }
  577.  
  578. //----------------------------------------------------------------------------
  579.  
  580. class TGameApp : public TApplication {
  581.   public:
  582.    TGameApp() :   TApplication("TicTacToe"), Game(0) {}
  583.    ~TGameApp() { delete Game; }
  584.  
  585.   private:
  586.    void CmGameNew();
  587.    void CmGameOptions();
  588.    void CmAbout();
  589.    void InitMainWindow();
  590.  
  591.    TGame*  Game;
  592.  
  593.   DECLARE_RESPONSE_TABLE(TGameApp);
  594. };
  595.  
  596. DEFINE_RESPONSE_TABLE1(TGameApp, TApplication)
  597.   EV_COMMAND(CM_GAMENEW, CmGameNew),
  598.   EV_COMMAND(CM_GAMEOPTIONS, CmGameOptions),
  599.   EV_COMMAND(CM_ABOUT, CmAbout),
  600. END_RESPONSE_TABLE;
  601.  
  602. void
  603. TGameApp::InitMainWindow()
  604. {
  605.   Game = new TGame(3);                         // game 'document'
  606.   TGameView* gameView = new TGameView(Game);   // view of game
  607.   Game->SetView(gameView);
  608.  
  609.   TFrameWindow* frame = new TFrameWindow(0, GetName(), gameView, true, this);
  610.   frame->Attr.Style &= ~(WS_THICKFRAME|WS_MAXIMIZEBOX);
  611.   frame->SetIcon(this, IDI_TTT);
  612.   frame->AssignMenu("IDM_TTTGAME");
  613.  
  614.   SetMainWindow(frame);
  615.   EnableBWCC();
  616. }
  617.  
  618. void
  619. TGameApp::CmGameNew()
  620. {
  621.   Game->New();
  622. }
  623.  
  624. void
  625. TGameApp::CmGameOptions()
  626. {
  627.   TGameOptionsBox(GetMainWindow(), Game).Execute();
  628. }
  629.  
  630. void
  631. TGameApp::CmAbout()
  632. {
  633.   TGameAboutBox(GetMainWindow()).Execute();
  634. }
  635.  
  636. //----------------------------------------------------------------------------
  637.  
  638. int
  639. OwlMain(int /*argc*/, char* /*argv*/ [])
  640. {
  641.   return TGameApp().Run();
  642. }
  643.