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