/* hanoi.c ** ** Towers of Hanoi for the Amiga, by Ali Ozer (ali@Score.Stanford.Edu). ** Originally written June '86, revised February '87. This program may be ** freely distributed. Please do not delete the author's name & picture: 8-) ** ** This program will create a window within the workbench screen and start ** solving the Towers of Hanoi problem (with 5 disks). The main purpose ** of this program is to sit in the side and absorb unused cycles --- By ** default it runs at priority -20... ** ** This program can be started from the CLI or the Workbench. If started ** from the CLI, it will accept command line arguments to change the various ** parameters. (Give "?" as an argument to see what arguments are expected.) ** If started from the Workbench, the program will open a window and explicitly ** ask for the parameters. Hit or ctrl-\ to use the default values. ** ** The idea for this program came from a similar program that runs on some ** Xerox Lisp machine. (I think it was some Xerox machine, I just saw it ** run one day as I was watching a friend work on one of them...) ** ** Future improvements to this code might include (if anyone wishes to ** play with this program) making the disks look better and making them ** move pixel by pixel instead of jumping as they do now. ** ** Thanks to Lee Taran (who was a few weeks ahead of me in solving the great ** mysteries of the Rom Kernel Manual, back in May '86, when we first got the ** Amiga --- Most of the initialization code is from her), and Tom Rokicki, ** who convinced us to get an Amiga in the first place and who also posted the ** code for the OpenInfoWindow routine used near the end of this program... */ #include #include #include #include /* Default values. Actually there are some more values in the program ** that should be #define'd here, such as the default speed, default number ** of disks, etc, but then I would have to have my strings work more ** dynamically, and, and, and... (excuses excuses) */ #define MAXDISKS 25 #define WIDTH 180 #define HEIGHT 60 #define WINDOWX (625-WIDTH) #define WINDOWY 14 #define TEXTDELAY 6L /* Time to wait after text */ #define MINWIDTHFORTEXT (4 + 8 * 7) /* 8: Font Width, 7: #chars */ FILE *fopen(), *input, *output, temp; /* For fputs/fgets console I/O. */ char *fgets(); void fputs(); void *OpenLibrary(), *OpenWindow(), *FindTask(), *IntuitionBase, *GfxBase; BYTE disks[3][MAXDISKS+1]; /* We have 3 towers with upto MAXDISKS disks */ int topdisk[3]; /* Free location on each tower (0 = all free) */ int towerloc[4]; /* Pixel location of each tower (4th = imag.) */ /* Tons of globals... */ int dx, dy, height, width, top, moveto, numsteps, numdisks, wbench, writetext; long movedelay, windowcolor, diskcolor, outlinecolor, beepcolor, textcolor, priority; struct NewWindow WindowInfo = { WINDOWX, WINDOWY, WIDTH, HEIGHT, /* Left, Top, Width, Height */ 0, 0, /* Detail pen, Block pen (-1 = use screens) */ CLOSEWINDOW | NEWSIZE | REFRESHWINDOW | SIZEVERIFY, /* IDCMPflags */ WINDOWDEPTH | WINDOWSIZING | SMART_REFRESH | WINDOWCLOSE | WINDOWDRAG, /* Flags */ NULL, NULL, NULL, /* FirstGadget, Menu Checkmark, Title */ NULL, NULL, /* Screen, Bitmap */ 0, 0, 640, 200, /* Min Width/Height Max Width/Height */ WBENCHSCREEN /* Type */ }; /* Structure to hold all the window info maintained by Intuition */ struct Window *HanoiWindow; struct RastPort *HanoiRPort; /* A macro to make dealing with undefined argv's easier... */ #define ARGV(n) (argc <= n ? "" : argv[n]) main(argc, argv) int argc; char *argv[]; { int i, cnt, GetNum(), GetArg(); struct Task *thistask; if (wbench = (argc == 0)) { /* Ie, started from the workbench */ if (!OpenInfoWindow()) exit (1); fputs ("Enter parameters, or ^\\ for defaults\n", output); numdisks = GetNum ("Number of disks? [1..25, default 5] ", 1, 25, 5); movedelay = 12-GetNum ("Speed? [1..12, default 4] ", 1, 12, 4); windowcolor = GetNum ("Background Color? [0..3, default 3] ", 0, 3, 3); textcolor = GetNum ("Text Color? [0..3, default 3] ", 0, 3, 3); diskcolor = GetNum ("Disk Color [0..3, default 2] ", 0, 3, 2); outlinecolor= GetNum ("Outline Color [0..3, default 2] ", 0, 3, 2); priority = -20L; CloseInfoWindow (); } else { /* Started from the CLI */ numdisks = GetArg (ARGV(1), 1, 25, 5); movedelay = 12-GetArg (ARGV(2), 1, 12, 4); windowcolor = GetArg (ARGV(3), 0, 3, 3); textcolor = GetArg (ARGV(4), 0, 3, 3); diskcolor = GetArg (ARGV(5), 0, 3, 2); outlinecolor= GetArg (ARGV(6), 0, 3, 2); WindowInfo.LeftEdge = GetArg (ARGV(7), 0, 640 - WIDTH, WINDOWX); WindowInfo.TopEdge = GetArg (ARGV(8), 0, 200 - HEIGHT, WINDOWY); priority = GetArg (ARGV(9), 0, 8, 0) * 5 - 20; }; if (thistask = FindTask (NULL)) SetTaskPri (thistask, (long)priority); numsteps = (numdisks * 2) + 4; writetext = (windowcolor != textcolor); WindowInfo.DetailPen = windowcolor; WindowInfo.BlockPen = windowcolor; WindowInfo.MinWidth = numsteps * 3; WindowInfo.MinHeight = numdisks * 2 + 4 + writetext * 10; WindowInfo.Height += (writetext * 10); /* Make sure beepcolor is different than disk or window color */ if ((beepcolor = ((diskcolor - 1) & 3)) == windowcolor) beepcolor = (beepcolor - 1) & 3; OpenLibraries(); SetupEnvironment(); ReCalculateParameters(); InitializeTowers(); ClearWindow(); DrawAllDisks(diskcolor); for (cnt = 0; cnt < 10; cnt++) { CheckForMessages (); Delay (20L); } while (1) for (i = 0; i < 3; i++) { MoveDisks(i, (i+1)%3, (i+2)%3, numdisks); DrawAllDisks (beepcolor); Delay (6L); DrawAllDisks (diskcolor); for (cnt = 0; cnt < numdisks * 3; cnt++) { CheckForMessages (); Delay (20L); } } } /* Called after resizing to determine new parameters... */ ReCalculateParameters() { int xstart, i; top = (writetext ? 9 : 0); height = HanoiWindow->Height; width = HanoiWindow->Width; dx = width / (3 * numsteps); dy = (height-top) / (numdisks + 2); xstart = (width - dx * 3 * numsteps) / 2; for (i = 0; i < 4; i++) towerloc[i] = xstart + i * numsteps * dx; moveto = top + dy; } DrawDiskAtLoc (disk, tower, location, diskcolor, outlinecolor) int disk, tower, location; long diskcolor, outlinecolor; { if (disk) { SetAPen (HanoiRPort, diskcolor); SetOPen (HanoiRPort, outlinecolor); RectFill (HanoiRPort, (long)(towerloc[tower] + (numdisks - disk + 1) * dx), (long)(height - (location + 1) * dy), (long)(towerloc[tower+1] - 1 - (numdisks - disk + 1) * dx), (long)(height - (location * dy) - 2)); } } DrawAllDisks(color) long color; { int tower, location; for (tower = 0; tower < 3; tower++) for (location = 0; location < numdisks; location++) DrawDiskAtLoc (disks [tower][location], tower, location, color, outlinecolor); } /* InitializeTowers will clear towers 1 and 2 and fill up tower 0. ** To start off, we have disk[0][0] = largest disk, disk[0][1] = next */ InitializeTowers() { int i; for (i=0; i 9) titlestring[5] = '0' + ndisks / 10; else titlestring[5] = ' '; titlestring[6] = '0' + ndisks % 10; titlestring[0] = 'A' + fromtower; titlestring[3] = 'A' + totower; SetAPen (HanoiRPort, textcolor); SetDrMd (HanoiRPort, JAM2); Move (HanoiRPort, 1L, 8L); Text (HanoiRPort, " ", 7L); SetDrMd (HanoiRPort, JAM1); Move (HanoiRPort, 1L, 8L); Text (HanoiRPort, titlestring, 7L); } /* This is the recursive Hanoi function! Looks simple, no? */ MoveDisks (fromtower, totower, sparetower, ndisks) int fromtower, totower, sparetower, ndisks; { if (writetext && width > MINWIDTHFORTEXT) { ShowMoveInfo (fromtower, totower, ndisks); Delay (TEXTDELAY * movedelay); CheckForMessages (); } if (ndisks == 1) /* Base case */ MoveOneDisk (fromtower, totower); else { MoveDisks (fromtower, sparetower, totower, ndisks-1); MoveOneDisk (fromtower, totower); MoveDisks (sparetower, totower, fromtower, ndisks-1); } } /* This moves (graphically) the top disk from fromtower to totower. */ MoveOneDisk (fromtower, totower) int fromtower, totower; { int cnt; int disk = disks[fromtower][topdisk[fromtower]-1]; if (writetext && width > MINWIDTHFORTEXT) ShowMoveInfo (fromtower, totower, 1); for (cnt = topdisk[fromtower]; cnt <= numdisks; cnt++) { DrawDiskAtLoc (disk, fromtower, cnt-1, windowcolor, windowcolor); DrawDiskAtLoc (disk, fromtower, cnt, diskcolor, outlinecolor); Delay (movedelay); } topdisk[fromtower]--; disks[fromtower][topdisk[fromtower]] = 0; CheckForMessages (); if (fromtower < totower) for (cnt = fromtower+1; cnt <= totower; cnt++) { DrawDiskAtLoc (disk, cnt-1, numdisks, windowcolor, windowcolor); DrawDiskAtLoc (disk, cnt, numdisks, diskcolor, outlinecolor); Delay (movedelay); } else for (cnt = fromtower-1; cnt >= totower; cnt--) { DrawDiskAtLoc (disk, cnt+1, numdisks, windowcolor, windowcolor); DrawDiskAtLoc (disk, cnt, numdisks, diskcolor, outlinecolor); Delay (movedelay); }; CheckForMessages (); for (cnt = numdisks-1; cnt >= topdisk[totower]; cnt--) { DrawDiskAtLoc (disk, totower, cnt+1, windowcolor, windowcolor); DrawDiskAtLoc (disk, totower, cnt, diskcolor, outlinecolor); Delay (movedelay); } disks[totower][topdisk[totower]] = disk; topdisk[totower]++; CheckForMessages (); } CheckForMessages () { struct IntuiMessage *message, *GetMsg(); ULONG class; /* What kind of message? */ /* Try to get a message. If there is one, process it. */ while (message = GetMsg(HanoiWindow->UserPort)) { class = message->Class; ReplyMsg(message); switch (class) { case SIZEVERIFY : Wait (1L << HanoiWindow->UserPort->mp_SigBit); break; case NEWSIZE : ReCalculateParameters(); /* Fall through */ case REFRESHWINDOW : ClearWindow(); DrawAllDisks(diskcolor); break; case CLOSEWINDOW : CloseThings(0); /* CloseThings exits */ default : CloseThings(1); /* CloseThings exits */ } } } OpenLibraries () { if (!(IntuitionBase = OpenLibrary("intuition.library",0L))) Panic("No intuition library\n"); if (!(GfxBase = OpenLibrary("graphics.library",0L))) Panic("No graphics library\n"); } /* Initializes the Hanoi window and sets up the HanoiRPort, used often. */ SetupEnvironment() { if (!(HanoiWindow = OpenWindow(&WindowInfo))) Panic ("Can't open window\n"); HanoiRPort = HanoiWindow->RPort; SetBPen (HanoiRPort, windowcolor); } /* Prints out an error message about what went wrong and exits gracefully */ Panic(errormsg) char *errormsg; { if (wbench) DisplayBeep (NULL); /* Simply flash the display... */ else puts (errormsg); CloseThings(1); } /* Closes the Libraries that have been opened and frees up all memory */ CloseThings(error_code) int error_code; { if (HanoiWindow) CloseWindow(HanoiWindow); if (GfxBase) CloseLibrary(GfxBase); if (IntuitionBase) CloseLibrary(IntuitionBase); exit (error_code); } int GetNum (prompt, min, max, def) int min, max, def; char *prompt; { int result, cnt; char resp[80]; while (1) { fputs (prompt, output); fflush (output); fgets (resp, 80, input); cnt = 0; while (resp[cnt] == ' ') cnt++; if (resp[cnt] == '\0' || resp[cnt] == '\n') return (def); result = 0; while (resp[cnt] != '\0' && resp[cnt] != '\n' && result <= max && result != -1) { if (resp[cnt] < '0' || resp[cnt] > '9') result = -1; else result = result * 10 + resp[cnt++] - '0'; }; if (result >= min && result <= max) return (result); if (result == -1) fputs ("Illegal integer, please try again.\n", output); else fputs ("Value out of range, please try again.\n", output); } } /* Opens a console window for fputs/fgets compatible I/O. Thanks to Tom ** Rokicki (from whose posting the last 4 lines of this routine come...). */ int OpenInfoWindow () { static char consp[] = "CON:30/30/400/80/Hanoi by Ali Ozer"; consp[30] = 214; /* Guess what this does? Run the program and see */ if ((output = fopen (consp, "w+")) == NULL) return (0); input = &temp; *input = *output; return (1); } CloseInfoWindow () { fclose (input); fclose (output); } /* Gets a positive integer from argstr. If argstr is empty or "-", return ** defaultnum. Error if argstr is an invalid number or is not in range. */ int GetArg (argstr, min, max, def) int min, max, def; char *argstr; { int result = 0; if (strlen(argstr) == 0 || !strcmp(argstr,"-")) return (def); while (*argstr != '\0' && result <= max) { if (*argstr < '0' || *argstr > '9') IllegalArgs(); result = result * 10 + *argstr++ - '0'; }; if (result >= min && result <= max) return (result); IllegalArgs (); } /* Prints usage line and causes program to terminate. Considering we only ** call GetArg once we know we were called from CLI, we can safely do puts. */ IllegalArgs () { puts ("\nUsage:\nHanoi NDisks Speed BGColor TxtColor DskColor OutLineColor X Y Priority"); puts (" NDisks 1..25, Speed 0..12, Colors 0..3 (WorkBench colors)"); puts (" Priority 0..8 (Corresponding to -20..+20)"); puts (" All arguments are optional. Use - to get a default value.\n"); CloseThings (1); } /* hanoi.c, by Ali Ozer */