home *** CD-ROM | disk | FTP | other *** search
/ Club Amiga de Montreal - CAM / CAM_CD_1.iso / files / 047.lha / hanoi.c < prev    next >
C/C++ Source or Header  |  1986-11-20  |  15KB  |  443 lines

  1. /* hanoi.c                  21-Feb-1987           From: 2294.comp-sys-amiga
  2. **
  3. ** Towers of Hanoi for the Amiga, by Ali Ozer (ali@Score.Stanford.Edu).
  4. ** Originally written June '86, revised February '87. This program may be
  5. ** freely distributed. Please do not delete the author's name & picture: 8-)
  6. **
  7. ** This program will create a window within the workbench screen and start
  8. ** solving the Towers of Hanoi problem (with 5 disks). The main purpose
  9. ** of this program is to sit in the side and absorb unused cycles --- By
  10. ** default it runs at priority -20... 
  11. ** 
  12. ** This program can be started from the CLI or the Workbench. If started
  13. ** from the CLI, it will accept command line arguments to change the various
  14. ** parameters. (Give "?" as an argument to see what arguments are expected.)
  15. ** If started from the Workbench, the program will open a window and explicitly
  16. ** ask for the parameters. Hit <return> or ctrl-\ to use the default values.
  17. **
  18. ** The idea for this program came from a similar program that runs on some
  19. ** Xerox Lisp machine. (I think it was some Xerox machine, I just saw it
  20. ** run one day as I was watching a friend work on one of them...) 
  21. **
  22. ** Future improvements to this code might include (if anyone wishes to
  23. ** play with this program) making the disks look better and making them
  24. ** move pixel by pixel instead of jumping as they do now. 
  25. **
  26. ** Thanks to Lee Taran (who was a few weeks ahead of me in solving the great
  27. ** mysteries of the Rom Kernel Manual, back in May '86, when we first got the
  28. ** Amiga --- Most of the initialization code is from her), and Tom Rokicki,
  29. ** who convinced us to get an Amiga in the first place and who also posted the
  30. ** code for the OpenInfoWindow routine used near the end of this program...
  31. */
  32.  
  33. #include <exec/types.h>
  34. #include <intuition/intuition.h>
  35. #include <graphics/gfxmacros.h>
  36. #include <stdio.h>
  37.  
  38. /* Default values. Actually there are some more values in the program
  39. ** that should be #define'd here, such as the default speed, default number
  40. ** of disks, etc, but then I would have to have my strings work more
  41. ** dynamically, and, and, and... (excuses excuses)
  42. */
  43. #define MAXDISKS            25
  44. #define WIDTH              180
  45. #define HEIGHT              60
  46. #define WINDOWX    (625-WIDTH)
  47. #define WINDOWY             14
  48. #define TEXTDELAY           6L           /* Time to wait after text */
  49. #define MINWIDTHFORTEXT  (4 + 8 * 7)     /* 8: Font Width, 7: #chars */
  50.  
  51. FILE *fopen(), *input, *output, temp;    /* For fputs/fgets console I/O. */
  52.  
  53. char *fgets();
  54. void  fputs();
  55.  
  56. void *OpenLibrary(), *OpenWindow(), *FindTask(), *IntuitionBase, *GfxBase;
  57.  
  58. BYTE disks[3][MAXDISKS+1];  /* We have 3 towers with upto MAXDISKS disks  */
  59. int  topdisk[3];            /* Free location on each tower (0 = all free) */
  60. int  towerloc[4];           /* Pixel location of each tower (4th = imag.) */
  61.  
  62. /* Tons of globals... */
  63. int dx, dy, height, width, top, moveto, 
  64.     numsteps, numdisks, wbench, writetext;
  65. long movedelay, windowcolor, diskcolor, 
  66.      outlinecolor, beepcolor, textcolor, priority;
  67.  
  68. struct NewWindow WindowInfo =
  69. { WINDOWX, WINDOWY, WIDTH, HEIGHT,         /* Left, Top, Width, Height */
  70.   0, 0,                    /* Detail pen, Block pen (-1 = use screens) */
  71.   CLOSEWINDOW | NEWSIZE | REFRESHWINDOW | SIZEVERIFY,    /* IDCMPflags */
  72.   WINDOWDEPTH | WINDOWSIZING |
  73.   SMART_REFRESH | WINDOWCLOSE | WINDOWDRAG,                   /* Flags */
  74.   NULL, NULL, NULL,              /* FirstGadget, Menu Checkmark, Title */
  75.   NULL, NULL,                                        /* Screen, Bitmap */
  76.   0, 0, 640, 200,                 /* Min Width/Height Max Width/Height */
  77.   WBENCHSCREEN                                                 /* Type */
  78. };
  79.  
  80. /* Structure to hold all the window info maintained by Intuition */
  81. struct Window   *HanoiWindow;
  82. struct RastPort *HanoiRPort;
  83.  
  84. /* A macro to make dealing with undefined argv's easier... */
  85. #define ARGV(n) (argc <= n ? "" : argv[n])
  86.  
  87. main(argc, argv)
  88. int   argc;
  89. char *argv[];
  90.   int i, cnt, GetNum(), GetArg();
  91.   struct Task *thistask;
  92.  
  93.   if (wbench = (argc == 0)) {   /* Ie, started from the workbench */
  94.     if (!OpenInfoWindow()) exit (1);
  95.     fputs ("Enter parameters, <CR> or ^\\ for defaults\n", output);
  96.     numdisks    = GetNum ("Number of disks? [1..25, default 5] ", 1, 25, 5);
  97.     movedelay   = 12-GetNum ("Speed? [1..12, default 4]           ", 1, 12, 4);
  98.     windowcolor = GetNum ("Background Color? [0..3, default 3] ", 0, 3, 3);
  99.     textcolor   = GetNum ("Text Color? [0..3, default 3]       ", 0, 3, 3);
  100.     diskcolor   = GetNum ("Disk Color [0..3, default 2]        ", 0, 3, 2);
  101.     outlinecolor= GetNum ("Outline Color [0..3, default 2]     ", 0, 3, 2);
  102.     priority    = -20L;
  103.     CloseInfoWindow ();
  104.   } else {    /* Started from the CLI */
  105.     numdisks    = GetArg (ARGV(1), 1, 25, 5);
  106.     movedelay   = 12-GetArg (ARGV(2), 1, 12, 4);
  107.     windowcolor = GetArg (ARGV(3), 0, 3, 3);
  108.     textcolor   = GetArg (ARGV(4), 0, 3, 3);
  109.     diskcolor   = GetArg (ARGV(5), 0, 3, 2);
  110.     outlinecolor= GetArg (ARGV(6), 0, 3, 2);
  111.     WindowInfo.LeftEdge = GetArg (ARGV(7), 0, 640 - WIDTH,  WINDOWX);
  112.     WindowInfo.TopEdge  = GetArg (ARGV(8), 0, 200 - HEIGHT, WINDOWY);
  113.     priority    = GetArg (ARGV(9), 0, 8, 0) * 5 - 20;
  114.   };
  115.  
  116.   if (thistask = FindTask (NULL)) SetTaskPri (thistask, (long)priority);
  117.  
  118.   numsteps             = (numdisks * 2) + 4;
  119.   writetext            = (windowcolor != textcolor);
  120.   WindowInfo.DetailPen = windowcolor;
  121.   WindowInfo.BlockPen  = windowcolor;
  122.   WindowInfo.MinWidth  = numsteps * 3;
  123.   WindowInfo.MinHeight = numdisks * 2 + 4 + writetext * 10;
  124.   WindowInfo.Height   += (writetext * 10);
  125.   /* Make sure beepcolor is different than disk or window color */
  126.   if ((beepcolor = ((diskcolor - 1) & 3)) == windowcolor)
  127.      beepcolor = (beepcolor - 1) & 3;
  128.  
  129.   OpenLibraries();  
  130.   SetupEnvironment();  
  131.   ReCalculateParameters();
  132.   InitializeTowers();  
  133.   ClearWindow();
  134.   DrawAllDisks(diskcolor);
  135.  
  136.   for (cnt = 0; cnt < 10; cnt++) {
  137.     CheckForMessages ();  Delay (20L);
  138.   }
  139.   while (1) 
  140.     for (i = 0; i < 3; i++) {
  141.       MoveDisks(i, (i+1)%3, (i+2)%3, numdisks);  
  142.       DrawAllDisks (beepcolor); Delay (6L); DrawAllDisks (diskcolor);
  143.       for (cnt = 0; cnt < numdisks * 3; cnt++) {
  144.         CheckForMessages ();  Delay (20L);
  145.       }
  146.     }
  147. }
  148.  
  149. /* Called after resizing to determine new parameters...
  150. */
  151. ReCalculateParameters()
  152. {
  153.   int xstart, i;
  154.  
  155.   top    = (writetext ? 9 : 0);
  156.   height = HanoiWindow->Height;
  157.   width  = HanoiWindow->Width;
  158.   dx     = width / (3 * numsteps);
  159.   dy     = (height-top) / (numdisks + 2);
  160.   xstart = (width - dx * 3 * numsteps) / 2;
  161.   for (i = 0; i < 4; i++) towerloc[i] = xstart + i * numsteps * dx;
  162.   moveto = top + dy;
  163. }
  164.  
  165. DrawDiskAtLoc (disk, tower, location, diskcolor, outlinecolor)
  166. int  disk, tower, location;
  167. long diskcolor, outlinecolor;
  168. {
  169.   if (disk) {
  170.     SetAPen (HanoiRPort, diskcolor);
  171.     SetOPen (HanoiRPort, outlinecolor);
  172.     RectFill (HanoiRPort, 
  173.               (long)(towerloc[tower] + (numdisks - disk + 1) * dx),
  174.               (long)(height - (location + 1) * dy),
  175.               (long)(towerloc[tower+1] - 1 - (numdisks - disk + 1) * dx),
  176.               (long)(height - (location * dy) - 2)); 
  177.   }
  178. }
  179.             
  180. DrawAllDisks(color)
  181. long color;
  182. {
  183.   int tower, location;
  184.  
  185.   for (tower = 0; tower < 3; tower++) 
  186.     for (location = 0; location < numdisks; location++)
  187.       DrawDiskAtLoc (disks [tower][location], tower,
  188.                      location, color, outlinecolor);
  189. }
  190.   
  191.  
  192. /* InitializeTowers will clear towers 1 and 2 and fill up tower 0. 
  193. ** To start off, we have disk[0][0] = largest disk, disk[0][1] = next
  194. */
  195. InitializeTowers()
  196. {
  197.   int i;
  198.   for (i=0; i<numdisks; i++) {
  199.     disks[0][i] = numdisks-i; 
  200.     disks[1][i] = 0;
  201.     disks[2][i] = 0;
  202.   }
  203.   topdisk[0] = numdisks;
  204.   topdisk[1] = 0;
  205.   topdisk[2] = 0;
  206. }
  207.  
  208. /* Write over *everything* in sight, including the borders and the title...
  209. */
  210. ClearWindow ()
  211. {
  212.   SetOPen  (HanoiRPort, windowcolor);
  213.   SetAPen  (HanoiRPort, windowcolor);
  214.   RectFill (HanoiRPort, 0L, 0L, (long)(width-1), (long)(height-1));
  215. }
  216.  
  217. static char titlestring[8] =  "X->X   ";
  218.  
  219. /* ShowMoveInfo will print out the current move being made. Note that
  220. ** this routine is NOT CALLED at all if the user specifies the text color
  221. ** to be the same as the window color... This routine does slow down the
  222. ** program, but it makes it a more educational tool...
  223. */
  224. ShowMoveInfo (fromtower, totower, ndisks)
  225. int fromtower, totower, ndisks;
  226. {
  227.   if (ndisks > 9) titlestring[5] = '0' + ndisks / 10;
  228.   else titlestring[5] = ' ';
  229.   titlestring[6] = '0' + ndisks % 10;
  230.   titlestring[0] = 'A' + fromtower;
  231.   titlestring[3] = 'A' + totower;
  232.  
  233.   SetAPen (HanoiRPort, textcolor);
  234.   SetDrMd (HanoiRPort, JAM2);
  235.   Move (HanoiRPort, 1L, 8L);
  236.   Text (HanoiRPort, "       ", 7L);
  237.   SetDrMd (HanoiRPort, JAM1);
  238.   Move (HanoiRPort, 1L, 8L);
  239.   Text (HanoiRPort, titlestring, 7L);
  240. }  
  241.       
  242.  
  243. /* This is the recursive Hanoi function! Looks simple, no?
  244. */
  245. MoveDisks (fromtower, totower, sparetower, ndisks)
  246. int fromtower, totower, sparetower, ndisks;
  247. {
  248.   if (writetext && width > MINWIDTHFORTEXT) {
  249.     ShowMoveInfo (fromtower, totower, ndisks);
  250.     Delay (TEXTDELAY * movedelay);
  251.     CheckForMessages ();
  252.   }
  253.  
  254.   if (ndisks == 1)  /* Base case */
  255.     MoveOneDisk (fromtower, totower);
  256.   else {
  257.     MoveDisks   (fromtower, sparetower, totower, ndisks-1);
  258.     MoveOneDisk (fromtower, totower);
  259.     MoveDisks   (sparetower, totower, fromtower, ndisks-1);
  260.   }
  261. }
  262.  
  263. /* This moves (graphically) the top disk from fromtower to totower.
  264. */
  265. MoveOneDisk (fromtower, totower)
  266. int fromtower, totower;
  267. {
  268.   int cnt;
  269.   int disk = disks[fromtower][topdisk[fromtower]-1];
  270.  
  271.   if (writetext && width > MINWIDTHFORTEXT) 
  272.     ShowMoveInfo (fromtower, totower, 1);
  273.  
  274.   for (cnt = topdisk[fromtower]; cnt <= numdisks; cnt++) {
  275.     DrawDiskAtLoc (disk, fromtower, cnt-1, windowcolor, windowcolor);
  276.     DrawDiskAtLoc (disk, fromtower, cnt, diskcolor, outlinecolor);
  277.     Delay (movedelay);
  278.   }
  279.  
  280.   topdisk[fromtower]--;
  281.   disks[fromtower][topdisk[fromtower]] = 0;
  282.   CheckForMessages ();
  283.  
  284.   if (fromtower < totower) 
  285.     for (cnt = fromtower+1; cnt <= totower; cnt++) {
  286.       DrawDiskAtLoc (disk, cnt-1, numdisks, windowcolor, windowcolor);
  287.       DrawDiskAtLoc (disk, cnt, numdisks, diskcolor, outlinecolor);
  288.       Delay (movedelay);
  289.     }
  290.   else 
  291.     for (cnt = fromtower-1; cnt >= totower; cnt--) {
  292.       DrawDiskAtLoc (disk, cnt+1, numdisks, windowcolor, windowcolor);
  293.       DrawDiskAtLoc (disk, cnt, numdisks, diskcolor, outlinecolor);
  294.       Delay (movedelay);
  295.     };
  296.  
  297.   CheckForMessages ();
  298.  
  299.   for (cnt = numdisks-1; cnt >= topdisk[totower]; cnt--) {
  300.     DrawDiskAtLoc (disk, totower, cnt+1, windowcolor, windowcolor);
  301.     DrawDiskAtLoc (disk, totower, cnt, diskcolor, outlinecolor);
  302.     Delay (movedelay);
  303.   }
  304.  
  305.   disks[totower][topdisk[totower]] = disk;
  306.   topdisk[totower]++;
  307.  
  308.   CheckForMessages ();  
  309. }
  310.  
  311. CheckForMessages ()
  312. {
  313.   struct IntuiMessage *message, *GetMsg();
  314.   ULONG  class;  /* What kind of message? */
  315.  
  316.   /* Try to get a message. If there is one, process it. */  
  317.  
  318.   while (message = GetMsg(HanoiWindow->UserPort)) {
  319.     class = message->Class;
  320.     ReplyMsg(message);
  321.     switch (class) {
  322.     case SIZEVERIFY    : Wait (1L << HanoiWindow->UserPort->mp_SigBit);
  323.                  break;
  324.         case NEWSIZE       : ReCalculateParameters();  /* Fall through */
  325.         case REFRESHWINDOW : ClearWindow(); 
  326.                              DrawAllDisks(diskcolor);
  327.                    break;
  328.         case CLOSEWINDOW   : CloseThings(0);  /* CloseThings exits */
  329.         default            : CloseThings(1);  /* CloseThings exits */
  330.     }
  331.   }  
  332. }
  333.  
  334. OpenLibraries ()
  335. {
  336.   if (!(IntuitionBase = OpenLibrary("intuition.library",0L))) 
  337.     Panic("No intuition library\n");
  338.   if (!(GfxBase = OpenLibrary("graphics.library",0L))) 
  339.     Panic("No graphics library\n");
  340. }  
  341.  
  342. /* Initializes the Hanoi window and sets up the HanoiRPort, used often. */
  343. SetupEnvironment()
  344. {
  345.   if (!(HanoiWindow = OpenWindow(&WindowInfo))) Panic ("Can't open window\n");
  346.   HanoiRPort = HanoiWindow->RPort;
  347.   SetBPen (HanoiRPort, windowcolor);
  348. }
  349.  
  350. /* Prints out an error message about what went wrong and exits gracefully */
  351. Panic(errormsg)
  352. char *errormsg;
  353. {
  354.   if (wbench) DisplayBeep (NULL);   /* Simply flash the display... */
  355.   else puts (errormsg);
  356.   CloseThings(1);
  357. }
  358.  
  359. /* Closes the Libraries that have been opened and frees up all memory */ 
  360. CloseThings(error_code)
  361. int error_code;
  362.   if (HanoiWindow)   CloseWindow(HanoiWindow);
  363.   if (GfxBase)       CloseLibrary(GfxBase);
  364.   if (IntuitionBase) CloseLibrary(IntuitionBase);
  365.   exit (error_code);
  366. }
  367.  
  368. int GetNum (prompt, min, max, def)
  369. int min, max, def;
  370. char *prompt;
  371. {
  372.   int   result, cnt;
  373.   char  resp[80];
  374.  
  375.   while (1) {
  376.     fputs (prompt, output); fflush (output);
  377.     fgets (resp, 80, input);
  378.     cnt = 0;
  379.     while (resp[cnt] == ' ') cnt++;
  380.     if (resp[cnt] == '\0' || resp[cnt] == '\n') return (def);
  381.     result = 0;
  382.     while (resp[cnt] != '\0' && resp[cnt] != '\n' &&
  383.            result <= max && result != -1) {
  384.       if (resp[cnt] < '0' || resp[cnt] > '9') result = -1;
  385.       else result = result * 10 + resp[cnt++] - '0';
  386.     };
  387.     if (result >= min && result <= max) return (result);
  388.     if (result == -1) fputs ("Illegal integer, please try again.\n", output);
  389.     else fputs ("Value out of range, please try again.\n", output);
  390.   }
  391. }
  392.  
  393. /* Opens a console window for fputs/fgets compatible I/O. Thanks to Tom
  394. ** Rokicki (from whose posting the last 4 lines of this routine come...).
  395. */
  396. int OpenInfoWindow ()
  397. {
  398.    static char consp[] = "CON:30/30/400/80/Hanoi by Ali Ozer";
  399.    consp[30] = 214; /* Guess what this does? Run the program and see */
  400.  
  401.    if ((output = fopen (consp, "w+")) == NULL) return (0);     
  402.    input = &temp;
  403.    *input = *output;
  404.    return (1);
  405. }
  406.  
  407. CloseInfoWindow ()
  408. {
  409.   fclose (input);
  410.   fclose (output);
  411. }
  412.  
  413. /* Gets a positive integer from argstr. If argstr is empty or "-", return
  414. ** defaultnum. Error if argstr is an invalid number or is not in range.
  415. */
  416. int GetArg (argstr, min, max, def)
  417. int min, max, def;
  418. char *argstr;
  419. {
  420.   int result = 0;
  421.   if (strlen(argstr) == 0 || !strcmp(argstr,"-")) return (def);
  422.   while (*argstr != '\0' && result <= max) {
  423.     if (*argstr < '0' || *argstr > '9') IllegalArgs();
  424.     result = result * 10 + *argstr++ - '0';
  425.   };
  426.   if (result >= min && result <= max) return (result);
  427.   IllegalArgs ();
  428. }
  429.  
  430. /* Prints usage line and causes program to terminate. Considering we only
  431. ** call GetArg once we know we were called from CLI, we can safely do puts.
  432. */
  433. IllegalArgs ()
  434. {
  435.   puts ("\nUsage:\nHanoi NDisks Speed BGColor TxtColor DskColor OutLineColor X Y Priority");
  436.   puts (" NDisks 1..25, Speed 0..12, Colors 0..3 (WorkBench colors)");
  437.   puts (" Priority 0..8 (Corresponding to -20..+20)");
  438.   puts (" All arguments are optional. Use - to get a default value.\n");
  439.   CloseThings (1);
  440. }
  441.