home *** CD-ROM | disk | FTP | other *** search
- /******************************************************
- * Towers of Hanoi W.M. Leue
- * Rev 0.0 11/4/84 Original version written in Macintosh Pascal.
- * Rev 1.0 1/23/88 Translated to Lightspeed C 2.0
- * Rev 2.0 9/5/89 Changed recursive to iterative algorithm.
- ******************************************************/
-
- #include "Quickdraw.h"
- #include "MacTypes.h"
- #include "MenuMgr.h"
- #include "WindowMgr.h"
- #include "EventMgr.h"
- #include "ControlMgr.h"
- #include "FontMgr.h"
- #include "DialogMgr.h"
- #include "SoundMgr.h"
-
- /* Graphics-Related Literals */
-
- #define WINDOW_WIDTH 500
- #define WINDOW_HEIGHT 290
- #define WMARGIN 4
-
- #define NUMBER_OF_PINS 3
- #define SUM_OF_PINS 3
- #define NUM_DISKS 5
- #define MAX_DISKS 10
- #define BASE_WIDTH 130
- #define BASE_HEIGHT 20
- #define LEFT_MOST 35
- #define BOTTOM_MOST 250
- #define BASE_SPACING 150
- #define PIN_HEIGHT 160
- #define PIN_WIDTH 10
- #define PIN_MARGIN 10
- #define DISK_HEIGHT 15
- #define DISK_STEP 10
- #define MARGIN 3
- #define BIGGEST_DISK 120
- #define DISK_COR_WIDTH 12
- #define DISK_COR_HEIGHT 12
- #define LIFT_STEP 10
- #define PEAK 40
- #define CARRY_STEP 10
- #define TEXT_Y 30
- #define NUMX 300
- #define SYSFONT 0
- #define TEXTSZ 12
- #define NUM_RIPPLE 5
-
- /* Game States */
- #define NONE 0
- #define NEW 1
- #define INPROG 2
- #define DONE 3
- #define MANUAL 4
-
- /* Pick States */
- #define SOURCE 0
- #define DEST 1
-
- /* Speed States */
- #define SLOW 1
- #define MEDIUM 2
- #define FAST 3
-
- /* Pin ID's */
- #define LEFT 0
- #define MIDDLE 1
- #define RIGHT 2
-
- #define ABOUT_COM 1
-
- #define QUIT_COM 1
-
- #define UNDO_COM 1
- #define CUT_COM 3
- #define COPY_COM 4
- #define PASTE_COM 5
-
- #define NEW_COM 1
- #define RUN_COM 2
- #define STEP_COM 3
- #define HELP_COM 5
-
- #define D2_COM 1
- #define D3_COM 2
- #define D4_COM 3
- #define D5_COM 4
- #define D6_COM 5
- #define D7_COM 6
- #define D8_COM 7
- #define D9_COM 8
- #define D10_COM 9
- #define SLOW_COM 11
- #define FASTER_COM 12
- #define FASTEST_COM 13
- #define SOUND_COM 15
- #define COLOR_COM 16
-
- /* Private Resource ID's */
- #define windowID 128
- #define About_ID 258
- #define CLICK_ID 257
- #define GOOF_ID 259
- #define PRESTO_ID 260
- #define VERS_ID 1
- #define SIZE_ID -1
- #define MBAR_ID 400
- #define Desk_ID 400
- #define File_ID 401
- #define Edit_ID 402
- #define Commands_ID 403
- #define Options_ID 404
-
- #define NULL (long)0
-
- #define ON 1
- #define OFF 0
-
- #define CLICK_SND 4
- #define SYNCHRONOUS 0
-
- #define WNE_TRAP_NUM 0x60
- #define UNIMPL_TRAP_NUM 0x9F
-
- MenuHandle DeskMenu;
- MenuHandle FileMenu;
- MenuHandle EditMenu;
- MenuHandle CommandsMenu;
- MenuHandle OptionsMenu;
- WindowPtr myWindow;
- WindowRecord wRecord;
-
- int NumonTower[NUMBER_OF_PINS];
- int NumberofMoves;
- int g_source, g_target, g_level;
- int g_speed, g_vstep, g_hstep, g_state, g_fcolor;
- int g_pick, g_msrc, g_mdst, g_mvloc, g_mcenter, g_mlev;
- int DiskLocs[MAX_DISKS], DiskForeColors[MAX_DISKS], DiskBackColors[MAX_DISKS];
- int BestNum;
- Rect ssize, windowRect;
- int undo_level, undo_source, undo_target;
- Handle clickHandle, goofHandle, prestoHandle;
- int soundok, soundactive;
- extern ShowHelp();
-
- main()
-
- {
- extern InitWorld();
- extern void DrawTowers(), InitDisks();
- extern void MoveTower();
- extern void BuildMenus(), DoClick();
- extern int DoMenu();
- EventRecord myEvent;
- WindowPtr whichWindow;
- int ok, gWNEImplemented;
- char ch;
-
-
- InitWorld();
- BuildMenus();
- DrawTowers();
- InitDisks(NUM_DISKS);
- ok = TRUE;
- gWNEImplemented = (NGetTrapAddress(WNE_TRAP_NUM, ToolTrap) !=
- NGetTrapAddress(UNIMPL_TRAP_NUM, ToolTrap));
-
- /* Main Event Loop */
-
- do {
- if (gWNEImplemented) {
- WaitNextEvent(everyEvent, &myEvent, 0, NULL);
- }
- else {
- SystemTask();
- GetNextEvent(everyEvent, &myEvent);
- }
- switch(myEvent.what) {
- case mouseDown:
- switch(FindWindow(myEvent.where, &whichWindow)) {
- case inDesk:
- break;
- case inMenuBar:
- ok = DoMenu(MenuSelect(myEvent.where));
- break;
- case inGoAway:
- if (TrackGoAway(whichWindow, &myEvent.where)) ok = 0;
- break;
- case inDrag:
- doDrag(myEvent.where);
- break;
- case inSysWindow:
- SystemClick(&myEvent, whichWindow);
- break;
- case inContent:
- if (whichWindow != FrontWindow()) {
- SelectWindow(whichWindow);
- }
- else {
- DoClick(myEvent.where);
- }
- break;
- default:
- break;
- }
- break;
-
- case keyDown:
- ch = myEvent.message & charCodeMask;
- if (myEvent.modifiers & cmdKey) {
- ok = DoMenu(MenuKey(ch));
- }
- break;
-
- case activateEvt:
- doActivate(myEvent);
- break;
-
- case updateEvt:
- doUpdate();
- break;
-
- default:
- break; /* ignore other events */
- } /* end of Event type switch */
- } while(ok); /* end of Event loop */
-
- }
-
- /********************************************************
- * InitWorld() -- Initialize the Macintosh Environment; Create Window;
- * set up defaults.
- * Arguments: none
- *********************************************************/
-
- InitWorld()
- {
- long port;
- char *Title;
- int i, sw, sh, shc, svc;
-
- /* Do all Mac Manager Init's */
- InitGraf(&thePort);
- InitFonts();
- FlushEvents(everyEvent, 0);
- InitWindows();
- InitMenus();
- InitDialogs(NULL);
-
- /* Get screen size, locate window in the center */
- ssize = screenBits.bounds;
- sw = ssize.right - ssize.left + 1;
- sh = ssize.bottom - ssize.top + 1;
- shc = ssize.left + sw/2;
- svc = ssize.top + sh/2;
-
- /* Build the application's single window */
- SetRect(&windowRect, shc-WINDOW_WIDTH/2,
- svc - WINDOW_HEIGHT/2,
- shc + WINDOW_WIDTH/2,
- svc + WINDOW_HEIGHT/2);
- Title = "\pTowers of Hanoi";
- myWindow = NewWindow(&wRecord,
- &windowRect,
- Title,
- TRUE,
- noGrowDocProc,
- (WindowPtr)-1,
- TRUE,
- NULL);
- SetPort(myWindow);
- InitCursor();
- soundok = ((clickHandle = GetResource('snd ', CLICK_ID)) != (Handle)NULL);
- if (soundok) {
- goofHandle = GetResource('snd ', GOOF_ID);
- prestoHandle = GetResource('snd ', PRESTO_ID);
- }
- soundactive = soundok;
-
- /* One-time game initializations */
- g_source = LEFT;
- g_target = MIDDLE;
- g_speed = SLOW;
- g_vstep = LIFT_STEP;
- g_hstep = CARRY_STEP;
- g_pick = SOURCE; /* init move state machine */
- g_state = NONE; /* game not yet ready */
- g_level = 0; /* No disks yet */
- g_fcolor = 0; /* default color scheme */
- for (i=0;i<NUMBER_OF_PINS;i++) NumonTower[i] = 0;
-
- /* Disk Colors -- Dithered Patterns give Disk colors of magenta, red, orange, */
- /* yellow, chartruese, green, cyan, blue, violet, and black if all 10 disks are */
- /* used, for an approximate rainbow spectrum. Only classic QD is used! */
-
- DiskForeColors[0] = magentaColor; DiskBackColors[0] = magentaColor;
- DiskForeColors[1] = redColor; DiskBackColors[1] = redColor;
- DiskForeColors[2] = redColor; DiskBackColors[2] = yellowColor;
- DiskForeColors[3] = yellowColor; DiskBackColors[3] = yellowColor;
- DiskForeColors[4] = greenColor; DiskBackColors[4] = yellowColor;
- DiskForeColors[5] = greenColor; DiskBackColors[5] = greenColor;
- DiskForeColors[6] = cyanColor; DiskBackColors[6] = cyanColor;
- DiskForeColors[7] = blueColor; DiskBackColors[7] = blueColor;
- DiskForeColors[8] = blueColor; DiskBackColors[8] = redColor;
- DiskForeColors[9] = blackColor; DiskBackColors[9] = blackColor;
-
- TextFont(SYSFONT);
-
- }
-
- /********************************************************
- * DoMenu() -- Process Menu Selections
- * Arguments:
- * which: Event Record for menu selection.
- ********************************************************/
- int DoMenu(which)
- long which;
- {
- short menuID, itemNumber;
- extern void InitDisks();
- int i;
- extern void NextMove();
- Str255 daName;
- extern ShowHelp();
-
-
- menuID = HiWord(which);
- itemNumber = LoWord(which);
-
- switch(menuID) {
- case(Desk_ID):
- switch(itemNumber) {
- case(ABOUT_COM):
- doAbout();
- break;
- default:
- GetItem(DeskMenu, itemNumber, &daName);
- OpenDeskAcc(&daName);
- break;
- }
- break;
- case(File_ID):
- switch(itemNumber) {
- case(QUIT_COM):
- HiliteMenu(0);
- return(0);
- break;
- }
- break;
- case(Edit_ID):
- if (SystemEdit(itemNumber-1)) break;
- switch(itemNumber) {
- case(UNDO_COM):
- doUndo();
- break;
- case(CUT_COM):
- break;
- case(COPY_COM):
- break;
- case(PASTE_COM):
- break;
- }
- break;
- case(Commands_ID):
- switch(itemNumber) {
- case(NEW_COM):
- if (g_state != NEW) {
- InitDisks(g_level);
- }
- break;
- case(STEP_COM):
- if ((g_state == NEW) || (g_state == INPROG)) {
- if (g_state == NEW) {
- g_target = (g_level%2) ? MIDDLE : RIGHT;
- DrawBase(g_target, dkGray);
- g_state = INPROG;
- }
- NextMove();
- toggleMenu(Commands_ID, NEW_COM, ON);
- }
- break;
- case(RUN_COM):
- if ((g_state == NEW) || (g_state == INPROG)) {
- g_state = INPROG;
- toggleMenu(Commands_ID, NEW_COM, ON);
- MoveTower(g_source, g_target);
- }
- break;
- case(HELP_COM):
- ShowHelp();
- break;
- }
- break;
- case(Options_ID):
- switch(itemNumber) {
- case(D2_COM):
- case(D3_COM):
- case(D4_COM):
- case(D5_COM):
- case(D6_COM):
- case(D7_COM):
- case(D8_COM):
- case(D9_COM):
- case(D10_COM):
- for (i=D2_COM;i<=D10_COM;i++) {
- CheckItem(OptionsMenu, (short)i, FALSE);
- }
- CheckItem(OptionsMenu, (short)itemNumber, TRUE);
- InitDisks(itemNumber+1);
- break;
- case(SLOW_COM):
- g_speed = SLOW; g_vstep = LIFT_STEP; g_hstep = CARRY_STEP;
- CheckItem(OptionsMenu, (short)SLOW_COM, TRUE);
- CheckItem(OptionsMenu, (short)FASTER_COM, FALSE);
- CheckItem(OptionsMenu, (short)FASTEST_COM, FALSE);
- break;
- case(FASTER_COM):
- g_speed = MEDIUM; g_vstep = LIFT_STEP*2;
- g_hstep = CARRY_STEP*2;
- CheckItem(OptionsMenu, (short)SLOW_COM, FALSE);
- CheckItem(OptionsMenu, (short)FASTER_COM, TRUE);
- CheckItem(OptionsMenu, (short)FASTEST_COM, FALSE);
- break;
- case(FASTEST_COM):
- g_speed = FAST;
- CheckItem(OptionsMenu, (short)SLOW_COM, FALSE);
- CheckItem(OptionsMenu, (short)FASTER_COM, FALSE);
- CheckItem(OptionsMenu, (short)FASTEST_COM, TRUE);
- break;
- case(SOUND_COM):
- if (soundok) {
- soundactive = 1 - soundactive;
- if (soundactive) {
- CheckItem(OptionsMenu, (short)SOUND_COM, TRUE);
- }
- else {
- CheckItem(OptionsMenu, (short)SOUND_COM, FALSE);
- }
- }
- break;
- case(COLOR_COM):
- g_fcolor = (g_fcolor+1)%MAX_DISKS;
- reDraw();
- break;
- }
- break;
- }
- HiliteMenu(0);
- return(1);
- }
-
- /*******************************************************
- * ours() -- Boolean function which returns TRUE if its argument is
- * a pointer to the application's window, false otherwise.
- * Arguments:
- * w -- a pointer to a window
- *******************************************************/
-
- int ours(w)
- WindowPtr w;
- {
- return((myWindow != NULL) && (w == myWindow));
- }
-
- /*********************************************************
- * DoClick -- Handle a Mouse Click in the content section of the window
- * Arguments:
- * where -- location of the click in local coordinates.
- *********************************************************/
- void DoClick(where)
- Point where;
- {
- extern int PickTower();
- extern void HiLiteDisk(), MoveDisk(), DrawDisk();
- int pin, svloc, scenter, num, lev, over, i;
-
-
- if (g_state == DONE) return;
-
- GlobalToLocal(&where);
- pin = PickTower(where);
- if (pin < 0) return;
- num = NumonTower[pin];
- svloc = BOTTOM_MOST - num*DISK_HEIGHT;
- scenter = LEFT_MOST + pin*BASE_SPACING + BASE_WIDTH/2;
-
-
- switch (g_pick) {
-
- case(SOURCE):
- /* See if legitimate Source Tower has been picked */
-
- if (NumonTower[pin] == 0) {
- SysBeep(10);
- return;
- }
- for (lev=0;DiskLocs[lev] != pin;lev++);
- HiLiteDisk(lev, svloc, scenter);
- g_msrc = pin;
- g_mvloc = svloc;
- g_mcenter = scenter;
- g_mlev = lev;
- g_pick = DEST;
- break;
-
- case(DEST):
- if (pin == g_msrc) {
- DrawDisk(g_mlev, g_mvloc, g_mcenter, 1);
- g_pick = SOURCE;
- return;
- }
- if (NumonTower[pin] > 0) {
- for (lev=0;DiskLocs[lev] != pin;lev++);
- if (lev < g_mlev) {
- DrawDisk(g_mlev, g_mvloc, g_mcenter, 1);
- SysBeep(10);
- g_pick = SOURCE;
- return;
- }
- }
- undo_level = g_mlev;
- undo_source = pin;
- undo_target = g_msrc;
- MoveDisk(g_mlev, g_msrc, pin);
- g_state = MANUAL;
- g_pick = SOURCE;
- toggleMenu(Edit_ID, UNDO_COM, ON);
- toggleMenu(Commands_ID, RUN_COM, OFF);
- toggleMenu(Commands_ID, STEP_COM, OFF);
- toggleMenu(Commands_ID, NEW_COM, ON);
- NumberofMoves++;
- EraseMoves();
- if (NumberofMoves == 1) {
- ShowMoves();
- if (g_level%2) {
- g_target = pin;
- }
- else {
- g_target = SUM_OF_PINS - pin;
- }
- DrawBase(g_target, dkGray);
- }
- else {
- over = 1;
- for (i=0;i<g_level;i++) if (DiskLocs[i] != g_target) over = 0;
- if (over) {
- ShowEnd();
- g_state = DONE;
- toggleMenu(Edit_ID, UNDO_COM, OFF);
- }
- else {
- ShowMoves();
- }
- }
- break;
-
- }
- }
-
- /*****************************************************
- * PickTower -- Translate the location of a mouse click in the
- * content region of our window into the index of a tower.
- * Arguments:
- * where -- location of the mouse click in local coordinates.
- * Function Returns: index (0..2) of picked tower, or -1 if no
- * tower is selected.
- *****************************************************/
-
- int PickTower(where)
- Point where;
- {
- if ((where.v < BOTTOM_MOST - PIN_HEIGHT) ||
- (where.v > BOTTOM_MOST + BASE_HEIGHT)) {
- SysBeep(10);
- return(-1);
- }
- if ((where.h >= LEFT_MOST) && (where.h <= LEFT_MOST +
- BASE_WIDTH)) return(LEFT);
- if ((where.h >= LEFT_MOST + BASE_SPACING) &&
- (where.h <= LEFT_MOST + BASE_SPACING + BASE_WIDTH))
- return(MIDDLE);
- if ((where.h >= LEFT_MOST + BASE_SPACING*2) &&
- (where.h <= LEFT_MOST + BASE_SPACING*2 + BASE_WIDTH))
- return(RIGHT);
- return(-1);
- }
-
- /**************************************************
- * ShadeDisk -- draw shading on a game disk.
- * Arguments:
- * index -- index of disk (0 is smallest)
- * DiskRect -- bounding rectangle of the disk
- * DiskSize -- horizontal size of the disk in pixels
- **************************************************/
- void ShadeDisk (index, DiskRect, DiskSize)
- int index;
- Rect DiskRect;
- int DiskSize;
- {
-
- Rect ArcRect;
-
- PenPat(white);
- BackColor(whiteColor);
- PenSize(2, 2);
-
- ArcRect.top = DiskRect.top + MARGIN;
- ArcRect.left = DiskRect.left + MARGIN;
- ArcRect.bottom = DiskRect.bottom - MARGIN;
- ArcRect.right = DiskRect.left + DISK_HEIGHT - MARGIN * 2;
- FrameArc(&ArcRect, 180, 180);
-
- MoveTo(ArcRect.left + (DISK_HEIGHT - MARGIN * 2)/2,
- ArcRect.bottom - 1);
- Line(DiskSize - DISK_HEIGHT, 0);
- PenPat(black);
- PenSize(1, 1);
- }
-
- /******************************************************
- * DrawDisk -- Draw a game disk.
- * Arguments:
- * index -- index number of the disk (0 is the smallest)
- * vloc -- vertical position of the top of the disk in pixels
- * center -- horizontal position of the center of the disk in pixels
- * shade -- if true, shade the disk, otherwise not
- ******************************************************/
- void DrawDisk (index, vloc, center, shade)
- int index, vloc, center, shade;
- {
-
- Rect DiskRect, InRect;
- int size, color;
-
- color = (g_fcolor + index)%MAX_DISKS;
- ForeColor(DiskForeColors[color]);
- BackColor(DiskBackColors[color]);
- size = BIGGEST_DISK - (g_level - index )*DISK_STEP;
- DiskRect.top = vloc;
- DiskRect.left = center - size/2;
- DiskRect.bottom = vloc + DISK_HEIGHT;
- DiskRect.right = DiskRect.left + size;
-
- PenMode(patCopy);
- FrameRoundRect(&DiskRect, DISK_COR_WIDTH, DISK_COR_HEIGHT);
-
- InRect.top = DiskRect.top + 1;
- InRect.left = DiskRect.left + 1;
- InRect.bottom = DiskRect.bottom - 1;
- InRect.right = DiskRect.right - 1;
-
- if (shade) {
- FillRoundRect(&InRect, DISK_COR_WIDTH, DISK_COR_HEIGHT, gray);
- ShadeDisk(index, DiskRect, size);
- }
- else {
- FillRoundRect(&InRect, DISK_COR_WIDTH, DISK_COR_HEIGHT, white);
- }
- ForeColor(blackColor);
- BackColor(whiteColor);
- }
-
- /******************************************************
- * EraseDisk -- Just what it says
- * Arguments:
- * index -- index number of the disk (0 is the smallest)
- * vloc -- vertical location of the top of the disk in pixels
- * center -- horizontal location of the center of the disk in pixels
- ******************************************************/
- void EraseDisk (index, vloc, center)
- int index, vloc, center;
- {
- Rect DiskRect;
- int size;
-
-
- size = BIGGEST_DISK - (g_level - index)*DISK_STEP;
- DiskRect.top = vloc;
- DiskRect.left = center - size/2;
- DiskRect.bottom = vloc + DISK_HEIGHT;
- DiskRect.right = DiskRect.left + size;
-
- FillRoundRect(&DiskRect, DISK_COR_WIDTH, DISK_COR_HEIGHT, white);
- }
-
- /******************************************************
- * HiLiteDisk -- show selection by turning disk white
- * Arguments:
- * index -- index number of the disk (0 is the smallest)
- * vloc -- vertical location of the top of the disk in pixels
- * center -- horizontal location of the center of the disk in pixels
- ******************************************************/
- void HiLiteDisk (index, vloc, center)
- int index, vloc, center;
- {
- Rect DiskRect;
- int size, i;
-
- size = BIGGEST_DISK - (g_level - index)*DISK_STEP;
- DiskRect.top = vloc;
- DiskRect.left = center - size/2;
- DiskRect.bottom = vloc + DISK_HEIGHT;
- DiskRect.right = DiskRect.left + size;
-
- ForeColor(blackColor);
- for (i=0;i<10;i++) {
- FillRoundRect(&DiskRect, DISK_COR_WIDTH, DISK_COR_HEIGHT, black);
- FillRoundRect(&DiskRect, DISK_COR_WIDTH, DISK_COR_HEIGHT, white);
- }
- FrameRoundRect(&DiskRect, DISK_COR_WIDTH, DISK_COR_HEIGHT);
- }
- /******************************************************
- * RestorePin -- redraw the missing piece of the 'pin' of a tower
- * after a disk has been erased.
- * Arguments:
- * vloc -- vertical location of the disk top in pixels
- * center -- horizontal location of the center of the disk in pixels
- ******************************************************/
- void RestorePin(vloc, center)
- int vloc;
- int center;
- {
- int x1, x2;
- Rect PTRect;
-
-
- x1 = center - PIN_WIDTH/2;
- x2 = center + PIN_WIDTH/2 - 1;
- PenPat(black);
- PenSize(1, 1);
-
- MoveTo(x1, BOTTOM_MOST - PIN_HEIGHT + PIN_WIDTH/2);
- LineTo(x1, vloc + DISK_HEIGHT - 1);
- MoveTo(x2, BOTTOM_MOST - PIN_HEIGHT + PIN_WIDTH/2);
- LineTo(x2, vloc + DISK_HEIGHT - 1);
-
- PTRect.top = BOTTOM_MOST - PIN_HEIGHT;
- PTRect.left = center - PIN_WIDTH/2;
- PTRect.bottom = PTRect.top + PIN_WIDTH;
- PTRect.right = center + PIN_WIDTH/2;
-
- FrameArc(&PTRect, -90, 180);
- }
-
- /******************************************************
- * DrawTowers -- Draw the Tower Base and Pins.
- * Arguments: none
- ******************************************************/
- void DrawTowers ()
- {
- int i;
- Rect BaseRect, PinRect, DiskRect;
-
-
-
- PenPat(black);
-
- PinRect.top = BOTTOM_MOST - PIN_HEIGHT;
- PinRect.left = LEFT_MOST + BASE_WIDTH/2 - PIN_WIDTH/2;
- PinRect.bottom = BOTTOM_MOST + PIN_MARGIN;
- PinRect.right = PinRect.left + PIN_WIDTH;
-
- for (i=0;i<NUMBER_OF_PINS;i++) {
- FrameRoundRect(&PinRect, DISK_COR_WIDTH, DISK_COR_HEIGHT);
- PinRect.left += BASE_SPACING;
- PinRect.right = PinRect.left + PIN_WIDTH;
- }
-
- for (i=0;i<NUMBER_OF_PINS;i++) {
- DrawBase(i, gray);
- }
- }
-
- /**********************************************************
- * DrawBase -- Draw the Base of a Tower
- * Arguments:
- * pin -- Tower index 0..2
- * pat -- pattern index in standard Mac patterns
- **********************************************************/
-
- DrawBase(pin, pat)
- int pin;
- Pattern pat;
- {
- Rect BaseRect;
-
- BaseRect.top = BOTTOM_MOST;
- BaseRect.bottom = BaseRect.top + BASE_HEIGHT;
- BaseRect.left = LEFT_MOST + pin*BASE_SPACING;
- BaseRect.right = BaseRect.left + BASE_WIDTH;
-
- PenPat(pat);
- PaintRect(&BaseRect);
- PenPat(black);
- }
-
- /*********************************************************
- * InitDisks -- Set up for a new game. Erase any old disks, draw new
- * disks stack up on starting tower; initialize internal
- * database.
- * Arguments:
- * num -- number of disks to start with.
- *********************************************************/
- void InitDisks(num)
- int num;
- {
-
- int i, j, center, vloc, pin;
-
-
- for (i=0;i<NUMBER_OF_PINS;i++) DrawBase(i, gray);
-
- for (i=0;i<g_level;i++) {
- pin = DiskLocs[i];
- center = LEFT_MOST + pin*BASE_SPACING + BASE_WIDTH/2;
- vloc = BOTTOM_MOST - NumonTower[pin]*DISK_HEIGHT;
- EraseDisk(i, vloc, center);
- RestorePin(vloc, center);
- NumonTower[pin]--;
- }
-
- center = LEFT_MOST + g_source*BASE_SPACING + BASE_WIDTH/2;
- g_level = num;
-
- for (i=g_level-1;i>=0;i--) {
- NumonTower[g_source]++;
- vloc = BOTTOM_MOST - NumonTower[g_source]*DISK_HEIGHT;
- DrawDisk(i, vloc, center, 1);
- }
- NumberofMoves = 0;
- for (i=0;i<g_level;i++) DiskLocs[i] = g_source;
- BestNum = 1;
- for (i=0;i<num;i++) BestNum <<= 1;
- BestNum--;
- g_state = NEW;
- EraseMoves();
-
- toggleMenu(Edit_ID, UNDO_COM, OFF);
- toggleMenu(Commands_ID, NEW_COM, OFF);
- toggleMenu(Commands_ID, RUN_COM, ON);
- toggleMenu(Commands_ID, STEP_COM, ON);
- }
-
- /***********************************************************
- * MoveTower -- Move a tower from one pin to another. Called in
- * response to the "Run" menu command.
- * Arguments:
- * source -- index of source tower.
- * target -- index of destination tower.
- * Notes: This routine used to be recursive, but was changed to iterative
- * so that the game could be single-stepped conveniently (using the
- * 'Step' menu command, and also so that the user could get help from
- * the computer.
- **********************************************************/
- void MoveTower (source, target)
- int source, target;
-
- {
-
- int spare, xc;
- extern void NextMove();
- EventRecord myEvent;
- char ch, *ip, *op, out[80];
- static char *s1 = "Type \021-. to Interrupt";
-
-
- ip = s1; op = &out[1];
- for(;*ip != '\0';) *op++ = *ip++;
- out[0] = (char)(op - &out[1]);
- xc = (windowRect.left + windowRect.right)/2;
- MoveTo(xc - StringWidth(out)/2 - windowRect.left, TEXT_Y);
- DrawString(out);
-
- g_target = (g_level%2) ? MIDDLE : RIGHT;
- DrawBase(g_target, dkGray);
- do {
- NextMove();
- GetNextEvent(everyEvent, &myEvent);
- if (myEvent.what == keyDown) {
- ch = myEvent.message & charCodeMask;
- if ((ch == '.') && myEvent.modifiers & cmdKey) {
- EraseMoves();
- return;
- }
- }
- } while (g_state != DONE);
-
- EraseMoves();
-
- }
-
- /***********************************************************
- * NextMove -- Generate the next move in a standard game. Uses an
- * iterative algorithm rather than the usual recursive one.
- * The iterative algorithm gives the same results as the
- * recursive one, but is easier to single-step.
- * Arguments: none
- ***********************************************************/
- void NextMove()
- {
- int tmp, lev, src, dst, dir;
- extern void MoveDisk();
-
- NumberofMoves++;
- tmp = NumberofMoves;
- lev = 0;
-
- /* Use binary bits in 'NumberofMoves' right-to-left to find next piece to move */
- /* littlest disk moves every other time, next biggest moves every 4th time,
- /* next biggest moves every 8th time, etc. */
- for (;;) {
- if (tmp & 1) break;
- tmp >>= 1;
- lev++;
- }
-
- /* Now find source and destination pins for the selected disk */
- /* Disks of adjacent size move in opposite directions. */
- src = DiskLocs[lev];
- dir = 1 - ((lev%2) << 1);
- dst = src+dir;
- if (dst > RIGHT) dst = LEFT;
- if (dst < LEFT) dst = RIGHT;
-
- /* Move the Disk, check for game over */
- MoveDisk(lev, src, dst);
- if (NumberofMoves >= BestNum) {
- g_state = DONE;
- toggleMenu(Commands_ID, STEP_COM, OFF);
- toggleMenu(Commands_ID, RUN_COM, OFF);
- if (soundok && soundactive) SndPlay((Ptr)NULL, prestoHandle, SYNCHRONOUS);
- RippleDisks(g_target, NUM_RIPPLE);
- }
- }
-
- /******************************************************
- * MoveDisk -- Move a disk from one pin to another. Animate the
- * move and update the internal database.
- * Arguments:
- * level: index of the disk to move.
- * source: index of the source tower
- * target: index of the destination tower.
- ******************************************************/
-
- void MoveDisk (level, source, target)
- int level;
- int source, target;
- {
- int svloc, tvloc, scenter, tcenter;
- void LiftDisk(), DropDisk(), CarryDisk();
-
- svloc = BOTTOM_MOST - NumonTower[source]*DISK_HEIGHT;
- scenter = LEFT_MOST + source*BASE_SPACING + BASE_WIDTH/2;
- tcenter = LEFT_MOST + target*BASE_SPACING + BASE_WIDTH/2;
- NumonTower[target]++;
- tvloc = BOTTOM_MOST - NumonTower[target]*DISK_HEIGHT;
-
- if (g_speed == FAST) {
- EraseDisk(level, svloc, scenter);
- RestorePin(svloc, scenter);
- }
- else {
- LiftDisk(level, source);
- }
- NumonTower[source]--;
- DiskLocs[level] = target;
- if (g_speed <= MEDIUM) CarryDisk(level, source, target);
- if (g_speed == FAST) {
- DrawDisk(level, tvloc, tcenter);
- }
- else {
- DropDisk(level, target);
- }
- if (soundok && soundactive) SndPlay((Ptr)NULL, clickHandle, SYNCHRONOUS);
- }
-
- /******************************************************
- * LiftDisk -- animate lifting a disk off the source pin.
- * Arguments:
- * level -- index of the disk to be moved.
- * pin -- index of the source pin.
- ******************************************************/
- void LiftDisk(level, pin)
- int level, pin;
- {
- int vloc, center, num, i, v;
-
- vloc = BOTTOM_MOST - NumonTower[pin]*DISK_HEIGHT;
- center = LEFT_MOST + pin*BASE_SPACING + BASE_WIDTH/2;
- num = (vloc - PEAK)/g_vstep;
- v = vloc;
-
- for (i=0;i<num;i++) {
- EraseDisk(level, v, center);
- RestorePin(vloc, center);
- v -= g_vstep;
- DrawDisk(level, v, center, 0);
- }
- EraseDisk(level, v, center);
- }
-
- /*****************************************************
- * DropDisk -- animate dropping a disk onto the destination pin
- * Arguments:
- * level -- index of the disk to be moved.
- * pin -- index of the destination pin
- *****************************************************/
- void DropDisk(level, pin)
- int level, pin;
- {
- int vloc, center, num, i, v;
-
- vloc = BOTTOM_MOST - NumonTower[pin]*DISK_HEIGHT;
- center = LEFT_MOST + pin*BASE_SPACING + BASE_WIDTH/2;
- num = (vloc - PEAK)/g_vstep;
- v = vloc - num*g_vstep;
-
- for (i=0;i<num;i++) {
- DrawDisk(level, v, center, 0);
- EraseDisk(level, v, center);
- RestorePin(vloc, center);
- v += g_vstep;
- }
- DrawDisk(level, v, center, 1);
- }
-
- /****************************************************
- * CarryDisk -- animate sliding the disk from the source to
- * destination pins.
- * Arguments:
- * level -- index of the selected disk.
- * source -- index of the source pin.
- * target -- index of the destination pin.
- ****************************************************/
- void CarryDisk(level, source, target)
- int level, source, target;
- {
- int start, end, h, h2, dir;
-
-
- start = LEFT_MOST + source*BASE_SPACING + BASE_WIDTH/2;
- end = LEFT_MOST + target*BASE_SPACING + BASE_WIDTH/2;
- dir = (start < end) ? g_hstep : -g_hstep;
- h = start;
-
- if (dir > 0) {
- do {
- DrawDisk(level, PEAK, h, 0);
- EraseDisk(level, PEAK, h);
- h += dir;
- } while (h < end);
- }
- else {
-
- do {
- DrawDisk(level, PEAK, h, 0);
- EraseDisk(level, PEAK, h);
- h += dir;
- } while (h > end);
- }
- }
-
- /*****************************************************
- * BuildMenus -- Create the Application's Menus.
- * Arguments -- none
- *****************************************************/
- void BuildMenus()
- {
- Handle myMBar;
-
- myMBar = GetNewMBar(MBAR_ID);
- SetMenuBar(myMBar);
-
- DeskMenu = GetMHandle(Desk_ID);
- AddResMenu(DeskMenu, 'DRVR');
- FileMenu = GetMHandle(File_ID);
- EditMenu = GetMHandle(Edit_ID);
- CommandsMenu = GetMHandle(Commands_ID);
- OptionsMenu = GetMHandle(Options_ID);
-
- CheckItem(OptionsMenu, D5_COM, TRUE);
- CheckItem(OptionsMenu, SLOW_COM, TRUE);
- if (soundok) {
- CheckItem(OptionsMenu, SOUND_COM, TRUE);
- }
- else {
- toggleMenu(OptionsMenu, SOUND_COM, OFF);
- }
-
- DrawMenuBar();
-
- }
- /* =============================================
- * doUpdate -- action routine for UpdateEvt's
- * ============================================= */
-
- doUpdate()
- {
- BeginUpdate(myWindow);
- reDraw();
- EndUpdate(myWindow);
- }
-
- /* ================================================
- * doActivate -- handle activate events
- * =============================================== */
-
- doActivate(event)
- EventRecord event;
- {
- if (event.modifiers & activeFlag) {
- SelectWindow(myWindow);
- }
- }
-
- /* ================================================
- * doDrag -- handle window drag events
- * ============================================= */
-
- doDrag(where)
- Point where;
- {
- Rect br;
-
- br.top = ssize.top+WMARGIN;
- br.left = ssize.left+WMARGIN;
- br.bottom = ssize.bottom-WMARGIN;
- br.right = ssize.right-WMARGIN;
-
- DragWindow(myWindow, where, &br);
- }
- /* ================================================
- * doAbout -- handle "About Hanoi..." Menu
- * ============================================= */
-
- doAbout()
- {
- DialogPtr myDialog;
- short itemHit;
-
- myDialog = GetNewDialog(About_ID, NULL,(WindowPtr)-1);
- do (ModalDialog((ProcPtr)NULL, &itemHit)); while (itemHit != 1);
- CloseDialog(myDialog);
- }
-
- /*================================================
- * doUndo -- handle Undo Command
- *===============================================*/
-
- doUndo()
- {
- int i;
-
-
- MoveDisk(undo_level, undo_source, undo_target);
- NumberofMoves--;
- EraseMoves();
- if (NumberofMoves) {
- ShowMoves();
- }
- else {
- g_state = NEW;
- toggleMenu(Commands_ID, RUN_COM, ON);
- toggleMenu(Commands_ID, STEP_COM, ON);
- for (i=0;i<NUMBER_OF_PINS;i++) DrawBase(1, gray);
- }
- toggleMenu(Edit_ID, UNDO_COM, OFF);
- }
-
- /* ================================================
- * toggleMenu -- enable or disable the selected
- * menu item.
- * Arguments:
- * menu -- ID of menu
- * item -- index of menu item
- * state -- 1 = enable, 0 = disable
- * ============================================= */
-
- toggleMenu(menu, item, state)
- int menu;
- int item;
- int state;
- {
-
- MenuHandle mh;
-
- switch(menu) {
- case(Desk_ID): mh = DeskMenu; break;
- case(File_ID): mh = FileMenu; break;
- case(Edit_ID): mh = EditMenu; break;
- case(Commands_ID): mh = CommandsMenu; break;
- case(Options_ID): mh = OptionsMenu; break;
- default: return;
- }
-
- if (state == 1) {
- EnableItem(mh, item);
- }
- else {
- DisableItem(mh, item);
- }
- }
-
- /* ==============================================
- * reDraw -- repaint the screen with all towers
- * and disks in the current positions.
- * ============================================ */
-
- reDraw()
- {
- int i, num, vloc, center, pin;
- int h[NUMBER_OF_PINS];
-
- DrawTowers();
- for (i=0;i<NUMBER_OF_PINS;i++) h[i] = 1;
- for (i=g_level-1;i>=0;i--) {
- pin = DiskLocs[i];
- center = LEFT_MOST + pin*BASE_SPACING + BASE_WIDTH/2;
- vloc = BOTTOM_MOST - h[pin]*DISK_HEIGHT;
- DrawDisk(i, vloc, center, 1);
- h[pin]++;
- }
- if (g_state == MANUAL) {
- EraseMoves();
- ShowMoves();
- }
- }
- /*===============================================
- * ShowMoves() -- Show the current number of moves
- * and the best possible number of moves. Done for
- * a manual game only.
- *===============================================*/
-
- ShowMoves()
- {
- char out[40], *ip, *op;
- static char *s1 = " move done -- best is ";
- static char *s2 = " moves done -- best is ";
- extern char *utoa();
- int xc, w;
-
- op = utoa(NumberofMoves, &out[1]);
- ip = (NumberofMoves > 1) ? s2 : s1;
- for(;*ip != '\0';) *op++ = *ip++;
- op = utoa(BestNum, op);
- out[0] = (char)(op - &out[1]);
- xc = (windowRect.left + windowRect.right)/2;
- w = StringWidth(out);
- MoveTo(xc - w/2 - windowRect.left, TEXT_Y);
- DrawString(out);
- }
-
- /*==============================================
- * EraseMoves() -- Erase the Text Move Display
- *=============================================*/
-
- EraseMoves()
- {
- Rect er;
-
- er.top = TEXT_Y - 20;
- er.left = 0;
- er.bottom = TEXT_Y + 10;
- er.right = windowRect.right - windowRect.left;
- PenMode(srcCopy);
- EraseRect(&er);
- }
- /*==============================================
- * ShowEnd() -- Show the End Game Message
- *=============================================*/
-
- ShowEnd()
- {
- char out[120], *ip, *op;
- static char *s1 = "Congratulations -- A Perfect Score of ";
- static char *s2 = "You solved it in ";
- static char *s3 = " Moves!";
- static char *s4 = " (But it can be done in ";
- extern char *utoa();
- int xc;
- Handle soundHandle;
-
- op = &out[1];
- ip = (NumberofMoves <= BestNum) ? s1 : s2;
- for(;*ip != '\0';) *op++ = *ip++;
- op = utoa(NumberofMoves, op);
- ip = s3;
- for(;*ip != '\0';) *op++ = *ip++;
- if (NumberofMoves > BestNum) {
- soundHandle = goofHandle;
- ip = s4;
- for(;*ip != '\0';) *op++ = *ip++;
- op = utoa(BestNum, op);
- *op++ = '!';
- *op++ = ')';
- *op++ = '\0';
- }
- else {
- soundHandle = prestoHandle;
- }
- out[0] = (char)(op - &out[1]);
- xc = (windowRect.left + windowRect.right)/2;
- MoveTo(xc - StringWidth(out)/2 - windowRect.left, TEXT_Y);
- DrawString(out);
- if (soundok && soundactive) SndPlay((Ptr)NULL, soundHandle, SYNCHRONOUS);
- RippleDisks(g_target, NUM_RIPPLE);
- }
- /*****************************************************
- * RippleDisks -- Add a little pizazz to a finished game by
- * sequentially flashing the disks on the final stack.
- * Arguments:
- * pin -- index of pin
- * count -- number of cycles of flashing to do
- *****************************************************/
-
- RippleDisks(pin, count)
- int pin, count;
- {
- int i, j, center, vloc;
-
- center = LEFT_MOST + pin*BASE_SPACING + BASE_WIDTH/2;
-
- for (i=0;i<count;i++) {
- for (j=0;j<g_level;j++) {
- vloc = BOTTOM_MOST - (j+1)*DISK_HEIGHT;
- HiLiteDisk(g_level-j-1, vloc, center);
- DrawDisk(g_level-j-1, vloc, center, 1);
- }
- }
- }
-
- /*=============================================
- * utoa -- convert a positive int to a string.
- * Function returns a pointer to the null terminator.
- *=============================================*/
-
- char * utoa(num, str)
- int num;
- char *str;
- {
-
- char tmp[8], *tp;
- int tn, ll, i;
- tp = &tmp[0];
- ll = 0;
- do {
- tn = num%10;
- *tp++ = tn + '0';
- ll++;
- num /= 10;
- } while (num);
-
- for (i=0;i<ll;i++) *str++ = *(--tp);
- *str = '\0';
- return str;
- }
-
-
-
-
-
-