home *** CD-ROM | disk | FTP | other *** search
/ GEMini Atari / GEMini_Atari_CD-ROM_Walnut_Creek_December_1993.iso / files / gnu / crssrc16 / puzzle15.c < prev    next >
C/C++ Source or Header  |  1993-07-29  |  23KB  |  905 lines

  1. /*
  2.  * This program simulates a simple toy with many many variations.
  3.  *
  4.  * Compile with -DARROWKEYS if your curses library understands about
  5.  * arrow keys (and your keyboards have them).
  6.  * Compile with -DNOSIGNAL if your machine does not support the signal
  7.  * function to catch ctrl-C.
  8.  * Compile with "-DDIECHAR='<somechar>'" if you would like the program
  9.  * to exit gracefully when <somechar> is typed. All the quotes in and
  10.  * around this argument are necessary. The double quotes prevent the
  11.  * shell from stripping the single quotes. Instead of '<somechar' you
  12.  * can also type the ascii value of the key. If you do so, the double
  13.  * quotes are not needed.
  14.  *
  15.  * The copyright message is there to prevent uncontrolled spreading of
  16.  * a zillion different versions.
  17.  *
  18.  * You can mail your improvements and suggestions to me. I may include
  19.  * them in a future version of the program.
  20.  *
  21.  * I do not guarantee the fitness of this program for any purpose.
  22.  * USE IT AT YOUR OWN RISK.
  23.  *
  24.  * Peter Knoppers - knop@duteca.UUCP
  25.  * Bilderdijkhof 59
  26.  * 2624 ZG  Delft
  27.  * The Netherlands
  28.  *
  29.  *
  30.  * The following lines are put in the compiled program by the C compiler
  31.  * where they can be found by programs such as (Bsd) strings.
  32.  */
  33. char   *this_is = "puzzle15 V2.0 Feb 24 1989";
  34. char   *author0 = "(C) Copyright 1985, 1988, 1989 Peter Knoppers";
  35. char   *author1 = "Arrow keys added by Paul Lew 1988";
  36. char   *author2 = "Clock added by Bo Kullmar 1988";
  37. char   *author3 = "Select-tile method suggested by Larry Hastings 1988";
  38. char   *authorx = "Additions merged by Peter Knoppers";
  39. char   *release1 = "Permission to use and redistribute unmodified ";
  40. char   *release2 = "copies with source is granted to everyone";
  41. char   *release3 = "Distribution of modified copies is NOT allowed";
  42.  
  43. #include <curses.h>
  44. #ifndef NOSIGNAL
  45. #include <signal.h>
  46. #endif
  47. #include <ctype.h>
  48.  
  49. #ifndef DIECHAR
  50. #define DIECHAR 999        /* any non-ASCII value will do */
  51. #endif
  52. #define DEFSIZE          4    /* default size of board */
  53. #define BLANK          0
  54. #define UNSPECIFIED      (-1)
  55. /* classification of arguments */
  56. #define MOVEMETHOD      1
  57. #define CURSORMETHOD      2
  58. #define FACEMETHOD      3
  59. /* magic values are required to be different, no other restrictions apply */
  60. /* magic values for Movemethods */
  61. #define MOVEBLANK      4
  62. #define MOVETILE      5
  63. #define SELECTTILE      6
  64. /* magic values for Cursormethods */
  65. #define VIKEYS          7
  66. #ifdef ARROWKEYS
  67. #define ARROWK          8
  68. #endif
  69. #define NUMBERKEYS      9
  70. #define FACEKEYS     10
  71. #define KEYS         11
  72. /* magic values for Facemethods */
  73. #define NUMBERFACES     12
  74. #define ALPHAFACES     13
  75. #define NUMBERALPHAFACES 14
  76. #define ALPHANUMBERFACES 15
  77. #define FACES         16
  78.  
  79. unsigned long   Time_start;
  80. int     Movecnt = 0;
  81.  
  82. #ifdef __STDC__
  83. void   *malloc (unsigned);        /* to satisfy lint */
  84. long    time (long *);
  85. #else
  86. char   *malloc ();        /* to satisfy lint */
  87. long    time ();
  88. #endif
  89.  
  90. int     Sizex = UNSPECIFIED;    /* x-size of the board */
  91. int     Sizey = UNSPECIFIED;    /* y-size of the board */
  92. int     Size2;            /* total surface of the board */
  93. int     Movemethod = UNSPECIFIED;
  94. int     Cursormethod = UNSPECIFIED;
  95. int     Facemethod = UNSPECIFIED;
  96. char   *Movename;        /* name of the selected Movemethod */
  97. char   *Cursorname;        /* name of the selected Cursormethod */
  98. char   *Cursorkeys;        /* the user-defined Cursorkeys */
  99. char   *Facename;        /* name of the selected Facemethod */
  100. char   *Facechars;        /* the user-defined tile-faces */
  101. int     Up;            /* key-codes for cursor movements */
  102. int     Down;
  103. int     Left;
  104. int     Right;
  105. int     Newpos;            /* new position for empty field */
  106. char   *Myname;            /* name used to invoke this program */
  107. /*
  108.  * All possible keyword arguments are listed in the Argopts table.
  109.  * This is done in order to
  110.  * - ensure that every keyword appears at only one place in the program
  111.  *   (this makes the program easier to modify/maintain)
  112.  * - put most of the knowledge about incompatible arguments in one place
  113.  *   instead of scattering it all over the program.
  114.  * - simplify the argument parser
  115.  *   (I haven't been completely succesfull in this respect...)
  116.  */
  117. struct argtablemember
  118. {
  119.     char   *a_name;        /* what the user types */
  120.     int     a_type;        /* what kind of spec. it is */
  121.     int     a_magic;        /* magic value */
  122. } Argopts[] =
  123. {
  124.     {
  125.         "moveblank", MOVEMETHOD, MOVEBLANK
  126.     },
  127.     {
  128.     "movetile", MOVEMETHOD, MOVETILE
  129.     },
  130.     {                /* SELECTTILE must be before FACEKEYS */
  131.     "selecttile", MOVEMETHOD, SELECTTILE
  132.     },
  133.     {
  134.     "vikeys", CURSORMETHOD, VIKEYS
  135.     },
  136. #ifdef ARROWKEYS
  137.     {
  138.     "arrowkeys", CURSORMETHOD, ARROWK
  139.     },
  140. #endif
  141.     {
  142.     "numberkeys", CURSORMETHOD, NUMBERKEYS
  143.     },
  144.     {
  145.     "facekeys", CURSORMETHOD, FACEKEYS
  146.     },
  147.     {
  148.     "keys", CURSORMETHOD, KEYS
  149.     },
  150.     {
  151.     "numberfaces", FACEMETHOD, NUMBERFACES
  152.     },
  153.     {
  154.     "alphafaces", FACEMETHOD, ALPHAFACES
  155.     },
  156.     {
  157.     "numberalphafaces", FACEMETHOD, NUMBERALPHAFACES
  158.     },
  159.     {
  160.     "alphanumberfaces", FACEMETHOD, ALPHANUMBERFACES
  161.     },
  162.     {
  163.     "faces", FACEMETHOD, FACES
  164.     },
  165.     {
  166.     (char *) 0, 0, 0
  167.     }
  168. };
  169.  
  170. die ()                /* nice exit on ctrl-C */
  171. {
  172. #ifndef NOSIGNAL
  173.     signal (SIGINT, SIG_IGN);    /* ignore ctrl-C */
  174. #endif
  175.     mvprintw (LINES - 1, 0, "Goodbye. ");
  176.     clrtoeol ();
  177.     refresh ();
  178.     endwin ();            /* shutdown curses, restore ttymode */
  179.     exit (0);
  180. }
  181.  
  182. main (argc, argv)        /* scan the arguments, call game() */
  183. char  **argv;
  184. {
  185.     struct argtablemember  *atmp;/* to walk Argopts table */
  186.     int     swap;        /* to revers directions */
  187.  
  188.     Myname = *argv;        /* save this for usage () */
  189.  /* 
  190.   * scan the arguments
  191.   */
  192.     while (*++argv != ((char *) 0))
  193.     {                /* walk argument list */
  194.     for (atmp = Argopts; atmp -> a_name != (char *) 0; atmp++)
  195.     {            /* scan the keyword list */
  196.         if (strcmp (*argv, atmp -> a_name) == 0)
  197.         {            /* found keyword */
  198.         switch (atmp -> a_type)
  199.         {
  200.             case MOVEMETHOD: 
  201.             if (Movemethod != UNSPECIFIED)
  202.                 conflict (Movename, atmp -> a_name);
  203.             Movename = atmp -> a_name;
  204.             Movemethod = atmp -> a_magic;
  205.             break;
  206.             case CURSORMETHOD: 
  207.             if (Cursormethod != UNSPECIFIED)
  208.                 conflict (Cursorname, atmp -> a_name);
  209.             Cursorname = atmp -> a_name;
  210.             Cursormethod = atmp -> a_magic;
  211.             if (atmp -> a_magic == KEYS)
  212.             {
  213.                 if ((Cursorkeys = *++argv) == (char *) 0)
  214.                 novalue (*--argv);/* never returns */
  215.                 if (strlen (Cursorkeys) != 4)
  216.                 {
  217.                 printf ("%s need 4 cursorkeys\n",
  218.                     Cursorname);
  219.                 usage ();
  220.                 }
  221.             }
  222.             break;
  223.             case FACEMETHOD: 
  224.             if (Facemethod != UNSPECIFIED)
  225.                 conflict (Facename, atmp -> a_name);
  226.             Facename = atmp -> a_name;
  227.             Facemethod = atmp -> a_magic;
  228.             if (atmp -> a_magic == FACES)
  229.                 if ((Facechars = *++argv) == (char *) 0)
  230.                 novalue (*--argv);
  231.             break;
  232.             default: 
  233.             printf ("aargh: bad switch (atmp -> a_type = %d)\n",
  234.                 atmp -> a_type);
  235.             exit (1);
  236.         }
  237.         break;
  238.         }
  239.     }
  240.     if (atmp -> a_name == (char *) 0)/* not a keyword */
  241.         if (isdigit (**argv) && (Sizex == UNSPECIFIED))
  242.         {            /* it's a boardsize specification */
  243.         if (sscanf (*argv, "%dx%d", &Sizex, &Sizey) != 2)
  244.             if (sscanf (*argv, "%d", &Sizex) == 1)
  245.             Sizey = Sizex;
  246.             else
  247.             {
  248.             printf ("bad argument %s\n", *argv);
  249.             usage ();
  250.             }
  251.         }
  252.         else        /* it's garbage */
  253.         {
  254.         printf ("bad argument %s\n", *argv);
  255.         usage ();
  256.         }
  257.     }
  258.  
  259.  /* 
  260.   * insert default values
  261.   */
  262.     if (Sizex == UNSPECIFIED)
  263.     Sizex = Sizey = DEFSIZE;
  264.  
  265.     if (Cursormethod == UNSPECIFIED)
  266.     {                /* find first CURSORMETHOD in Argopts */
  267.     for (atmp = Argopts; atmp -> a_name != (char *) 0; atmp++)
  268.         if (atmp -> a_type == CURSORMETHOD)
  269.         break;
  270.     if (atmp -> a_name == (char *) 0)
  271.     {
  272.         printf ("aargh: can't find default Cursormethod\n");
  273.         exit (1);
  274.     }
  275.     Cursormethod = atmp -> a_magic;
  276.     Cursorname = atmp -> a_name;
  277.     }
  278.  
  279.     if (Facemethod == UNSPECIFIED)
  280.     {                /* find first FACEMETHOD in Argopts */
  281.     for (atmp = Argopts; atmp -> a_name != (char *) 0; atmp++)
  282.         if (atmp -> a_type == FACEMETHOD)
  283.         break;
  284.     if (atmp -> a_name == (char *) 0)
  285.     {
  286.         printf ("aargh: can't find default Facemethod\n");
  287.         exit (1);
  288.     }
  289.     Facemethod = atmp -> a_magic;
  290.     Facename = atmp -> a_name;
  291.     }
  292.  
  293.     if (Movemethod == UNSPECIFIED)
  294.     if (Cursormethod == FACEKEYS)
  295.         Movemethod = SELECTTILE;/* by implication */
  296.     else
  297.     {            /* find first MOVEMETHOD in Argopts */
  298.         for (atmp = Argopts; atmp -> a_name != (char *) 0; atmp++)
  299.         if (atmp -> a_type == MOVEMETHOD)
  300.             break;
  301.         if (atmp -> a_name == (char *) 0)
  302.         {
  303.         printf ("aargh: can't find default Movemethod\n");
  304.         exit (1);
  305.         }
  306.         Movemethod = atmp -> a_magic;
  307.         Movename = atmp -> a_name;
  308.     }
  309.  
  310.     if ((Cursormethod == FACEKEYS) && (Movemethod != SELECTTILE))
  311.     conflict (Cursorname, Movename);/* does not return */
  312.     switch (Cursormethod)
  313.     {
  314.     case VIKEYS: 
  315.         Up = 'k';
  316.         Down = 'j';
  317.         Left = 'h';
  318.         Right = 'l';
  319.         break;
  320. #ifdef ARROWKEYS
  321.     case ARROWK: 
  322.         Up = KEY_UP;
  323.         Down = KEY_DOWN;
  324.         Left = KEY_LEFT;
  325.         Right = KEY_RIGHT;
  326.         break;
  327. #endif
  328.     case NUMBERKEYS: 
  329.         Up = '8';
  330.         Down = '2';
  331.         Left = '4';
  332.         Right = '6';
  333.         break;
  334.     case FACEKEYS: 
  335.         Up = 1000;        /* values must not correspond to any ASCII */
  336.         Down = 1001;    /*   value, otherwise no restrictions */
  337.         Left = 1002;
  338.         Right = 1003;
  339.         break;
  340.     case KEYS: 
  341.         Up = Cursorkeys[0];
  342.         Down = Cursorkeys[1];
  343.         Left = Cursorkeys[2];
  344.         Right = Cursorkeys[3];
  345.         break;
  346.     default: 
  347.         printf ("aargh: bad switch (Cursormethod = %d)\n",
  348.             Cursormethod);
  349.         exit (1);
  350.     }
  351.  
  352.     if (Movemethod == MOVETILE)
  353.     {                /* revers cursor directions */
  354.     swap = Up;
  355.     Up = Down;
  356.     Down = swap;
  357.     swap = Left;
  358.     Left = Right;
  359.     Right = swap;
  360.     }
  361.  
  362.     Size2 = Sizex * Sizey;    /* surface area of the board */
  363.  
  364.     switch (Facemethod)
  365.     {
  366.     case NUMBERFACES: 
  367.         if (Size2 <= 10)    /* tiles can be labeled with 1 char */
  368.         {
  369.         Facechars = "123456789";
  370.         Facemethod = NUMBERALPHAFACES;
  371.         }
  372.         break;
  373.     case ALPHAFACES: 
  374.         Facechars = "abcdefghijklmnopqrstuvwxyz";
  375.         break;
  376.     case NUMBERALPHAFACES: 
  377.         Facechars = "0123456789abcdefghijklmnopqrstuvwxyz";
  378.         break;
  379.     case ALPHANUMBERFACES: 
  380.         Facechars = "abcdefghijklmnopqrstuvwxyz0123456789";
  381.         break;
  382.     }
  383.  
  384.     if ((Size2 - 1) > ((Facemethod == NUMBERFACES) ? 99 :
  385.         strlen (Facechars)))
  386.     {
  387.     printf ("sorry, too many tiles - not enough labels\n");
  388.     exit (0);
  389.     }
  390.     if ((Sizex < 2) || (Sizey < 2))
  391.     {
  392.     printf ("sorry, board is too small for a non-trivial game\n");
  393.     exit (0);
  394.     }
  395.  
  396.  /* 
  397.   * Initialize the curses screen handling functions
  398.   */
  399. #ifndef NOSIGNAL
  400.     signal (SIGINT, SIG_IGN);    /* protect this critical section */
  401. #endif
  402.     initscr ();
  403.     crmode ();
  404.     noecho ();
  405. #ifdef ARROWKEYS
  406.     keypad (stdscr, TRUE);
  407. #endif
  408. #ifndef NOSIGNAL
  409.     signal (SIGINT, die);    /* die() knows how to restore ttymode */
  410. #endif
  411.  /* 
  412.   * From now on use die() to exit the program, otherwise the
  413.   * ttymode will not be restored.
  414.   *
  415.   * Now that curses has been initialized we can check whether the board
  416.   * fits on the screen.
  417.   */
  418.     if (Sizex > (COLS - 1) / 5)    /* COLS works only after initscr() */
  419.     {
  420.     mvprintw (LINES - 2, 0,
  421.         "sorry, your screen is not wide enough\n");
  422.     die ();
  423.     }
  424.     if (Sizey > (LINES - 4) / 2)
  425.     {
  426.     mvprintw (LINES - 2, 0,
  427.         "sorry, your screen is not tall enough\n");
  428.     die ();
  429.     }
  430.  
  431.     game ();            /* play the game */
  432.  
  433.     mvprintw (LINES - 2, 0,
  434.         "You've reached the solution in %d moves.\n", Movecnt);
  435.     die ();            /* restore ttymode and exit */
  436. }
  437.  
  438. usage ()
  439. {
  440.     int     curtype = UNSPECIFIED;
  441.     int     prevtype = UNSPECIFIED;
  442.     char   *selecttilename = (char *) 0;
  443.     struct argtablemember  *atmp;/* to walk Argopts table */
  444.  
  445.     printf ("usage: %s [<width[x<height>]] %s",
  446.         Myname, "[movemethod] [cursormethod] [facemethod]\n");
  447.     for (atmp = Argopts; atmp -> a_name != (char *) 0; atmp++)
  448.     {
  449.     if (atmp -> a_magic == SELECTTILE)
  450.         selecttilename = atmp -> a_name;
  451.     if (atmp -> a_type != curtype)
  452.     {
  453.         curtype = atmp -> a_type;
  454.         if (curtype == MOVEMETHOD)
  455.         printf ("possible movemethods:\n");
  456.         if (curtype == CURSORMETHOD)
  457.         printf ("possible cursormethods:\n");
  458.         if (curtype == FACEMETHOD)
  459.         printf ("possible facemethods:\n");
  460.     }
  461.     switch (atmp -> a_magic)
  462.     {
  463.         case KEYS: 
  464.         printf ("\t\t%s <up><down><left><right>", atmp -> a_name);
  465.         break;
  466.         case FACES: 
  467.         printf ("\t\t%s <facecharacters>", atmp -> a_name);
  468.         break;
  469.         case FACEKEYS: 
  470.         if (selecttilename == (char *) 0)
  471.         {
  472.             printf ("aargh: can't find string for SELECTTILE\n");
  473.             exit (1);
  474.         }
  475.         printf ("\t\t%-20.20s (implies movemethod %s)",
  476.             atmp -> a_name, selecttilename);
  477.         break;
  478.         default: 
  479.         printf ("\t\t%-20.20s", atmp -> a_name);
  480.         break;
  481.     }
  482.     if (curtype != prevtype)
  483.         printf (" (this is default)\n");
  484.     else
  485.         printf ("\n");
  486.     prevtype = curtype;
  487.     }
  488.     exit (0);
  489. }
  490.  
  491. conflict (word1, word2)
  492. char   *word1;
  493. char   *word2;
  494. {
  495.     printf ("You may not specify both %s and %s\n", word1, word2);
  496.     usage ();
  497. }
  498.  
  499. novalue (word)
  500. char   *word;
  501. {
  502.     printf ("%s requires an argument\n", word);
  503.     usage ();
  504. }
  505.  
  506. game ()                /* initialize board, execute moves */
  507. {
  508.     register int    i, j;    /* generally used indices and counters */
  509.     int    *board;        /* pointer to malloc-ed board */
  510.     int     empty;        /* position of empty field */
  511.     int     swap;        /* used to swap two tiles */
  512.     int     nswap = 0;        /* to determine reachability */
  513.     int     steps;        /* number of tiles that move */
  514.     int     unchanged = 0;    /* used to indicate that board has changed */
  515.     int     cursorx;        /* to save cursorposition while */
  516.     int     cursory;        /*   printing error messages */
  517.     int     m;            /* move, first character / direction */
  518.     int     m2;            /* second character of move */
  519.  /* 
  520.   * Set up the board and shuffle the tiles
  521.   */
  522.     board = (int *) malloc ((unsigned) (Size2 * sizeof (int)));
  523.     if (board == NULL)
  524.     {
  525.     printf ("aargh: malloc failed\n");
  526.     die ();
  527.     }
  528.     srand ((int) time ((long) 0));/* initialize random number generator */
  529.     for (i = 1; i < Size2; i++)    /* put tiles on the board in their */
  530.     board[i - 1] = i;    /*   final positions */
  531.     for (i = Size2 - 2; i > 0; i--)/* permutate tiles */
  532.     {
  533.     j = rand () % i;
  534.     swap = board[i];
  535.     board[i] = board[j];
  536.     board[j] = swap;
  537.     }
  538.  /* 
  539.   * Check that final position can be reached from current permutation
  540.   */
  541.     for (i = 0; i < Size2 - 1; i++)
  542.     for (j = 0; j < i; j++)
  543.         if (board[j] > board[i])
  544.         nswap++;
  545.     if ((nswap % 2) != 0)    /* this position is unreachable */
  546.     {                /*   swap two adjacent tiles */
  547.     swap = board[1];
  548.     board[1] = board[0];
  549.     board[0] = swap;
  550.     }
  551.     empty = Size2 - 1;        /* empty field starts in lower right */
  552.     board[empty] = BLANK;    /*   corner */
  553.     Newpos = empty;
  554.     Time_start = time ((long *) 0);/* start the clock */
  555.  
  556.     while (1)            /* until final position is reached */
  557.     {
  558.     if (unchanged == 0)    /* the board must be (re-)printed */
  559.     {
  560.         printboard (board);    /* also puts cursor at Newpos */
  561.         unchanged++;    /* the board on the screen is up to date */
  562.     /* 
  563.      * Check if final position is reached
  564.      */
  565.         for (i = 0; i < Size2 - 1; i++)
  566.         if (board[i] != i + 1)
  567.             break;    /* final position is not yet reached */
  568.         if (i == Size2 - 1)    /* all tiles are in final positions */
  569.         return;        /* game ends */
  570.     }
  571.     /* 
  572.      * Let the user make a move
  573.      */
  574.     m = getch ();
  575.     if (m == DIECHAR)
  576.         die ();
  577.     if (Movemethod == SELECTTILE)
  578.     {
  579.         if (Cursormethod == FACEKEYS)
  580.         {
  581.         if (Facemethod == NUMBERFACES)
  582.         {
  583.             if (!isdigit (m))
  584.             {
  585.             getyx (stdscr, cursory, cursorx);
  586.             mvprintw (LINES - 1, 0,
  587.                 "use one of the keys");
  588.             for (i = 0; (i <= Size2) && (i < 10); i++)
  589.                 printw (" %d", i);
  590.             clrtoeol ();
  591.             move (cursory, cursorx);
  592.             refresh ();
  593.             continue;
  594.             }
  595.             if ((m - '0') > (Size2 / 10))
  596.             m -= '0';
  597.             else    /* we need a second digit */
  598.             {
  599.             m = (m - '0') * 10;
  600.             m2 = getch ();
  601.             if (m == DIECHAR)
  602.                 die ();
  603.             if ((!isdigit (m2)) || (m + m2 - '0' >= Size2))
  604.             {
  605.                 getyx (stdscr, cursory, cursorx);
  606.                 mvprintw (LINES - 1, 0,
  607.                     "use one of the keys");
  608.                 for (i = 0; (i < Size2 % 10) && (i + m < Size2);
  609.                     i++)
  610.                 printw (" %d", i);
  611.                 clrtoeol ();
  612.                 move (cursory, cursorx);
  613.                 refresh ();
  614.                 continue;
  615.             }
  616.             m += m2 - '0';
  617.             }
  618.         /* 
  619.          * find out where this tile is on the board
  620.          */
  621.             for (Newpos = 0; Newpos < Size2; Newpos++)
  622.             if (board[Newpos] == m)
  623.                 break;/* found tile */
  624.             if (Newpos == Size2)/* no tile with face m */
  625.             {
  626.             mvprintw (LINES - 2, 0,
  627.                 "aargh: can't find tile %d on board\n", m);
  628.             die ();
  629.             }
  630.         }
  631.         else
  632.         {
  633.         /* 
  634.          * Facemethod != NUMBERFACES
  635.          * This means that a single keystroke identifies the
  636.          * tile that is to be moved.
  637.          */
  638.             for (Newpos = 0; Newpos < Size2; Newpos++)
  639.             if (board[Newpos] > 0)
  640.                 if (Facechars[board[Newpos] - 1] == m)
  641.                 break;/* found tile */
  642.             if (Newpos == Size2)
  643.             {
  644.             getyx (stdscr, cursory, cursorx);
  645.             mvprintw (LINES - 1, 0,
  646.                 "use one of the keys ");
  647.             for (i = 0; (i < Size2 - 1) && (i < 30); i++)
  648.                 printw ("%c", Facechars[i]);
  649.             if (i < Size2 - 1)
  650.                 printw ("...");
  651.             clrtoeol ();
  652.             move (cursory, cursorx);
  653.             refresh ();
  654.             continue;
  655.             }
  656.         }
  657.         }
  658.         else        /* Cursormethod != FACEKEYS */
  659.         {
  660.         if (m == Up)
  661.         {
  662.             if (Newpos >= Sizex)
  663.             Newpos -= Sizex;
  664.             unchanged = 0;/* board must be reprinted */
  665.             continue;
  666.         }
  667.         if (m == Down)
  668.         {
  669.             if (Newpos + Sizex < Size2)
  670.             Newpos += Sizex;
  671.             unchanged = 0;
  672.             continue;
  673.         }
  674.         if (m == Left)
  675.         {
  676.             if ((Newpos % Sizex) != 0)
  677.             Newpos--;
  678.             unchanged = 0;
  679.             continue;
  680.         }
  681.         if (m == Right)
  682.         {
  683.             if (((Newpos + 1) % Sizex) != 0)
  684.             Newpos++;
  685.             unchanged = 0;
  686.             continue;
  687.         }
  688.         /* 
  689.          * If a key not in the set { Up, Down, Left, Right } was
  690.          * typed we fall through and try to move the empty field to
  691.          * Newpos.
  692.          */
  693.         }
  694.     /* 
  695.      * The user has indicated a new location for the empty field.
  696.      * The new position of the empty field in the array board is in
  697.      * Newpos.
  698.      * We must now check that the new position is on the same row
  699.      * or the same column as the current position and we must
  700.      * determine how many tiles must be moved.
  701.      */
  702.         if (Newpos == empty)
  703.         continue;    /* nothing changed */
  704.         steps = 0;
  705.         if (Newpos > empty)
  706.         {
  707.         if ((empty % Sizex + Newpos - empty) < Sizex)
  708.         {
  709.             m = Right;
  710.             steps = Newpos - empty;
  711.         }
  712.         else
  713.             if (((Newpos - empty) % Sizex) == 0)
  714.             {
  715.             m = Down;
  716.             steps = (Newpos - empty) / Sizex;
  717.             }
  718.         }
  719.         else
  720.         {
  721.         if (empty % Sizex + Newpos - empty >= 0)
  722.         {
  723.             m = Left;
  724.             steps = empty - Newpos;
  725.         }
  726.         else
  727.             if (((empty - Newpos) % Sizex) == 0)
  728.             {
  729.             m = Up;
  730.             steps = (empty - Newpos) / Sizex;
  731.             }
  732.         }
  733.         if (steps == 0)
  734.         {
  735.         getyx (stdscr, cursory, cursorx);
  736.         mvprintw (LINES - 1, 0,
  737.             "tile must be in same row as empty field\n");
  738.         move (cursory, cursorx);
  739.         refresh ();
  740.  
  741.         continue;
  742.         }
  743.     }
  744.     else            /* Movemethod is MOVEBLANK or MOVETILE */
  745.         steps = 1;        /* one step per move */
  746.     /* 
  747.      * m should now be one of the four directions, but it may be an
  748.      * illegal key. This can not happen if Movemethod == SELECTTILE.
  749.      *
  750.      * steps indicates how many tiles are to be moved
  751.      */
  752.     if ((m != Up) && (m != Down) && (m != Left) && (m != Right))
  753.     {
  754.         getyx (stdscr, cursory, cursorx);
  755. #ifdef ARROWKEYS
  756.         if (Cursormethod == ARROWK)
  757.         mvprintw (LINES - 1, 0,
  758.             "Use the arrow keys for up, down, left and  right");
  759.         else
  760. #endif
  761.         if (Movemethod == MOVETILE)
  762.         {
  763.             mvprintw (LINES - 1, 0,
  764.                 "use %c for up, %c for down, ", Down, Up);
  765.             printw ("%c for left and %c for right", Right, Left);
  766.         }
  767.         else
  768.         {
  769.             mvprintw (LINES - 1, 0,
  770.                 "use %c for up, %c for down, ", Up, Down);
  771.             printw ("%c for left and %c for right", Left, Right);
  772.         }
  773.         clrtoeol ();
  774.         move (cursory, cursorx);
  775.         refresh ();
  776.         continue;
  777.     }
  778.     /* 
  779.      * m contains the direction to move
  780.      * steps contains the number of tiles to move
  781.      * Apply the move to the board.
  782.      */
  783.     if (m == Up)
  784.         if (empty >= Sizex)
  785.         while (steps-- > 0)
  786.         {
  787.             board[empty] = board[empty - Sizex];
  788.             board[empty - Sizex] = BLANK;
  789.             empty -= Sizex;
  790.             Movecnt++;
  791.         }
  792.     if (m == Down)
  793.         if (empty + Sizex < Size2)
  794.         while (steps-- > 0)
  795.         {
  796.             board[empty] = board[empty + Sizex];
  797.             board[empty + Sizex] = BLANK;
  798.             empty += Sizex;
  799.             Movecnt++;
  800.         }
  801.     if (m == Left)
  802.         if ((empty % Sizex) != 0)
  803.         while (steps-- > 0)
  804.         {
  805.             board[empty] = board[empty - 1];
  806.             board[empty - 1] = BLANK;
  807.             empty--;
  808.             Movecnt++;
  809.         }
  810.     if (m == Right)
  811.         if (((empty + 1) % Sizex) != 0)
  812.         while (steps-- > 0)
  813.         {
  814.             board[empty] = board[empty + 1];
  815.             board[empty + 1] = BLANK;
  816.             empty++;
  817.             Movecnt++;
  818.         }
  819.     if (steps == 1)        /* you ran into a wall */
  820.     {
  821.         getyx (stdscr, cursory, cursorx);
  822.         mvprintw (LINES - 1, 0,
  823.             "Your can't cross that wall\n");
  824.         clrtoeol ();
  825.         move (cursory, cursorx);
  826.         refresh ();
  827.         continue;
  828.     }
  829.     if (steps != -1)    /* something is very wrong */
  830.     {
  831.         mvprintw (LINES - 2, 0,
  832.             "aargh: couldn't move enough tiles (steps = %d)\n",
  833.             steps);
  834.         die ();
  835.     }
  836.     Newpos = empty;
  837.     unchanged = 0;        /* the board must be reprinted */
  838.     }
  839. }
  840.  
  841. printboard (board)
  842. int    *board;
  843. {
  844.     register int    i, j;
  845.     int     tilewidth;
  846.     unsigned long   time_used;
  847.     unsigned    minutes;
  848.     unsigned    seconds;
  849.     unsigned long   time_now;
  850.  
  851.     tilewidth = ((Facemethod == NUMBERFACES) && (Size2 > 10)) ? 5 : 4;
  852.     mvprintw ((LINES - 4 - 2 * Sizey) / 2,
  853.         (COLS - 1 - tilewidth * Sizex) / 2,
  854.         "+");        /* print top edge of board */
  855.     for (j = 0; j < Sizex; j++)
  856.     if (tilewidth == 5)
  857.         printw ("----+");
  858.     else
  859.         printw ("---+");
  860.     for (i = 0; i < Sizey; i++)
  861.     {
  862.     mvprintw ((LINES - 4 - 2 * Sizey) / 2 + i * 2 + 1,
  863.         (COLS - 1 - tilewidth * Sizex) / 2, "| ");
  864.     for (j = 0; j < Sizex; j++)
  865.         if (tilewidth == 5)
  866.         if (board[Sizex * i + j] != BLANK)
  867.             if ((Size2 > 9) && (Cursormethod == FACEKEYS) &&
  868.                 (board[Sizex * i + j] <= Size2 / 10))
  869.             printw ("%02d | ", board[Sizex * i + j]);
  870.             else
  871.             printw ("%2d | ", board[Sizex * i + j]);
  872.         else
  873.             printw ("   | ");
  874.         else
  875.         if (board[Sizex * i + j] != BLANK)
  876.             printw ("%c | ", Facechars[board[Sizex * i + j] - 1]);
  877.         else
  878.             printw ("  | ");
  879.     mvprintw ((LINES - 4 - 2 * Sizey) / 2 + i * 2 + 2,
  880.         (COLS - 1 - tilewidth * Sizex) / 2, "+");
  881.     for (j = 0; j < Sizex; j++)
  882.         if (tilewidth == 5)
  883.         printw ("----+");
  884.         else
  885.         printw ("---+");
  886.     }
  887.     mvprintw (LINES - 1, 0, "\n");/* erase error messages */
  888.  /* 
  889.   * Update the clock
  890.   */
  891.     time_now = time ((long *) 0);
  892.     time_used = time_now - Time_start;
  893.     minutes = time_used / 60;
  894.     seconds = time_used % 60;
  895.     mvprintw (LINES - 3, 0, "Time used: %02d min %02d sec   Move: %d",
  896.         minutes, seconds, Movecnt);
  897.  /* 
  898.   * Put cursor on the position indicated by Newpos
  899.   */
  900.     move ((LINES - 4 - 2 * Sizey) / 2 + (Newpos / Sizex) * 2 + 1,
  901.         (COLS - 1 - tilewidth * Sizex) / 2 +
  902.         (Newpos % Sizex) * tilewidth + tilewidth / 2);
  903.     refresh ();            /* put all this on the screen */
  904. }
  905.