home *** CD-ROM | disk | FTP | other *** search
/ Otherware / Otherware_1_SB_Development.iso / mac / game / board / tower.sit / hanoi.c next >
Encoding:
C/C++ Source or Header  |  1989-09-26  |  33.1 KB  |  1,364 lines

  1. /******************************************************
  2.  * Towers of Hanoi   W.M. Leue
  3.  * Rev 0.0  11/4/84  Original version written in Macintosh Pascal.
  4.  * Rev 1.0   1/23/88  Translated to Lightspeed C 2.0
  5.  * Rev 2.0   9/5/89   Changed recursive to iterative algorithm.
  6.  ******************************************************/
  7.  
  8. #include "Quickdraw.h"
  9. #include "MacTypes.h"
  10. #include "MenuMgr.h"
  11. #include "WindowMgr.h"
  12. #include "EventMgr.h"
  13. #include "ControlMgr.h"
  14. #include "FontMgr.h"
  15. #include "DialogMgr.h"
  16. #include "SoundMgr.h"
  17.  
  18. /* Graphics-Related Literals */
  19.  
  20. #define    WINDOW_WIDTH    500
  21. #define    WINDOW_HEIGHT    290
  22. #define    WMARGIN            4
  23.  
  24. #define    NUMBER_OF_PINS    3
  25. #define    SUM_OF_PINS        3
  26. #define    NUM_DISKS        5
  27. #define    MAX_DISKS        10
  28. #define    BASE_WIDTH        130
  29. #define    BASE_HEIGHT        20
  30. #define    LEFT_MOST        35
  31. #define    BOTTOM_MOST        250
  32. #define    BASE_SPACING        150
  33. #define    PIN_HEIGHT        160
  34. #define    PIN_WIDTH        10
  35. #define    PIN_MARGIN        10
  36. #define    DISK_HEIGHT        15
  37. #define    DISK_STEP        10
  38. #define    MARGIN            3
  39. #define    BIGGEST_DISK        120
  40. #define    DISK_COR_WIDTH    12
  41. #define    DISK_COR_HEIGHT    12
  42. #define    LIFT_STEP        10
  43. #define    PEAK            40
  44. #define    CARRY_STEP        10
  45. #define    TEXT_Y            30
  46. #define    NUMX            300
  47. #define    SYSFONT            0
  48. #define    TEXTSZ            12
  49. #define    NUM_RIPPLE        5
  50.  
  51. /* Game States */
  52. #define    NONE                0
  53. #define    NEW                1
  54. #define    INPROG            2
  55. #define    DONE                3
  56. #define    MANUAL            4
  57.  
  58. /* Pick States */
  59. #define    SOURCE            0
  60. #define    DEST                1
  61.  
  62. /* Speed States */
  63. #define    SLOW            1
  64. #define    MEDIUM            2
  65. #define    FAST            3
  66.  
  67. /* Pin ID's */
  68. #define    LEFT                0
  69. #define    MIDDLE            1
  70. #define    RIGHT            2
  71.  
  72. #define ABOUT_COM        1
  73.  
  74. #define QUIT_COM        1
  75.  
  76. #define UNDO_COM        1
  77. #define CUT_COM        3
  78. #define COPY_COM        4
  79. #define PASTE_COM        5
  80.  
  81. #define NEW_COM        1
  82. #define RUN_COM        2
  83. #define STEP_COM        3
  84. #define HELP_COM        5
  85.  
  86. #define D2_COM        1
  87. #define D3_COM        2
  88. #define D4_COM        3
  89. #define D5_COM        4
  90. #define D6_COM        5
  91. #define D7_COM        6
  92. #define D8_COM        7
  93. #define D9_COM        8
  94. #define D10_COM        9
  95. #define SLOW_COM        11
  96. #define FASTER_COM    12
  97. #define FASTEST_COM    13
  98. #define SOUND_COM        15
  99. #define COLOR_COM        16
  100.  
  101. /* Private Resource ID's */
  102. #define windowID        128
  103. #define    About_ID        258
  104. #define    CLICK_ID        257
  105. #define    GOOF_ID        259
  106. #define    PRESTO_ID    260
  107. #define    VERS_ID        1
  108. #define    SIZE_ID        -1
  109. #define    MBAR_ID        400
  110. #define Desk_ID        400
  111. #define File_ID        401
  112. #define Edit_ID        402
  113. #define Commands_ID    403
  114. #define Options_ID        404
  115.  
  116. #define NULL    (long)0
  117.  
  118. #define    ON            1
  119. #define    OFF            0
  120.  
  121. #define    CLICK_SND    4
  122. #define    SYNCHRONOUS    0
  123.  
  124. #define    WNE_TRAP_NUM    0x60
  125. #define    UNIMPL_TRAP_NUM    0x9F
  126.  
  127. MenuHandle DeskMenu;
  128. MenuHandle FileMenu;
  129. MenuHandle EditMenu;
  130. MenuHandle CommandsMenu;
  131. MenuHandle OptionsMenu;
  132. WindowPtr myWindow;
  133. WindowRecord wRecord;
  134.  
  135. int NumonTower[NUMBER_OF_PINS];
  136. int NumberofMoves;
  137. int g_source, g_target, g_level;
  138. int g_speed, g_vstep, g_hstep, g_state, g_fcolor;
  139. int g_pick, g_msrc, g_mdst,  g_mvloc, g_mcenter, g_mlev;
  140. int DiskLocs[MAX_DISKS], DiskForeColors[MAX_DISKS], DiskBackColors[MAX_DISKS];
  141. int BestNum;
  142. Rect ssize, windowRect;
  143. int undo_level, undo_source, undo_target;
  144. Handle clickHandle, goofHandle, prestoHandle;
  145. int soundok, soundactive;
  146. extern ShowHelp();
  147.  
  148. main()
  149.  
  150. {
  151.     extern InitWorld();
  152.     extern void DrawTowers(), InitDisks();
  153.     extern void MoveTower();
  154.     extern void BuildMenus(), DoClick();
  155.     extern int DoMenu();
  156.     EventRecord myEvent;
  157.     WindowPtr whichWindow;
  158.     int ok, gWNEImplemented;
  159.     char ch;
  160.     
  161.     
  162.     InitWorld();
  163.     BuildMenus();
  164.     DrawTowers();
  165.     InitDisks(NUM_DISKS);
  166.     ok = TRUE;
  167.     gWNEImplemented = (NGetTrapAddress(WNE_TRAP_NUM, ToolTrap) !=
  168.                     NGetTrapAddress(UNIMPL_TRAP_NUM, ToolTrap));
  169.     
  170.     /* Main Event Loop */
  171.     
  172.     do {
  173.         if (gWNEImplemented) {
  174.             WaitNextEvent(everyEvent, &myEvent, 0, NULL);
  175.         }
  176.         else {
  177.             SystemTask();
  178.             GetNextEvent(everyEvent, &myEvent);
  179.         }
  180.         switch(myEvent.what) {
  181.             case mouseDown:
  182.                 switch(FindWindow(myEvent.where, &whichWindow)) {
  183.                 case inDesk:
  184.                     break;
  185.                 case inMenuBar:
  186.                     ok = DoMenu(MenuSelect(myEvent.where));
  187.                     break;
  188.                 case inGoAway:
  189.                     if (TrackGoAway(whichWindow, &myEvent.where)) ok = 0;
  190.                     break;
  191.                 case inDrag:
  192.                     doDrag(myEvent.where);
  193.                     break;
  194.                 case inSysWindow:
  195.                     SystemClick(&myEvent, whichWindow);
  196.                     break;
  197.                 case inContent:
  198.                     if (whichWindow != FrontWindow()) {
  199.                         SelectWindow(whichWindow);
  200.                     }
  201.                     else {
  202.                         DoClick(myEvent.where);
  203.                     }
  204.                     break;
  205.                 default:
  206.                     break;
  207.                 }
  208.                 break;
  209.                 
  210.             case keyDown:
  211.                 ch = myEvent.message & charCodeMask;
  212.                 if (myEvent.modifiers & cmdKey) {
  213.                     ok = DoMenu(MenuKey(ch));
  214.                 }
  215.                 break;            
  216.             
  217.             case activateEvt:
  218.                 doActivate(myEvent);
  219.                 break;
  220.                 
  221.             case updateEvt:
  222.                 doUpdate();
  223.                 break;
  224.                         
  225.             default:
  226.                 break;        /* ignore other events */
  227.         }  /* end of Event type switch */
  228.     } while(ok); /* end of Event loop */
  229.     
  230. }
  231.  
  232. /********************************************************
  233.  *  InitWorld() -- Initialize the Macintosh Environment; Create Window;
  234.  *                  set up defaults.
  235.  *  Arguments:  none
  236.  *********************************************************/
  237.  
  238. InitWorld()
  239. {
  240.     long port;
  241.     char *Title;
  242.     int i, sw, sh, shc, svc;
  243.  
  244.     /* Do all Mac Manager Init's */    
  245.     InitGraf(&thePort);
  246.     InitFonts();
  247.     FlushEvents(everyEvent, 0);
  248.     InitWindows();
  249.     InitMenus();
  250.     InitDialogs(NULL);
  251.     
  252.     /* Get screen size, locate window in the center */
  253.     ssize = screenBits.bounds;
  254.     sw = ssize.right - ssize.left + 1;
  255.     sh = ssize.bottom - ssize.top + 1;
  256.     shc = ssize.left + sw/2;
  257.     svc = ssize.top + sh/2;
  258.  
  259.     /* Build the application's single window */    
  260.     SetRect(&windowRect, shc-WINDOW_WIDTH/2,
  261.                 svc - WINDOW_HEIGHT/2,
  262.                 shc + WINDOW_WIDTH/2,
  263.                 svc + WINDOW_HEIGHT/2); 
  264.     Title = "\pTowers of Hanoi";
  265.     myWindow = NewWindow(&wRecord,
  266.                     &windowRect,
  267.                     Title,
  268.                     TRUE,
  269.                     noGrowDocProc,
  270.                     (WindowPtr)-1,
  271.                     TRUE,
  272.                     NULL);
  273.     SetPort(myWindow);    
  274.     InitCursor();
  275.     soundok = ((clickHandle = GetResource('snd ', CLICK_ID)) != (Handle)NULL);
  276.     if (soundok) {
  277.         goofHandle = GetResource('snd ', GOOF_ID);
  278.         prestoHandle = GetResource('snd ', PRESTO_ID);
  279.     }
  280.     soundactive = soundok;
  281.  
  282.     /* One-time game initializations */    
  283.     g_source = LEFT;
  284.     g_target = MIDDLE;
  285.     g_speed = SLOW;
  286.     g_vstep = LIFT_STEP;
  287.     g_hstep = CARRY_STEP;
  288.     g_pick = SOURCE;        /* init move state machine */
  289.     g_state = NONE;        /* game not yet ready */
  290.     g_level = 0;            /* No disks yet */
  291.     g_fcolor = 0;            /* default color scheme */
  292.     for (i=0;i<NUMBER_OF_PINS;i++) NumonTower[i] = 0;
  293.     
  294.     /* Disk Colors -- Dithered Patterns give Disk colors of magenta, red, orange, */
  295.     /* yellow, chartruese, green, cyan, blue, violet, and black if all 10 disks are  */
  296.     /* used, for an approximate rainbow spectrum.  Only classic QD is used!         */
  297.     
  298.     DiskForeColors[0] = magentaColor;    DiskBackColors[0] = magentaColor;
  299.     DiskForeColors[1] = redColor;        DiskBackColors[1] = redColor;
  300.     DiskForeColors[2] = redColor;        DiskBackColors[2] = yellowColor;
  301.     DiskForeColors[3] = yellowColor;    DiskBackColors[3] = yellowColor;
  302.     DiskForeColors[4] = greenColor;    DiskBackColors[4] = yellowColor;
  303.     DiskForeColors[5] = greenColor;    DiskBackColors[5] = greenColor;
  304.     DiskForeColors[6] = cyanColor;        DiskBackColors[6] = cyanColor;
  305.     DiskForeColors[7] = blueColor;        DiskBackColors[7] = blueColor;
  306.     DiskForeColors[8] = blueColor;        DiskBackColors[8] = redColor;
  307.     DiskForeColors[9] = blackColor;    DiskBackColors[9] = blackColor;
  308.  
  309.     TextFont(SYSFONT);
  310.     
  311. }
  312.  
  313. /********************************************************
  314.  *  DoMenu() -- Process Menu Selections
  315.  *  Arguments:
  316.  *    which:  Event Record for menu selection.
  317.  ********************************************************/
  318. int DoMenu(which)
  319.     long which;
  320. {
  321.     short menuID, itemNumber;
  322.     extern void InitDisks();
  323.     int i;
  324.     extern void NextMove();
  325.     Str255 daName;
  326.     extern ShowHelp();
  327.     
  328.     
  329.     menuID = HiWord(which);
  330.     itemNumber = LoWord(which);
  331.     
  332.     switch(menuID) {
  333.     case(Desk_ID):
  334.         switch(itemNumber) {
  335.         case(ABOUT_COM):
  336.             doAbout();
  337.             break;
  338.         default:
  339.             GetItem(DeskMenu, itemNumber, &daName);
  340.             OpenDeskAcc(&daName);
  341.             break;
  342.         }
  343.         break;
  344.     case(File_ID):
  345.         switch(itemNumber) {
  346.         case(QUIT_COM):
  347.             HiliteMenu(0);
  348.             return(0);
  349.             break;
  350.         }
  351.         break;
  352.     case(Edit_ID):
  353.         if (SystemEdit(itemNumber-1)) break;
  354.         switch(itemNumber) {
  355.         case(UNDO_COM):
  356.             doUndo();
  357.             break;
  358.         case(CUT_COM):
  359.             break;
  360.         case(COPY_COM):
  361.             break;
  362.         case(PASTE_COM):
  363.             break;
  364.         }                
  365.         break;
  366.     case(Commands_ID):
  367.         switch(itemNumber) {
  368.         case(NEW_COM):
  369.             if (g_state != NEW) {
  370.                 InitDisks(g_level);
  371.             }
  372.             break;
  373.         case(STEP_COM):
  374.             if ((g_state == NEW) || (g_state == INPROG)) {
  375.                 if (g_state == NEW) {
  376.                     g_target = (g_level%2) ? MIDDLE : RIGHT;
  377.                     DrawBase(g_target, dkGray);        
  378.                     g_state = INPROG;
  379.                 }
  380.                 NextMove();
  381.                 toggleMenu(Commands_ID, NEW_COM, ON);
  382.             }
  383.             break;
  384.         case(RUN_COM):
  385.             if ((g_state == NEW) || (g_state == INPROG)) {
  386.                 g_state = INPROG;
  387.                 toggleMenu(Commands_ID, NEW_COM, ON);
  388.                 MoveTower(g_source, g_target);
  389.             }
  390.             break;
  391.         case(HELP_COM):
  392.             ShowHelp();
  393.             break;
  394.         }
  395.         break;
  396.     case(Options_ID):
  397.         switch(itemNumber) {
  398.         case(D2_COM):
  399.         case(D3_COM):
  400.         case(D4_COM):
  401.         case(D5_COM):
  402.         case(D6_COM):
  403.         case(D7_COM):
  404.         case(D8_COM):
  405.         case(D9_COM):
  406.         case(D10_COM):
  407.             for (i=D2_COM;i<=D10_COM;i++) {
  408.                 CheckItem(OptionsMenu, (short)i, FALSE);
  409.             }
  410.             CheckItem(OptionsMenu, (short)itemNumber, TRUE);
  411.             InitDisks(itemNumber+1);
  412.             break;            
  413.         case(SLOW_COM):
  414.             g_speed = SLOW; g_vstep = LIFT_STEP; g_hstep = CARRY_STEP;
  415.             CheckItem(OptionsMenu, (short)SLOW_COM, TRUE);
  416.             CheckItem(OptionsMenu, (short)FASTER_COM, FALSE);
  417.             CheckItem(OptionsMenu, (short)FASTEST_COM, FALSE);
  418.             break;
  419.         case(FASTER_COM):
  420.             g_speed = MEDIUM; g_vstep = LIFT_STEP*2;
  421.             g_hstep = CARRY_STEP*2;
  422.             CheckItem(OptionsMenu, (short)SLOW_COM, FALSE);
  423.             CheckItem(OptionsMenu, (short)FASTER_COM, TRUE);
  424.             CheckItem(OptionsMenu, (short)FASTEST_COM, FALSE);
  425.             break;
  426.         case(FASTEST_COM):
  427.             g_speed = FAST;
  428.             CheckItem(OptionsMenu, (short)SLOW_COM, FALSE);
  429.             CheckItem(OptionsMenu, (short)FASTER_COM, FALSE);
  430.             CheckItem(OptionsMenu, (short)FASTEST_COM, TRUE);
  431.             break;
  432.         case(SOUND_COM):
  433.             if (soundok) {
  434.                 soundactive = 1 - soundactive;
  435.                 if (soundactive) {
  436.                     CheckItem(OptionsMenu, (short)SOUND_COM, TRUE);
  437.                 }
  438.                 else {
  439.                     CheckItem(OptionsMenu, (short)SOUND_COM, FALSE);
  440.                 }
  441.             }
  442.             break;
  443.         case(COLOR_COM):
  444.             g_fcolor = (g_fcolor+1)%MAX_DISKS;
  445.             reDraw();
  446.             break;
  447.         }        
  448.         break;
  449.     }
  450.     HiliteMenu(0);
  451.     return(1);
  452. }
  453.  
  454. /*******************************************************
  455.  *  ours() -- Boolean function which returns TRUE if its argument is
  456.  *  a pointer to the application's window, false otherwise.
  457.  *  Arguments:
  458.  *     w -- a pointer to a window
  459.  *******************************************************/
  460.  
  461. int ours(w)
  462.     WindowPtr w;
  463. {
  464.     return((myWindow != NULL) && (w == myWindow));
  465. }
  466.  
  467. /*********************************************************
  468.  *  DoClick -- Handle a Mouse Click in the content section of the window
  469.  *  Arguments:
  470.  *    where -- location of the click in local coordinates.
  471.  *********************************************************/
  472. void DoClick(where)
  473.     Point where;
  474. {
  475.     extern int PickTower();
  476.     extern void HiLiteDisk(), MoveDisk(), DrawDisk();
  477.     int pin, svloc, scenter, num, lev, over, i;
  478.     
  479.  
  480.     if (g_state == DONE) return;
  481.         
  482.     GlobalToLocal(&where);
  483.     pin = PickTower(where);
  484.     if (pin < 0) return;
  485.     num = NumonTower[pin];
  486.     svloc = BOTTOM_MOST - num*DISK_HEIGHT;          
  487.     scenter = LEFT_MOST + pin*BASE_SPACING + BASE_WIDTH/2;
  488.  
  489.                   
  490.     switch (g_pick) {
  491.     
  492.     case(SOURCE):
  493.     /* See if legitimate Source Tower has been picked */
  494.  
  495.     if (NumonTower[pin] == 0) {
  496.         SysBeep(10);
  497.         return;
  498.     }
  499.       for (lev=0;DiskLocs[lev] != pin;lev++);
  500.       HiLiteDisk(lev, svloc, scenter);      
  501.     g_msrc = pin;
  502.     g_mvloc = svloc;
  503.     g_mcenter = scenter;
  504.     g_mlev = lev;
  505.     g_pick = DEST;
  506.     break;
  507.     
  508.     case(DEST):
  509.     if (pin == g_msrc) {
  510.           DrawDisk(g_mlev, g_mvloc, g_mcenter, 1);
  511.           g_pick = SOURCE;
  512.           return;
  513.       }
  514.       if (NumonTower[pin] > 0) {
  515.           for (lev=0;DiskLocs[lev] != pin;lev++);
  516.           if (lev < g_mlev) {
  517.               DrawDisk(g_mlev, g_mvloc, g_mcenter, 1);
  518.               SysBeep(10);
  519.               g_pick = SOURCE;
  520.               return;
  521.           }
  522.       }
  523.       undo_level = g_mlev;
  524.       undo_source = pin;
  525.       undo_target = g_msrc;
  526.       MoveDisk(g_mlev, g_msrc, pin);
  527.     g_state = MANUAL;
  528.     g_pick = SOURCE;
  529.     toggleMenu(Edit_ID, UNDO_COM, ON);
  530.     toggleMenu(Commands_ID, RUN_COM, OFF);
  531.     toggleMenu(Commands_ID, STEP_COM, OFF);
  532.     toggleMenu(Commands_ID, NEW_COM, ON);
  533.     NumberofMoves++;
  534.     EraseMoves();
  535.     if (NumberofMoves == 1) {
  536.         ShowMoves();
  537.         if (g_level%2) {
  538.             g_target = pin;
  539.         }
  540.         else {
  541.             g_target = SUM_OF_PINS - pin;
  542.         }
  543.         DrawBase(g_target, dkGray);
  544.     }
  545.     else {
  546.         over = 1;
  547.         for (i=0;i<g_level;i++) if (DiskLocs[i] != g_target) over = 0;
  548.         if (over) {
  549.             ShowEnd();
  550.             g_state = DONE;
  551.             toggleMenu(Edit_ID, UNDO_COM, OFF);
  552.         }
  553.         else {
  554.             ShowMoves();
  555.         }
  556.     }
  557.     break;
  558.     
  559.     }
  560. }
  561.  
  562. /*****************************************************
  563.  *  PickTower -- Translate the location of a mouse click in the
  564.  *  content region of our window into the index of a tower.
  565.  *  Arguments:
  566.  *    where -- location of the mouse click in local coordinates.
  567.  *  Function Returns:  index (0..2) of picked tower, or -1 if no
  568.  *  tower is selected.
  569.  *****************************************************/
  570.  
  571. int PickTower(where)
  572.     Point where;
  573. {
  574.     if ((where.v < BOTTOM_MOST - PIN_HEIGHT) || 
  575.         (where.v > BOTTOM_MOST + BASE_HEIGHT)) {
  576.             SysBeep(10);
  577.             return(-1);
  578.     }
  579.     if ((where.h >= LEFT_MOST) && (where.h <= LEFT_MOST + 
  580.         BASE_WIDTH)) return(LEFT);
  581.     if ((where.h >= LEFT_MOST + BASE_SPACING) &&
  582.         (where.h <= LEFT_MOST + BASE_SPACING + BASE_WIDTH))
  583.             return(MIDDLE);
  584.     if ((where.h >= LEFT_MOST + BASE_SPACING*2) &&
  585.         (where.h <= LEFT_MOST + BASE_SPACING*2 + BASE_WIDTH))
  586.             return(RIGHT);
  587.     return(-1);
  588. }
  589.  
  590. /**************************************************
  591.  *  ShadeDisk -- draw shading on a game disk.
  592.  *  Arguments:
  593.  *    index -- index of disk (0 is smallest)
  594.  *    DiskRect -- bounding rectangle of the disk
  595.  *    DiskSize -- horizontal size of the disk in pixels
  596.  **************************************************/            
  597. void ShadeDisk (index, DiskRect, DiskSize)
  598.     int index;
  599.     Rect DiskRect;
  600.     int DiskSize;
  601. {
  602.  
  603.     Rect ArcRect;
  604.     
  605.     PenPat(white);
  606.     BackColor(whiteColor);
  607.     PenSize(2, 2);
  608.     
  609.     ArcRect.top = DiskRect.top + MARGIN;
  610.     ArcRect.left = DiskRect.left + MARGIN;
  611.     ArcRect.bottom = DiskRect.bottom - MARGIN;
  612.     ArcRect.right = DiskRect.left + DISK_HEIGHT - MARGIN * 2;
  613.     FrameArc(&ArcRect, 180, 180);
  614.  
  615.     MoveTo(ArcRect.left + (DISK_HEIGHT - MARGIN * 2)/2, 
  616.         ArcRect.bottom - 1);
  617.     Line(DiskSize - DISK_HEIGHT, 0);
  618.     PenPat(black);
  619.     PenSize(1, 1);
  620. }
  621.  
  622. /******************************************************
  623.  *  DrawDisk -- Draw a game disk.
  624.  *  Arguments:
  625.  *    index -- index number of the disk (0 is the smallest)
  626.  *    vloc -- vertical position of the top of the disk in pixels
  627.  *    center -- horizontal position of the center of the disk in pixels
  628.  *    shade -- if true, shade the disk, otherwise not
  629.  ******************************************************/
  630. void DrawDisk (index, vloc, center, shade)
  631.     int index, vloc, center, shade;
  632. {
  633.  
  634.     Rect DiskRect, InRect;
  635.     int size, color;
  636.  
  637.     color = (g_fcolor + index)%MAX_DISKS;
  638.     ForeColor(DiskForeColors[color]);
  639.     BackColor(DiskBackColors[color]);
  640.     size = BIGGEST_DISK - (g_level - index )*DISK_STEP;
  641.     DiskRect.top = vloc;
  642.     DiskRect.left = center - size/2;
  643.     DiskRect.bottom = vloc + DISK_HEIGHT;
  644.     DiskRect.right = DiskRect.left + size;
  645.     
  646.     PenMode(patCopy);
  647.     FrameRoundRect(&DiskRect, DISK_COR_WIDTH, DISK_COR_HEIGHT);
  648.     
  649.     InRect.top = DiskRect.top + 1;
  650.     InRect.left = DiskRect.left + 1;
  651.     InRect.bottom = DiskRect.bottom - 1;
  652.     InRect.right = DiskRect.right - 1;
  653.     
  654.     if (shade) {
  655.         FillRoundRect(&InRect, DISK_COR_WIDTH, DISK_COR_HEIGHT, gray);
  656.         ShadeDisk(index, DiskRect, size);
  657.     }
  658.     else {
  659.         FillRoundRect(&InRect, DISK_COR_WIDTH, DISK_COR_HEIGHT, white);
  660.     }
  661.     ForeColor(blackColor);
  662.     BackColor(whiteColor);
  663. }
  664.  
  665. /******************************************************
  666.  *  EraseDisk -- Just what it says
  667.  *  Arguments:
  668.  *    index -- index number of the disk (0 is the smallest)
  669.  *    vloc -- vertical location of the top of the disk in pixels
  670.  *    center -- horizontal location of the center of the disk in pixels
  671.  ******************************************************/
  672. void EraseDisk (index, vloc, center)
  673.     int index, vloc, center;
  674. {
  675.     Rect DiskRect;
  676.     int size;
  677.         
  678.  
  679.     size = BIGGEST_DISK - (g_level - index)*DISK_STEP;  
  680.     DiskRect.top = vloc;
  681.     DiskRect.left = center - size/2;
  682.     DiskRect.bottom = vloc + DISK_HEIGHT;
  683.     DiskRect.right = DiskRect.left + size;
  684.  
  685.      FillRoundRect(&DiskRect, DISK_COR_WIDTH, DISK_COR_HEIGHT, white);
  686. }
  687.  
  688. /******************************************************
  689.  *  HiLiteDisk -- show selection by turning disk white
  690.  *  Arguments:
  691.  *    index -- index number of the disk (0 is the smallest)
  692.  *    vloc -- vertical location of the top of the disk in pixels
  693.  *    center -- horizontal location of the center of the disk in pixels
  694.  ******************************************************/
  695. void HiLiteDisk (index, vloc, center)
  696.     int index, vloc, center;
  697. {
  698.     Rect DiskRect;
  699.     int size, i;
  700.  
  701.     size = BIGGEST_DISK - (g_level - index)*DISK_STEP;  
  702.     DiskRect.top = vloc;
  703.     DiskRect.left = center - size/2;
  704.     DiskRect.bottom = vloc + DISK_HEIGHT;
  705.     DiskRect.right = DiskRect.left + size;
  706.  
  707.     ForeColor(blackColor);
  708.     for (i=0;i<10;i++) {
  709.          FillRoundRect(&DiskRect, DISK_COR_WIDTH, DISK_COR_HEIGHT, black);
  710.          FillRoundRect(&DiskRect, DISK_COR_WIDTH, DISK_COR_HEIGHT, white);
  711.      }
  712.     FrameRoundRect(&DiskRect, DISK_COR_WIDTH, DISK_COR_HEIGHT);
  713. }
  714. /******************************************************
  715.  *  RestorePin -- redraw the missing piece of the 'pin' of a tower
  716.  *                after a disk has been erased.
  717.  *  Arguments:
  718.  *    vloc    -- vertical location of the disk top in pixels
  719.  *    center -- horizontal location of the center of the disk in pixels
  720.  ******************************************************/
  721. void RestorePin(vloc, center)
  722.     int vloc;
  723.     int center;
  724. {
  725.     int x1, x2;
  726.     Rect PTRect;
  727.     
  728.     
  729.     x1 = center - PIN_WIDTH/2;
  730.     x2 = center + PIN_WIDTH/2 - 1;
  731.     PenPat(black);
  732.     PenSize(1, 1);
  733.         
  734.     MoveTo(x1, BOTTOM_MOST - PIN_HEIGHT + PIN_WIDTH/2);
  735.     LineTo(x1, vloc + DISK_HEIGHT - 1);
  736.     MoveTo(x2, BOTTOM_MOST - PIN_HEIGHT + PIN_WIDTH/2);
  737.     LineTo(x2, vloc + DISK_HEIGHT - 1);
  738.     
  739.     PTRect.top =  BOTTOM_MOST - PIN_HEIGHT;
  740.     PTRect.left = center - PIN_WIDTH/2;
  741.     PTRect.bottom =  PTRect.top + PIN_WIDTH;
  742.     PTRect.right = center + PIN_WIDTH/2;
  743.     
  744.     FrameArc(&PTRect, -90, 180);
  745. }
  746.  
  747. /******************************************************
  748.  *  DrawTowers -- Draw the Tower Base and Pins.
  749.  *  Arguments:  none
  750.  ******************************************************/
  751. void DrawTowers ()
  752. {
  753.     int i;
  754.     Rect BaseRect, PinRect, DiskRect;
  755.     
  756.  
  757.  
  758.     PenPat(black);
  759.  
  760.     PinRect.top = BOTTOM_MOST - PIN_HEIGHT;
  761.     PinRect.left = LEFT_MOST + BASE_WIDTH/2 - PIN_WIDTH/2;
  762.     PinRect.bottom = BOTTOM_MOST + PIN_MARGIN;
  763.     PinRect.right = PinRect.left + PIN_WIDTH;
  764.  
  765.      for (i=0;i<NUMBER_OF_PINS;i++) {
  766.         FrameRoundRect(&PinRect, DISK_COR_WIDTH, DISK_COR_HEIGHT);
  767.         PinRect.left += BASE_SPACING;
  768.         PinRect.right = PinRect.left + PIN_WIDTH;
  769.       }
  770.  
  771.      for (i=0;i<NUMBER_OF_PINS;i++) {
  772.          DrawBase(i, gray);
  773.      }
  774.  }
  775.  
  776.  /**********************************************************
  777.   *  DrawBase -- Draw the Base of a Tower
  778.   *  Arguments:
  779.   *    pin -- Tower index 0..2
  780.   *    pat -- pattern index in standard Mac patterns
  781.   **********************************************************/
  782.   
  783.   DrawBase(pin, pat)
  784.       int pin;
  785.       Pattern pat;
  786.   {
  787.       Rect BaseRect;
  788.       
  789.     BaseRect.top = BOTTOM_MOST;
  790.     BaseRect.bottom = BaseRect.top + BASE_HEIGHT;
  791.     BaseRect.left = LEFT_MOST + pin*BASE_SPACING;
  792.     BaseRect.right = BaseRect.left + BASE_WIDTH;
  793.        
  794.      PenPat(pat);
  795.      PaintRect(&BaseRect); 
  796.     PenPat(black);
  797. }
  798.  
  799. /*********************************************************
  800.  *  InitDisks -- Set up for a new game.  Erase any old disks, draw new
  801.  *              disks stack up on starting tower; initialize internal
  802.  *            database.
  803.  *  Arguments:
  804.  *    num -- number of disks to start with.
  805.  *********************************************************/
  806. void InitDisks(num)
  807.     int num;
  808. {
  809.  
  810.     int i, j, center, vloc, pin;
  811.     
  812.  
  813.     for (i=0;i<NUMBER_OF_PINS;i++) DrawBase(i, gray);
  814.     
  815.     for (i=0;i<g_level;i++) {
  816.         pin = DiskLocs[i];
  817.         center = LEFT_MOST + pin*BASE_SPACING + BASE_WIDTH/2;
  818.         vloc = BOTTOM_MOST - NumonTower[pin]*DISK_HEIGHT;
  819.         EraseDisk(i, vloc, center);
  820.         RestorePin(vloc, center);
  821.         NumonTower[pin]--;
  822.     }
  823.         
  824.     center = LEFT_MOST + g_source*BASE_SPACING + BASE_WIDTH/2;
  825.     g_level = num;
  826.                       
  827.     for (i=g_level-1;i>=0;i--) {
  828.         NumonTower[g_source]++;
  829.         vloc = BOTTOM_MOST - NumonTower[g_source]*DISK_HEIGHT;
  830.         DrawDisk(i, vloc, center, 1);
  831.     }
  832.     NumberofMoves = 0;
  833.     for (i=0;i<g_level;i++) DiskLocs[i] = g_source;
  834.     BestNum = 1;
  835.     for (i=0;i<num;i++) BestNum <<= 1;
  836.     BestNum--;
  837.     g_state = NEW;
  838.     EraseMoves();
  839.     
  840.     toggleMenu(Edit_ID, UNDO_COM, OFF);
  841.     toggleMenu(Commands_ID, NEW_COM, OFF);
  842.     toggleMenu(Commands_ID, RUN_COM, ON);
  843.     toggleMenu(Commands_ID, STEP_COM, ON);
  844. }
  845.  
  846. /***********************************************************
  847.  *  MoveTower -- Move a tower from one pin to another.  Called in
  848.  *                response to the "Run" menu command.
  849.  *  Arguments:
  850.  *    source -- index of source tower.
  851.  *    target -- index of destination tower.
  852.  *  Notes:  This routine used to be recursive, but was changed to iterative
  853.  *  so that the game could be single-stepped conveniently (using the
  854.  *  'Step' menu command, and also so that the user could get help from
  855.  *  the computer.
  856.  **********************************************************/
  857. void MoveTower (source, target)     
  858.      int source, target;
  859.      
  860. {
  861.  
  862.     int spare, xc;
  863.     extern void NextMove();
  864.     EventRecord myEvent;
  865.     char ch, *ip, *op, out[80];
  866.     static char *s1 = "Type \021-. to Interrupt";
  867.     
  868.     
  869.      ip = s1; op = &out[1];
  870.      for(;*ip != '\0';) *op++ = *ip++;
  871.      out[0] = (char)(op - &out[1]);
  872.      xc = (windowRect.left + windowRect.right)/2;
  873.      MoveTo(xc - StringWidth(out)/2 - windowRect.left, TEXT_Y);
  874.      DrawString(out);
  875.      
  876.     g_target = (g_level%2) ? MIDDLE : RIGHT;
  877.     DrawBase(g_target, dkGray);        
  878.     do {
  879.         NextMove();
  880.         GetNextEvent(everyEvent, &myEvent);
  881.         if (myEvent.what == keyDown) {
  882.             ch = myEvent.message & charCodeMask;
  883.             if ((ch == '.') && myEvent.modifiers & cmdKey)  {
  884.                 EraseMoves();
  885.                 return;
  886.             }
  887.         }
  888.     } while (g_state != DONE);
  889.     
  890.     EraseMoves();
  891.  
  892. }
  893.  
  894. /***********************************************************
  895.  *  NextMove -- Generate the next move in a standard game.  Uses an
  896.  *            iterative algorithm rather than the usual recursive one.
  897.  *            The iterative algorithm gives the same results as the
  898.  *            recursive one, but is easier to single-step.
  899.  *  Arguments:    none
  900.  ***********************************************************/
  901. void NextMove()
  902. {
  903.     int tmp, lev, src, dst, dir;
  904.     extern void MoveDisk();
  905.  
  906.     NumberofMoves++;    
  907.     tmp = NumberofMoves;
  908.     lev = 0;
  909.     
  910.     /* Use binary bits in 'NumberofMoves' right-to-left to find next piece to move */
  911.     /* littlest disk moves every other time, next biggest moves every 4th time,
  912.     /* next biggest moves every 8th time, etc. */
  913.     for (;;) {
  914.         if (tmp & 1) break;
  915.         tmp >>= 1;
  916.         lev++;
  917.     }
  918.     
  919.     /* Now find source and destination pins for the selected disk */
  920.     /* Disks of adjacent size move in opposite directions. */
  921.     src = DiskLocs[lev];
  922.     dir = 1 - ((lev%2) << 1);
  923.     dst = src+dir;
  924.     if (dst > RIGHT) dst = LEFT;
  925.     if (dst < LEFT) dst = RIGHT;
  926.     
  927.     /* Move the Disk, check for game over */
  928.     MoveDisk(lev, src, dst);
  929.     if (NumberofMoves >= BestNum) {
  930.         g_state = DONE;
  931.         toggleMenu(Commands_ID, STEP_COM, OFF);
  932.         toggleMenu(Commands_ID, RUN_COM, OFF);
  933.         if (soundok && soundactive) SndPlay((Ptr)NULL, prestoHandle, SYNCHRONOUS);
  934.         RippleDisks(g_target, NUM_RIPPLE);
  935.     }
  936. }
  937.  
  938. /******************************************************
  939.  *  MoveDisk -- Move a disk from one pin to another.  Animate the
  940.  *            move and update the internal database.
  941.  *  Arguments:
  942.  *    level:    index of the disk to move.
  943.  *    source:    index of the source tower
  944.  *    target:    index of the destination tower.
  945.  ******************************************************/
  946.  
  947. void MoveDisk (level, source, target)    
  948.     int level;
  949.     int  source, target;
  950. {
  951.     int svloc, tvloc, scenter, tcenter;
  952.     void LiftDisk(), DropDisk(), CarryDisk();
  953.     
  954.     svloc = BOTTOM_MOST - NumonTower[source]*DISK_HEIGHT;          
  955.     scenter = LEFT_MOST + source*BASE_SPACING + BASE_WIDTH/2;
  956.     tcenter = LEFT_MOST + target*BASE_SPACING + BASE_WIDTH/2;        
  957.       NumonTower[target]++;
  958.     tvloc = BOTTOM_MOST - NumonTower[target]*DISK_HEIGHT;
  959.  
  960.     if (g_speed == FAST) {
  961.         EraseDisk(level, svloc, scenter);
  962.         RestorePin(svloc, scenter);        
  963.     }
  964.     else {
  965.         LiftDisk(level, source);
  966.     }
  967.       NumonTower[source]--;
  968.       DiskLocs[level] = target;
  969.       if (g_speed <= MEDIUM) CarryDisk(level, source, target);
  970.       if (g_speed == FAST) {
  971.           DrawDisk(level, tvloc, tcenter);
  972.       }
  973.       else {
  974.         DropDisk(level, target);
  975.     }
  976.     if (soundok && soundactive) SndPlay((Ptr)NULL, clickHandle, SYNCHRONOUS);
  977. }
  978.  
  979. /******************************************************
  980.  *  LiftDisk -- animate lifting a disk off the source pin.
  981.  *  Arguments:
  982.  *    level -- index of the disk to be moved.
  983.  *    pin -- index of the source pin.
  984.  ******************************************************/
  985. void LiftDisk(level, pin)
  986.     int level, pin;
  987. {
  988.     int vloc, center, num, i, v;
  989.  
  990.     vloc = BOTTOM_MOST - NumonTower[pin]*DISK_HEIGHT;          
  991.     center = LEFT_MOST + pin*BASE_SPACING + BASE_WIDTH/2;          
  992.     num = (vloc - PEAK)/g_vstep;
  993.     v = vloc;
  994.     
  995.     for (i=0;i<num;i++) {
  996.         EraseDisk(level, v, center);
  997.         RestorePin(vloc, center); 
  998.         v -= g_vstep;
  999.         DrawDisk(level, v, center, 0);
  1000.     }
  1001.     EraseDisk(level, v, center);
  1002. }
  1003.  
  1004. /*****************************************************
  1005.  *  DropDisk -- animate dropping a disk onto the destination pin
  1006.  *  Arguments:
  1007.  *    level -- index of the disk to be moved.
  1008.  *    pin -- index of the destination pin
  1009.  *****************************************************/
  1010. void DropDisk(level, pin)
  1011.     int level, pin;
  1012. {
  1013.     int vloc, center, num, i, v;
  1014.  
  1015.     vloc = BOTTOM_MOST - NumonTower[pin]*DISK_HEIGHT;          
  1016.     center = LEFT_MOST + pin*BASE_SPACING + BASE_WIDTH/2;          
  1017.     num = (vloc - PEAK)/g_vstep;
  1018.     v = vloc - num*g_vstep;
  1019.     
  1020.     for (i=0;i<num;i++) {
  1021.         DrawDisk(level, v, center, 0);
  1022.         EraseDisk(level, v, center);
  1023.         RestorePin(vloc, center); 
  1024.         v += g_vstep;        
  1025.     }
  1026.     DrawDisk(level, v, center, 1);
  1027. }
  1028.  
  1029. /****************************************************
  1030.  *  CarryDisk -- animate sliding the disk from the source to
  1031.  *                destination pins.
  1032.  *  Arguments:
  1033.  *    level -- index of the selected disk.
  1034.  *    source -- index of the source pin.
  1035.  *    target -- index of the destination pin.
  1036.  ****************************************************/
  1037. void CarryDisk(level, source, target)
  1038.     int level, source, target;
  1039. {
  1040.     int start, end, h, h2,  dir;
  1041.     
  1042.     
  1043.     start = LEFT_MOST + source*BASE_SPACING + BASE_WIDTH/2;          
  1044.     end = LEFT_MOST + target*BASE_SPACING + BASE_WIDTH/2;
  1045.     dir = (start < end) ? g_hstep : -g_hstep;
  1046.     h = start;
  1047.  
  1048.     if (dir > 0) {
  1049.         do {
  1050.             DrawDisk(level, PEAK, h, 0);
  1051.             EraseDisk(level, PEAK, h);
  1052.             h += dir;
  1053.         } while (h < end);
  1054.     }
  1055.     else {
  1056.  
  1057.         do {
  1058.             DrawDisk(level, PEAK, h, 0);
  1059.             EraseDisk(level, PEAK, h);
  1060.             h += dir;
  1061.         } while (h > end);
  1062.     }
  1063. }
  1064.  
  1065. /*****************************************************
  1066.  *  BuildMenus -- Create the Application's Menus.
  1067.  *  Arguments -- none
  1068.  *****************************************************/          
  1069. void BuildMenus()
  1070. {
  1071. Handle    myMBar;
  1072.     
  1073. myMBar = GetNewMBar(MBAR_ID);
  1074. SetMenuBar(myMBar);
  1075.  
  1076. DeskMenu = GetMHandle(Desk_ID);
  1077. AddResMenu(DeskMenu, 'DRVR');
  1078. FileMenu = GetMHandle(File_ID);
  1079. EditMenu = GetMHandle(Edit_ID);
  1080. CommandsMenu = GetMHandle(Commands_ID);
  1081. OptionsMenu = GetMHandle(Options_ID);
  1082.  
  1083. CheckItem(OptionsMenu, D5_COM, TRUE);
  1084. CheckItem(OptionsMenu, SLOW_COM, TRUE);
  1085. if (soundok) {
  1086.     CheckItem(OptionsMenu, SOUND_COM, TRUE);
  1087. }
  1088. else {
  1089.     toggleMenu(OptionsMenu, SOUND_COM, OFF);
  1090. }
  1091.  
  1092. DrawMenuBar();
  1093.     
  1094. }
  1095. /* =============================================
  1096.  *  doUpdate -- action routine for UpdateEvt's
  1097.  * ============================================= */
  1098.  
  1099. doUpdate()
  1100. {
  1101.     BeginUpdate(myWindow);
  1102.     reDraw();
  1103.     EndUpdate(myWindow);
  1104. }
  1105.  
  1106. /* ================================================
  1107.  * doActivate -- handle activate events
  1108.  * =============================================== */
  1109.  
  1110. doActivate(event)
  1111.     EventRecord event;
  1112. {
  1113.      if (event.modifiers & activeFlag) {
  1114.          SelectWindow(myWindow);
  1115.      }
  1116. }
  1117.  
  1118. /* ================================================
  1119.  *  doDrag -- handle window drag events
  1120.  * ============================================= */
  1121.  
  1122. doDrag(where)
  1123.     Point where;
  1124. {
  1125.      Rect br;
  1126.  
  1127.     br.top = ssize.top+WMARGIN;
  1128.     br.left = ssize.left+WMARGIN;
  1129.     br.bottom =  ssize.bottom-WMARGIN;
  1130.     br.right =  ssize.right-WMARGIN;
  1131.     
  1132.     DragWindow(myWindow, where, &br);
  1133. }
  1134. /* ================================================
  1135.  *  doAbout -- handle "About Hanoi..." Menu
  1136.  * ============================================= */
  1137.  
  1138. doAbout()
  1139. {
  1140.     DialogPtr myDialog;
  1141.     short itemHit;
  1142.     
  1143.     myDialog = GetNewDialog(About_ID, NULL,(WindowPtr)-1);
  1144.     do (ModalDialog((ProcPtr)NULL, &itemHit)); while (itemHit != 1);
  1145.     CloseDialog(myDialog);
  1146. }
  1147.  
  1148. /*================================================
  1149.  *  doUndo -- handle Undo Command
  1150.  *===============================================*/
  1151.  
  1152.  doUndo()
  1153.  {
  1154.      int i;
  1155.      
  1156.      
  1157.      MoveDisk(undo_level, undo_source, undo_target);
  1158.      NumberofMoves--;
  1159.      EraseMoves();
  1160.      if (NumberofMoves) {
  1161.          ShowMoves();
  1162.      }
  1163.      else {
  1164.          g_state = NEW;
  1165.          toggleMenu(Commands_ID, RUN_COM, ON);
  1166.          toggleMenu(Commands_ID, STEP_COM, ON);
  1167.          for (i=0;i<NUMBER_OF_PINS;i++) DrawBase(1, gray);
  1168.      }
  1169.      toggleMenu(Edit_ID, UNDO_COM, OFF);
  1170.  }
  1171.  
  1172. /* ================================================
  1173.  *  toggleMenu -- enable or disable the selected
  1174.  *  menu item.
  1175.  *  Arguments:
  1176.  *    menu -- ID of menu
  1177.  *    item -- index of menu item
  1178.  *    state -- 1 = enable, 0 = disable
  1179.  * ============================================= */
  1180.  
  1181. toggleMenu(menu, item, state)
  1182.      int menu;
  1183.      int item;
  1184.      int state;
  1185. {
  1186.  
  1187.     MenuHandle    mh;
  1188.  
  1189.     switch(menu) {
  1190.         case(Desk_ID): mh = DeskMenu; break;
  1191.          case(File_ID): mh = FileMenu; break;
  1192.          case(Edit_ID): mh = EditMenu; break;
  1193.          case(Commands_ID): mh = CommandsMenu; break;
  1194.          case(Options_ID): mh = OptionsMenu; break;
  1195.          default: return;
  1196.     }
  1197.      
  1198.     if (state == 1) {
  1199.          EnableItem(mh, item);
  1200.     }
  1201.     else {
  1202.          DisableItem(mh, item);
  1203.     }
  1204. }
  1205.  
  1206. /* ==============================================
  1207.  *  reDraw -- repaint the screen with all towers
  1208.  *  and disks in the current positions.
  1209.  * ============================================ */
  1210.  
  1211. reDraw()
  1212. {
  1213.     int i, num, vloc, center, pin;
  1214.     int h[NUMBER_OF_PINS];
  1215.     
  1216.     DrawTowers();
  1217.     for (i=0;i<NUMBER_OF_PINS;i++) h[i] = 1;
  1218.     for (i=g_level-1;i>=0;i--) {
  1219.         pin = DiskLocs[i];
  1220.         center = LEFT_MOST + pin*BASE_SPACING + BASE_WIDTH/2;
  1221.         vloc = BOTTOM_MOST - h[pin]*DISK_HEIGHT;
  1222.         DrawDisk(i, vloc, center, 1);
  1223.         h[pin]++;
  1224.     }
  1225.     if (g_state == MANUAL) {
  1226.         EraseMoves();
  1227.         ShowMoves();
  1228.     }
  1229. }
  1230. /*===============================================
  1231.  *  ShowMoves() -- Show the current number of moves
  1232.  *  and the best possible number of moves.  Done for
  1233.  *  a manual game only.
  1234.  *===============================================*/
  1235.  
  1236.  ShowMoves()
  1237.  {
  1238.      char out[40], *ip, *op;
  1239.      static char *s1 = " move done -- best is ";
  1240.      static char *s2 = " moves done -- best is "; 
  1241.      extern char *utoa();
  1242.      int xc, w;
  1243.      
  1244.      op = utoa(NumberofMoves, &out[1]);
  1245.      ip = (NumberofMoves > 1) ? s2 : s1;
  1246.      for(;*ip != '\0';) *op++ = *ip++;
  1247.      op = utoa(BestNum, op);
  1248.      out[0] = (char)(op - &out[1]);
  1249.      xc = (windowRect.left + windowRect.right)/2;
  1250.      w = StringWidth(out);
  1251.      MoveTo(xc - w/2 - windowRect.left, TEXT_Y);
  1252.      DrawString(out);
  1253.  }
  1254.  
  1255.  /*==============================================
  1256.   *  EraseMoves() -- Erase the Text Move Display
  1257.   *=============================================*/
  1258.   
  1259.   EraseMoves()
  1260.   {
  1261.      Rect er;
  1262.      
  1263.      er.top = TEXT_Y - 20;
  1264.      er.left = 0;
  1265.      er.bottom = TEXT_Y + 10;
  1266.      er.right = windowRect.right - windowRect.left;
  1267.      PenMode(srcCopy);
  1268.      EraseRect(&er);
  1269.  }
  1270.    /*==============================================
  1271.   *  ShowEnd() -- Show the End Game Message
  1272.   *=============================================*/
  1273.   
  1274.   ShowEnd()
  1275.   {
  1276.      char out[120], *ip, *op;
  1277.      static char *s1 = "Congratulations -- A Perfect Score of ";
  1278.      static char *s2 = "You solved it in ";
  1279.      static char *s3 = " Moves!";
  1280.      static char *s4 = " (But it can be done in  ";  
  1281.      extern char *utoa();
  1282.      int xc;
  1283.      Handle soundHandle;
  1284.  
  1285.     op = &out[1];
  1286.     ip = (NumberofMoves <= BestNum) ? s1 : s2;
  1287.      for(;*ip != '\0';) *op++ = *ip++;
  1288.      op = utoa(NumberofMoves, op);
  1289.      ip = s3;
  1290.      for(;*ip != '\0';) *op++ = *ip++; 
  1291.      if (NumberofMoves > BestNum) {
  1292.          soundHandle = goofHandle;
  1293.          ip = s4;        
  1294.          for(;*ip != '\0';) *op++ = *ip++; 
  1295.          op = utoa(BestNum, op);
  1296.          *op++ = '!';
  1297.          *op++ = ')';
  1298.          *op++ = '\0';
  1299.      }
  1300.      else {
  1301.          soundHandle = prestoHandle;
  1302.     }
  1303.      out[0] = (char)(op - &out[1]);
  1304.      xc = (windowRect.left + windowRect.right)/2;
  1305.      MoveTo(xc - StringWidth(out)/2 - windowRect.left, TEXT_Y);
  1306.      DrawString(out);
  1307.      if (soundok && soundactive) SndPlay((Ptr)NULL, soundHandle, SYNCHRONOUS);
  1308.     RippleDisks(g_target, NUM_RIPPLE);     
  1309.  }
  1310.  /*****************************************************
  1311.   *  RippleDisks -- Add a little pizazz to a finished game by
  1312.   *  sequentially flashing the disks on the final stack.
  1313.   *  Arguments:
  1314.   *    pin -- index of pin
  1315.   *    count -- number of cycles of flashing to do
  1316.   *****************************************************/
  1317.   
  1318.   RippleDisks(pin, count)
  1319.       int pin, count;
  1320.   {
  1321.       int i, j, center, vloc;
  1322.       
  1323.     center = LEFT_MOST + pin*BASE_SPACING + BASE_WIDTH/2; 
  1324.  
  1325.     for (i=0;i<count;i++) {     
  1326.         for (j=0;j<g_level;j++) {
  1327.             vloc = BOTTOM_MOST - (j+1)*DISK_HEIGHT;
  1328.             HiLiteDisk(g_level-j-1, vloc, center);
  1329.             DrawDisk(g_level-j-1, vloc, center, 1);
  1330.         }
  1331.     }
  1332. }
  1333.   
  1334. /*=============================================
  1335.  *  utoa -- convert a positive int to a string.
  1336.  *  Function returns a pointer to the null terminator.
  1337.  *=============================================*/
  1338.    
  1339. char * utoa(num, str)
  1340.        int num;
  1341.        char *str;
  1342. {
  1343.    
  1344.        char tmp[8], *tp;
  1345.        int tn, ll, i;
  1346.        tp = &tmp[0];
  1347.        ll = 0;
  1348.        do {
  1349.            tn = num%10;
  1350.            *tp++ = tn + '0';
  1351.            ll++;
  1352.            num /= 10;
  1353.        } while (num);
  1354.        
  1355.        for (i=0;i<ll;i++) *str++ = *(--tp);
  1356.        *str = '\0';
  1357.        return str;
  1358. }
  1359.  
  1360.            
  1361.   
  1362.  
  1363.  
  1364.