home *** CD-ROM | disk | FTP | other *** search
/ Mac-Source 1994 July / Mac-Source_July_1994.iso / C and C++ / Entertainment / Concentration / Concentration.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-02-25  |  12.6 KB  |  604 lines  |  [TEXT/KAHL]

  1. /*
  2.  * - Need to check whether icons are distinct.  This is particularly a
  3.  * problem if a 'cicn' B/W image and an 'ICON' are similar on a B/W machine.
  4.  * - Change ReadPieces() to read a particular file.
  5.  * - Should put max/default/size stuff into a structure and pass it around.
  6.  */
  7.  
  8. # include    "TransSkel.h"
  9. # if ENABLE_DEBUG
  10. # include    "Debug.h"
  11. # endif ENABLE_DEBUG
  12.  
  13. # include    "Concentration.h"
  14.  
  15.  
  16. # define    normalHilite    0
  17. # define    dimHilite        255
  18.  
  19. # define    returnKey    '\r'
  20. # define    enterKey    3
  21.  
  22. /*
  23.  * Possible game states
  24.  */
  25.  
  26. typedef enum
  27. {
  28.     waitFirstChoice = 1,    /* waiting for current player's first choice */
  29.     waitSecondChoice,        /* waiting for current player's second choice */
  30.     waitNextPlayer,            /* waiting for click in "Next Player" button */
  31.     waitNewGame,            /* game over; waiting for new one to begin */
  32.     boardSizeTooBig
  33. };
  34.  
  35.  
  36. static BlobSetHandle    receptors = (BlobSetHandle) nil;
  37. static BlobHandle        firstChoice = (BlobHandle) nil;
  38. static BlobHandle        secondChoice = (BlobHandle) nil;
  39.  
  40. static ControlHandle    nextPlayerBtn;
  41. static ControlHandle    newGameBtn;
  42.  
  43. WindowPtr    gameWind;
  44.  
  45. short    nPairs;
  46. short    pairsLeft;
  47.  
  48. static GameBoard    largePieceBoard =
  49. {
  50.     largePiece,
  51.     maxLargeRows, maxLargeCols,
  52.     defLargeRows, defLargeCols,
  53.     defLargeRows, defLargeCols,
  54.     iconSize,
  55.     iconGap,
  56.     0
  57.     /* piece array left unspecified */
  58. };
  59. static GameBoard    smallPieceBoard =
  60. {
  61.     smallPiece,
  62.     maxSmallRows, maxSmallCols,
  63.     defSmallRows, defSmallCols,
  64.     defSmallRows, defSmallCols,
  65.     sitmSize,
  66.     sitmGap,
  67.     0
  68.     /* piece array left unspecified */
  69. };
  70.  
  71. GameBoard    *gb = &largePieceBoard;
  72.  
  73. static short    gameState;
  74. static short    movesMade = 0;
  75.  
  76.  
  77. static short
  78. GetButtonHilite (ControlHandle ctrl)
  79. {
  80.     return ((**ctrl).contrlHilite);
  81. }
  82.  
  83.  
  84. /*
  85.  * Read 'cicn', 'ICON', and 'SICN' resources from application resource
  86.  * fork.  Read color resources first in preference to the B/W resources.
  87.  */
  88.  
  89. void
  90. ReadPieces (void)
  91. {
  92. GameBoard    *tmpGB;
  93. short        nRsrcs;
  94. short        i, j, k;
  95. Handle        h;
  96. SitmHandle    sitmHandle;
  97. short        resID;
  98. Str255        resName;
  99. ResType        resType;
  100.  
  101. # if ENABLE_DEBUG
  102.     DisplayCString ("Number of 'cicn' resources: ");
  103.     DisplayShort (Count1Resources ('cicn'));
  104.     DisplayLn ();
  105.     DisplayCString ("Number of 'ICON' resources: ");
  106.     DisplayShort (Count1Resources ('ICON'));
  107.     DisplayLn ();
  108.     DisplayCString ("Number of 'SICN' resources: ");
  109.     DisplayShort (Count1Resources ('SICN'));
  110.     DisplayLn ();
  111. # endif
  112.  
  113.     tmpGB = &largePieceBoard;
  114.     tmpGB->nPieces = 0;
  115.  
  116.     nRsrcs = Count1Resources ('cicn');
  117.     for (i = 1; i <= nRsrcs; i++)
  118.     {
  119.         if (tmpGB->nPieces >= maxPieces)
  120.             break;
  121.         SetResLoad (false);
  122.         h = GetIndResource ('cicn', i);
  123.         SetResLoad (true);
  124.         if (h == (Handle) nil)
  125.             break;
  126.         GetResInfo (h, &resID, &resType, resName);
  127.         h = (Handle) GetCIcon (resID);
  128.         if (h == (Handle) nil)
  129.             break;
  130.         tmpGB->piece[tmpGB->nPieces].pieceType = cicnType;
  131.         tmpGB->piece[tmpGB->nPieces].pieceData = h;
  132.         ++tmpGB->nPieces;
  133.     }
  134.  
  135.     nRsrcs = Count1Resources ('ICON');
  136.     for (i = 1; i <= nRsrcs; i++)
  137.     {
  138.         if (tmpGB->nPieces >= maxPieces)
  139.             break;
  140.         h = GetIndResource ('ICON', i);
  141.         if (h == (Handle) nil)
  142.             break;
  143.         DetachResource (h);
  144.         tmpGB->piece[tmpGB->nPieces].pieceType = iconType;
  145.         tmpGB->piece[tmpGB->nPieces].pieceData = h;
  146.         ++tmpGB->nPieces;
  147.     }
  148. # if ENABLE_DEBUG
  149. DisplayCString ("Large pieces: ");
  150. DisplayShort (tmpGB->nPieces);
  151. DisplayLn ();
  152. # endif
  153.  
  154.     tmpGB = &smallPieceBoard;
  155.     tmpGB->nPieces = 0;
  156.  
  157.     nRsrcs = Count1Resources ('SICN');
  158.     for (i = 1; i <= nRsrcs; i++)
  159.     {
  160.         h = GetIndResource ('SICN', i);
  161.         if (h == (Handle) nil)
  162.             break;
  163.         j = GetHandleSize ((Handle) h) / sizeof (Sitm);
  164.         for (k = 0; k < j; k++)
  165.         {
  166.             if (tmpGB->nPieces >= maxPieces)
  167.                 break;
  168.             sitmHandle = (SitmHandle) NewHandle (sizeof (Sitm));
  169.             if (sitmHandle == (SitmHandle) nil)
  170.                 break;
  171.             HLock (h);
  172.             HLock ((Handle) sitmHandle);
  173.             **sitmHandle = (**(SicnHandle) h)[k];
  174.             HUnlock (h);
  175.             HUnlock ((Handle) sitmHandle);
  176.             tmpGB->piece[tmpGB->nPieces].pieceType = sicnType;
  177.             tmpGB->piece[tmpGB->nPieces].pieceData = (Handle) sitmHandle;
  178.             ++tmpGB->nPieces;
  179.         }
  180.         ReleaseResource (h);
  181.     }
  182. # if ENABLE_DEBUG
  183. DisplayCString ("Small pieces: ");
  184. DisplayShort (tmpGB->nPieces);
  185. DisplayLn ();
  186. # endif
  187. }
  188.  
  189.  
  190.  
  191. /*
  192.  * Set up the list of pieces for play.
  193.  */
  194.  
  195. void
  196. InitBoard (short sizeType)
  197. {
  198. BlobHandle    b;
  199. Rect    r;
  200. short    i, h, v;
  201.  
  202.     if (receptors != (BlobSetHandle) nil)
  203.     {
  204.         HideBlobSet (receptors);
  205.         DisposeBlobSet (receptors);
  206.         receptors = (BlobSetHandle) nil;
  207.     }
  208.  
  209.     if (sizeType == largePiece)
  210.         gb = &largePieceBoard;
  211.     else
  212.         gb = &smallPieceBoard;
  213.  
  214.     nPairs = (gb->nRows * gb->nCols) / 2;
  215.     receptors = NewBlobSet ();
  216.     for (i = 0; i < nPairs * 2; ++i)
  217.     {
  218.         h = gbHOffset + (gb->pieceSize + gb->pieceGap) * (i % gb->nCols);
  219.         v = gbVOffset + (gb->pieceSize + gb->pieceGap) * (i / gb->nCols);
  220.         MakeBlob (receptors, h, v, gb->sizeType);
  221.     }
  222. }
  223.  
  224.  
  225. /*
  226.  * Force window to be frontmost and everything to be redrawn.
  227.  * Call this after setting up for a new round of the game.
  228.  */
  229.  
  230. static void
  231. ForceRedraw (void)
  232. {
  233.     InvalRect (&gameWind->portRect);
  234.     SelectWindow (gameWind);            /* select and show in case this */
  235.     ShowWindow (gameWind);                /* is the first game */
  236.     SkelDoUpdates ();
  237.     if (gameState != boardSizeTooBig)
  238.         FlashPlayerFrame (sb->player);
  239. }
  240.  
  241.  
  242. /*
  243.  * Set up for a new round of the game.
  244.  *
  245.  * First check whether there are enough game pieces available for the
  246.  * given board size. (nPieces must be at least half nRows * nCols.)
  247.  *
  248.  * If enough pieces, randomize the piece array.  This allows new pieces
  249.  * to come into the board if there are more pieces available than needed
  250.  * to fill the board each time.
  251.  *
  252.  * Then assign each piece used to two different blobs and shuffle the
  253.  * blobs to randomize the images on the board.
  254.  */
  255.  
  256. void
  257. NewGameRound (void)
  258. {
  259. BlobHandle    b;
  260. short    i, j;
  261. Piece    tmpPiece;
  262.  
  263.     if (gb->nPieces * 2 < gb->nRows * gb->nCols)
  264.     {
  265.         gameState = boardSizeTooBig;
  266.         HideControl (nextPlayerBtn);
  267.         HideControl (newGameBtn);
  268.         ForceRedraw ();
  269.         return;
  270.     }
  271.  
  272.     gameState = waitFirstChoice;
  273.  
  274.     ResetScoreBoard (movesMade > 0);
  275.     movesMade = 0;
  276.  
  277.     /*
  278.      * Shuffle all available pieces, then pick the first nPairs of
  279.      * them.  This randomizes the pieces that get used for each
  280.      * round of the game.
  281.      */
  282.  
  283.     for (i = 0; i < gb->nPieces; ++i)
  284.     {
  285.         j = BlobRand (gb->nPieces - 1);
  286.         tmpPiece = gb->piece[i];
  287.         gb->piece[i] = gb->piece[j];
  288.         gb->piece[j] = tmpPiece;
  289.     }
  290.  
  291.     HideBlobSet (receptors);
  292.     ThawBlobSet (receptors);
  293.  
  294.     /*
  295.      * Make new image assignments
  296.      */
  297.  
  298.     for (i = 0; i < nPairs; i++)
  299.     {
  300.         SetBRefCon (GetBlobHandle (receptors, i), i);
  301.         SetBRefCon (GetBlobHandle (receptors, i + nPairs), i);
  302.     }
  303.  
  304.     for (b = FirstBlob (receptors); b != nil; b = NextBlob (b))
  305.         GlueGlob (b, b);
  306.     ShuffleBlobSet (receptors);
  307.     EnableBlobSet (receptors);
  308.     pairsLeft = nPairs;
  309.     HiliteControl (nextPlayerBtn, dimHilite);
  310.     SkelDrawButtonOutline (nextPlayerBtn);
  311.     ShowControl (nextPlayerBtn);
  312.     ShowControl (newGameBtn);
  313.  
  314.     ForceRedraw ();
  315. }
  316.  
  317.  
  318. void
  319. ShowAnswer (void)
  320. {
  321. short    i;
  322.  
  323.     UnglueGlobSet (receptors);
  324.     gameState = waitNewGame;
  325.     pairsLeft = 0;
  326.     i = sb->player;
  327.     sb->player = -1;
  328.     DrawPlayerInfo (i);    /* erase frame around last player */
  329.     HiliteControl (nextPlayerBtn, dimHilite);
  330.     SkelEraseButtonOutline (nextPlayerBtn);
  331.     SkelDrawButtonOutline (newGameBtn);
  332. }
  333.  
  334.  
  335. /*
  336.  * Undo the current player's choices by covering them back up,
  337.  * and go to the next player.  This should only be called after
  338.  * a player has selected two non-matching pieces.
  339.  */
  340.  
  341. static void
  342. NextPlayer (void)
  343. {
  344. short    oldPlayer;
  345.  
  346.     /* cover the pieces back up */
  347.     
  348.     GlueGlob (firstChoice, firstChoice);
  349.     GlueGlob (secondChoice, secondChoice);
  350.  
  351.     /* disable the "Next Player" button */
  352.  
  353.     HiliteControl (nextPlayerBtn, dimHilite);
  354.     SkelDrawButtonOutline (nextPlayerBtn);
  355.  
  356.     /* thaw the blobs so they can be clicked again */
  357.  
  358.     ThawBlobSet (receptors);
  359.  
  360.     oldPlayer = sb->player;
  361.     if (++sb->player >= sb->nPlayers)
  362.         sb->player = 0;
  363.     DrawPlayerInfo (oldPlayer);
  364.     DrawPlayerInfo (sb->player);
  365.     FlashPlayerFrame (sb->player);
  366.  
  367.     gameState = waitFirstChoice;
  368. }
  369.  
  370.  
  371. static pascal void
  372. Mouse (Point pt, long t, short mods)
  373. {
  374. BlobHandle        b;
  375. ControlHandle    ctl;
  376. short            i;
  377.  
  378.     if (gameState == boardSizeTooBig)
  379.         return;
  380.  
  381.     if (FindControl    (pt, gameWind, &ctl))
  382.     {
  383.         if (TrackControl (ctl, pt, nil))    /* button hit? */
  384.         {
  385.             if (ctl == newGameBtn)
  386.                 NewGameRound ();
  387.             else
  388.                 NextPlayer ();
  389.         }
  390.     }
  391.     else if (FindBlob (pt, receptors, &b) != 0 && BGlob (b) != nil)
  392.     {
  393.         ++movesMade;
  394.  
  395.         UnglueGlob (b);
  396.         if (gameState == waitFirstChoice)    /* player's first choice */
  397.         {
  398.             gameState = waitSecondChoice;
  399.             firstChoice = b;
  400.         }
  401.         else                                /* player's second choice */
  402.         {
  403.             secondChoice = b;
  404.  
  405.             if (GetBRefCon (firstChoice) == GetBRefCon (secondChoice))
  406.             {
  407.                 /*
  408.                  * It's a match.
  409.                  * Leave pieces uncovered, increment player's score.
  410.                  * Let same player keep playing.
  411.                  */
  412.                 --pairsLeft;
  413.                 ++sb->score[sb->player];
  414.                 DrawPlayerInfo (sb->player);
  415.                 if (pairsLeft)
  416.                 {
  417.                     gameState = waitFirstChoice;
  418.                     FlashPlayerFrame (sb->player);
  419.                 }
  420.                 else
  421.                 {
  422.  
  423.                     gameState = waitNewGame;
  424.                     i = sb->player;
  425.                     sb->player = -1;
  426.                     DrawPlayerInfo (i);    /* erase frame around last player */
  427.                     SkelEraseButtonOutline (nextPlayerBtn);
  428.                     SkelDrawButtonOutline (newGameBtn);
  429.                 }
  430.             }
  431.             else                        /* not a match; enable "Next Player" button */
  432.             {
  433.                 /*
  434.                  * It's not a match.  Enable the "Next Player" button, which must
  435.                  * be clicked to allow the next player to proceed.  The blobs are
  436.                  * frozen so they can't be clicked.
  437.                  */
  438.                 HiliteControl (nextPlayerBtn, normalHilite);
  439.                 SkelDrawButtonOutline (nextPlayerBtn);
  440.                 FreezeBlobSet (receptors);
  441.                 gameState = waitNextPlayer;
  442.             }
  443.         }
  444.             
  445.     }
  446. }
  447.  
  448.  
  449. static pascal void
  450. Key (short c, short code, short modifiers)
  451. {
  452.     if (gameState == boardSizeTooBig)
  453.         return;
  454.  
  455.     if (c == returnKey || c == enterKey)    /* interpret carriage return/enter as */
  456.     {                                        /* button click (if button is hilited) */
  457.         if (gameState == waitNewGame)
  458.         {
  459.             if (GetButtonHilite (newGameBtn) == normalHilite)
  460.             {
  461.                 SkelFlashButton (newGameBtn);
  462.                 NewGameRound ();
  463.             }
  464.         }
  465.         else
  466.         {
  467.             if (GetButtonHilite (nextPlayerBtn) == normalHilite)
  468.             {
  469.                 SkelFlashButton (nextPlayerBtn);
  470.                 NextPlayer ();
  471.             }
  472.         }
  473.     }
  474. }
  475.  
  476.  
  477.  
  478. static pascal void
  479. Update (Boolean resized)
  480. {
  481. short    i;
  482.  
  483.     EraseRect (&gameWind->portRect);
  484.  
  485.     if (gameState == boardSizeTooBig)
  486.     {
  487.         MoveTo (10, 30);
  488.         DrawString ("\pBoard size too big for number of pieces available.");
  489.         MoveTo (10, 50);
  490.         DrawString ("\pPlease select a smaller board size.");
  491.         return;
  492.     }
  493.  
  494.     DrawControls (gameWind);
  495.     if (gameState == waitNewGame)
  496.         SkelDrawButtonOutline (newGameBtn);
  497.     else
  498.         SkelDrawButtonOutline (nextPlayerBtn);
  499.     DrawBlobSet (receptors);
  500.  
  501.     DrawScoreBoard ();
  502. }
  503.  
  504.  
  505. /*
  506.  * If the "Next Player" button is visible (i.e., board size isn't too big),
  507.  * then hilite it according to whether or not the state is waitNextPlayer.
  508.  * If the window's not active, the button is always dim.
  509.  *
  510.  * "New Game" button is always active then the window is active.
  511.  */
  512.  
  513. static pascal void
  514. Activate (Boolean active)
  515. {
  516. short    hilite;
  517. Rect    r;
  518.  
  519.     if (gameState != boardSizeTooBig)
  520.     {
  521.         if (active)
  522.             hilite = (gameState == waitNextPlayer ? normalHilite : dimHilite);
  523.         else
  524.             hilite = dimHilite;
  525.         if (SkelHiliteControl (nextPlayerBtn, hilite))    /* inval on state change */
  526.         {
  527.             r = (**nextPlayerBtn).contrlRect;
  528.             InsetRect (&r, -4, -4);
  529.             InvalRect (&r);
  530.         }
  531.         hilite = (active ? normalHilite : dimHilite);
  532.         if (SkelHiliteControl (newGameBtn, hilite))    /* inval on state change */
  533.         {
  534.             r = (**newGameBtn).contrlRect;
  535.             InsetRect (&r, -4, -4);
  536.             InvalRect (&r);
  537.         }
  538.     }
  539. }
  540.  
  541.  
  542.  
  543. /*
  544.  * Dispose of game window
  545.  */
  546.  
  547. static pascal void
  548. DoClobber (void)
  549. {
  550.     DisposeWindow (gameWind);
  551. }
  552.  
  553.  
  554. /*
  555.  * Set up game window
  556.  */
  557.  
  558. void
  559. WindInit (void)
  560. {
  561. Rect    r;
  562. long    result;
  563.  
  564.     SetRect (&r, 0, 0, 487, 255);
  565.     if (SkelQuery (skelQHasColorQD))
  566.     {
  567.         gameWind = NewCWindow (nil, &r, "\pConcentration", false,
  568.                             noGrowDocProc, (WindowPtr) -1L, false, 0L);
  569.     }
  570.     else
  571.     {
  572.         gameWind = NewWindow (nil, &r, "\pConcentration", false,
  573.                             noGrowDocProc, (WindowPtr) -1L, false, 0L);
  574.     }
  575.  
  576.     SkelPositionWindow (gameWind, skelPositionOnMainDevice,
  577.                                     FixRatio (1, 2), FixRatio (1, 5));
  578.     SkelWindow (gameWind,
  579.                 Mouse,            /* mouse clicks */
  580.                 Key,            /* key clicks */
  581.                 Update,            /* updates */
  582.                 Activate,        /* activate/deactivate events */
  583.                 nil,            /* close window */
  584.                 DoClobber,        /* dispose of window */
  585.                 nil,            /* idle proc */
  586.                 false);            /* irrelevant, since no idle proc */
  587.  
  588.     TextFont (0);
  589.     TextSize (0);
  590.  
  591.     SetRect (&r, 0, 0, 90, 20);
  592.     OffsetRect (&r,
  593.                 sbHOffset + (sbWidth - 90) / 2,
  594.                 sbVOffset + sbHeight + 15);
  595.     nextPlayerBtn = NewControl (gameWind, &r, "\pNext Player", false, 0, 0, 1,
  596.                             pushButProc, 0L);
  597.     OffsetRect (&r, 0, 30);
  598.     newGameBtn = NewControl (gameWind, &r, "\pNew Game", false, 0, 0, 1,
  599.                             pushButProc, 0L);
  600.  
  601.     InitBoard (largePiece);
  602.     NewGameRound ();                /* selects and shows window */
  603. }
  604.