home *** CD-ROM | disk | FTP | other *** search
- /* hanoi.c 21-Feb-1987 From: 2294.comp-sys-amiga
- **
- ** 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 <return> 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 <exec/types.h>
- #include <intuition/intuition.h>
- #include <graphics/gfxmacros.h>
- #include <stdio.h>
-
- /* 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, <CR> 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<numdisks; i++) {
- disks[0][i] = numdisks-i;
- disks[1][i] = 0;
- disks[2][i] = 0;
- }
- topdisk[0] = numdisks;
- topdisk[1] = 0;
- topdisk[2] = 0;
- }
-
- /* Write over *everything* in sight, including the borders and the title...
- */
- ClearWindow ()
- {
- SetOPen (HanoiRPort, windowcolor);
- SetAPen (HanoiRPort, windowcolor);
- RectFill (HanoiRPort, 0L, 0L, (long)(width-1), (long)(height-1));
- }
-
- static char titlestring[8] = "X->X ";
-
- /* ShowMoveInfo will print out the current move being made. Note that
- ** this routine is NOT CALLED at all if the user specifies the text color
- ** to be the same as the window color... This routine does slow down the
- ** program, but it makes it a more educational tool...
- */
- ShowMoveInfo (fromtower, totower, ndisks)
- int fromtower, totower, ndisks;
- {
- if (ndisks > 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);
- }
-