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