ali@navajo.UUCP (02/21/87)
----->8 cut cut -------- Here's a fun little program. It solves the towers of hanoi in a workbench window of its own... It occupies little space, and is meant to be run with a low priority (by default, -20). I was inspired by a similar program I saw running on a workstation once. (But this is better (of course), because it has color and runs on our favorite micro.) The program will go on solving the puzzle forever, unless stopped by clicking on the (invisible) close gadget. The window also provides sizing and depth gadgets, all also invisible. You can vary the parameters like speed at which the disks are moved, the number of disks (1..25), colors, etc... From CLI, you can specify these parameters as command line arguments (type "hanoi ?" to see what is expected). From the Workbench, the program opens a little window and asks you for the values... Using the default values (5 disks, moving pretty slowly, and no text), and a stack size of 2000, I was able to get 22 hanoi's running (with 512k memory). Try it if you've got nothing better to do --- it's real fun! The overall speed at which the puzzle is solved doesn't change, really. (But, let me warn you, starting up 22 hanois will take some time... By the last few Intuition is really having a fun time drawing and redrawing all of the windows, over and over, every time to activate or reactive or resize any window...) I compiled hanoi with Manx, using 16 bits (code is about 9.2k). It should compile under Lattice, but that's what I said about YaBoing, too... (and it didn't) I'll appreciate any comments or suggestions! (But please don't tell me the disks look too ugly --- I know they do. Oh well! Not much you can do with RectFill(), you know.) Ali Ozer, ali@score.stanford.edu OR ali@navajo.stanford.edu ------->8 cut here and compile! you might also want to design an icon ----- /* 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 <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); } /* hanoi.c, by Ali Ozer */
dave@uwmcsd1.UUCP (02/24/87)
> ----->8 cut cut -------- > > Here's a fun little program. It solves the towers of hanoi in a workbench > > I compiled hanoi with Manx, using 16 bits (code is about 9.2k). It should > compile under Lattice, but that's what I said about YaBoing, too... (and > it didn't) > I compiled it with Lattice 3.03. It didn't like the 'min' and 'max' scattered through the code which I changed to zmin, zmax (or use your imagination). Lattice also doesn't appear to have 'output' defined as stdout, so I just converted the first fputs() to a puts(). Anyhow, it is an amusing little thing to keep your amiga busy while you're away... -- #include <std-disclaimer.h> Dave Rasmussen c/o Computing Services Division @ U of WI - Milwaukee mmm Internet: dave@csd1.milw.wisc.edu Uucp: uwvax!uwmcsd1!dave {o,o} Csnet: dave%uwmcsd1@uwm Phone: +1 (414) 963-5133 \u/
dillon@CORY.BERKELEY.EDU.UUCP (02/25/87)
The reason it didn't like min or max is because Lattice's STDIO.H #define's min and max as macros ( min(a,b) and max(a,b) ). -Matt