games@tekred.CNA.TEK.COM (03/16/89)
Submitted-by: "P. Knoppers" <nluug.nl!duteca!knop@tektronix> Posting-number: Volume 6, Issue 26 Archive-name: puzzle15-2 #! /bin/sh # This is a shell archive. Remove anything before this line, then unpack # it by saving it into a file and typing "sh file". To overwrite existing # files, type "sh file -c". You can also feed this as standard input via # unshar, or by typing "sh <file", e.g.. If this archive is complete, you # will see the following message at the end: # "End of shell archive." # Contents: README Makefile puzzle15.6 puzzle15.c # Wrapped by billr@saab on Wed Mar 15 14:36:49 1989 PATH=/bin:/usr/bin:/usr/ucb ; export PATH if test -f 'README' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'README'\" else echo shar: Extracting \"'README'\" \(604 characters\) sed "s/^X//" >'README' <<'END_OF_FILE' XI have applied some of the suggestions put to me by various persons X(acknowledged in the program text). The program can be compiled with Xthe option -DARROWKEYS, but I am not sure that it works - I cannot Xtest it. The other compile-time options have been tested and they work. X XIn the following shell archive you will find the source of the program Xand a manual page in [nt]roff -man format. X XSince the previous version I have moved to another machine. My email Xaddress is now knop@duteca.UUCP. Mail to the previous machine may not Xbounce, in which case it might appear in my mailbox on the new machine. X END_OF_FILE if test 604 -ne `wc -c <'README'`; then echo shar: \"'README'\" unpacked with wrong size! fi # end of 'README' fi if test -f 'Makefile' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'Makefile'\" else echo shar: Extracting \"'Makefile'\" \(101 characters\) sed "s/^X//" >'Makefile' <<'END_OF_FILE' X# simple makefile for puzzle15 Xpuzzle15: puzzle15.c X cc -o puzzle15 -O puzzle15.c -lcurses -ltermcap END_OF_FILE if test 101 -ne `wc -c <'Makefile'`; then echo shar: \"'Makefile'\" unpacked with wrong size! fi # end of 'Makefile' fi if test -f 'puzzle15.6' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'puzzle15.6'\" else echo shar: Extracting \"'puzzle15.6'\" \(6379 characters\) sed "s/^X//" >'puzzle15.6' <<'END_OF_FILE' X.TH PUZZLE15 6 X.UC 4 X.SH NAME Xpuzzle15 \- simulate a well known toy with many variations X.SH SYNOPSIS X.br X.B puzzle15 X[<width[x<height>]] X[movemethod] X[cursormethod] X[facemethod] X.SH DESCRIPTION X.I Puzzle15 Xsimulates a game that is available in many toy shops. This computerized Xversion is crt-oriented and offers fewer possibilities for cheating. X.br XWhen invoked without arguments X.I puzzle15 Xpresents a 4 by 4 grid with 15 numbered tiles and one empty square. XThe tiles are not ordered in sequence. XThe tiles adjacent to the empty square can exchange places with the Xempty square. This is done by indicating the direction that the empty Xsquare is to move with the vi(1) cursor keys (k for up, j for down, Xh for left and l for right). X.br XThe intention of the game is to rearrange the tiles in their ``natural'' Xsequence. X.PP XThe program keeps track of the time and the number of moves used. XThis makes it possible to arrange competitions where time, or the Xnumber of moves used determines the winner. X.PP XThe program ends when the tiles are arranged in sequence, or when you Xtype your interrupt character (usually ctrl-C or break). On computers Xthat lack good support for keyboard interrupts a key can be selected Xthat will terminate the program. (This is a compile-time option.) X.SH VARIATIONS XThe look and feel of the program can be altered by specifying different Xdimensions for the array, the way that the tile to move is selected X(movemethod), the keys that specify where the cursor is to go X(cursormethod) and the faces that are shown on the tiles (facemethod). X.SH DIMENSIONS XThe dimensions of the board can be varied within the limits of your Xterminal screen. To obtain a square array a single numeric argument Xis required. X.I Puzzle15 X.I 3 Xpresents a 3x3 array with 8 numbered tiles. With numbered tiles it Xis not possible to extend the array beyond 10 by 10, because only two Xdigits are used to identify each tile. X.PP XTo obtain a non-square array an argument that consists of a number, Xthe character 'x' and another number must be supplied. X.I Puzzle15 X.I 6x3 Xpresents a grid that is 6 columns wide and 3 rows high with 17 Xnumbered tiles. X.SH MOVEMETHODS XThe default movemethod is to indicate the direction that the empty X(blank) field is to move. This is called X.I moveblank. XThere are two other methods that can be selected by an argument on the Xcommand line. X.br X.I Movetile Xtells X.I puzzle15 Xthat you want to indicate the direction that an adjacent tile is to be Xmoved. This is equivalent to reversal of the directions of the cursor Xkeys. X.br X.I Selecttile Xtells X.I puzzle15 Xthat you want to indicate the new position of the empty field by Xmoving the cursor to that position and then typing a <space>, or X<return> (or whatever you like). In this way you can move several Xtiles in one blow. X.SH CURSORMETHODS XThe default way to move the cursor or the empty field is to use the Xvi(1) cursor keys. X.br XOn some computers it is possible to specify X.I arrowkeys, Xwhich tells X.I puzzle15 Xto use the arrow keys of your terminal to specify a move. X.br XIf you specify X.I numberkeys Xon the command line, X.I puzzle15 Xwill use '8' for up, '2' for down, '4' for left and '6' for right. Xthis corresponds to arrows on the numeric key pad of most personal Xcomputers. X.br XAnother way to specify where the empty field is to go is obtained by Xspecifying X.I facekeys Xon the command line. In this mode you move the empty field to a new Xposition by typing the character(s) shown on the tile that currently Xoccupies that position. X.I Facekeys Ximplies use of the movemethod X.I selecttile. XIf the X.I facekeys Xcursormethod is used with numbered tiles, some tile numbers are shown Xwith leading zero. To select such a tile a leading zero is required. XTiles that are labeled with a single digit can be selected with that Xdigit, or with a leading zero followed by that digit. X.br XFinally you can specify your own cursor movement keys with X.I keys X.I <up><down><left><right> Xon the command line. <Up> is the key that you will use to move Xup, etc. X.SH FACEMETHODS XThe default way that X.I puzzle15 Xuses to identify the tiles is to number them. If you prefer Xtiles that are marked with letters you must specify X.I alphafaces Xon the command line. X.I Puzzle15 Xwill now use the lower-case letters a..z to identify the tiles. X.br XSimilarly, if you specify X.I numberalphafaces X.I puzzle15 Xwill use the digits 0..9 followed by the lower-case letters a..z to Xidentify the tiles. X.br X.I Alphanumberfaces Xselects the lower-case letters a..z followed by the digits 0..9. X.br XFinally, you can specify the characters to use on the command line with X.I faces X.I <facecharacters>. XThe order in X.I <facecharacters> Xwill be used to determine when the tiles are ordered. X.SH EXAMPLES X.I puzzle15 X.I faces X.I '1234567890qwertyuiopasdfghjkl;zxcvbnm,.' X.I 10x4 X.br Xcreates a 10 by 4 array filled with the symbols of a typewriter Xkeyboard. The position to achieve is the ordering of the keys on Xthe typewriter. Quoting is necessary to prevent the shell from Xtreating the ``;'' as a special character. By adding the option X.I facekeys Xthe program turns into a tough typing tutor, especially if you decide Xnot to take your eyes off the screen. X.SH DIAGNOSTICS XAn unknown keyword or an illegal combination of keywords on the command Xline generates a complete list of possible keywords and restrictions on Xtheir combinations. X.br XIf you type an undefined key during the game, the program lists the keys Xthat are valid. X.br XOn startup fatal diagnostics from the curses screen handling package Xare possible if your terminal is ill-defined. X.br XIf the program detects an internal error a diagnostic starting with the Xletters X.I aargh: Xis printed. If you manage to get one of these, I like to know how Xyou did it. X.SH AUTHOR XP. Knoppers - knop@duteca.UUCP. X.br XPaul Lew added arrow keys. X.br XBo Kullmar added the clock and the move counter. X.br XLarry Hastings suggested the selecttile method. X.SH COPYRIGHT XThe X.I puzzle15 Xprogram is protected by copyright (C) 1984, 1988, 1989 by P. Knoppers. XDistribution of unmodified copies of the program with source is unrestricted. XYou are not allowed to distribute modified copies. This is to prevent Xuncontrolled spreading of a zillion different versions. XYou can mail suggestions for improvements to me. I X.I may Xincorporate them in a future version. END_OF_FILE if test 6379 -ne `wc -c <'puzzle15.6'`; then echo shar: \"'puzzle15.6'\" unpacked with wrong size! fi # end of 'puzzle15.6' fi if test -f 'puzzle15.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'puzzle15.c'\" else echo shar: Extracting \"'puzzle15.c'\" \(23213 characters\) sed "s/^X//" >'puzzle15.c' <<'END_OF_FILE' X/* X * This program simulates a simple toy with many many variations. X * X * Compile with -DARROWKEYS if your curses library understands about X * arrow keys (and your keyboards have them). X * Compile with -DNOSIGNAL if your machine does not support the signal X * function to catch ctrl-C. X * Compile with "-DDIECHAR='<somechar>'" if you would like the program X * to exit gracefully when <somechar> is typed. All the quotes in and X * around this argument are necessary. The double quotes prevent the X * shell from stripping the single quotes. Instead of '<somechar' you X * can also type the ascii value of the key. If you do so, the double X * quotes are not needed. X * X * The copyright message is there to prevent uncontrolled spreading of X * a zillion different versions. X * X * You can mail your improvements and suggestions to me. I may include X * them in a future version of the program. X * X * I do not guarantee the fitness of this program for any purpose. X * USE IT AT YOUR OWN RISK. X * X * Peter Knoppers - knop@duteca.UUCP X * Bilderdijkhof 59 X * 2624 ZG Delft X * The Netherlands X * X * X * The following lines are put in the compiled program by the C compiler X * where they can be found by programs such as (Bsd) strings. X */ Xchar *this_is = "puzzle15 V2.0 Feb 24 1989"; Xchar *author0 = "(C) Copyright 1985, 1988, 1989 Peter Knoppers"; Xchar *author1 = "Arrow keys added by Paul Lew 1988"; Xchar *author2 = "Clock added by Bo Kullmar 1988"; Xchar *author3 = "Select-tile method suggested by Larry Hastings 1988"; Xchar *authorx = "Additions merged by Peter Knoppers"; Xchar *release1 = "Permission to use and redistribute unmodified "; Xchar *release2 = "copies with source is granted to everyone"; Xchar *release3 = "Distribution of modified copies is NOT allowed"; X X#include <curses.h> X#ifndef NOSIGNAL X#include <signal.h> X#endif X#include <ctype.h> X X#ifndef DIECHAR X#define DIECHAR 999 /* any non-ASCII value will do */ X#endif X#define DEFSIZE 4 /* default size of board */ X#define BLANK 0 X#define UNSPECIFIED (-1) X/* classification of arguments */ X#define MOVEMETHOD 1 X#define CURSORMETHOD 2 X#define FACEMETHOD 3 X/* magic values are required to be different, no other restrictions apply */ X/* magic values for Movemethods */ X#define MOVEBLANK 4 X#define MOVETILE 5 X#define SELECTTILE 6 X/* magic values for Cursormethods */ X#define VIKEYS 7 X#ifdef ARROWKEYS X#define ARROWK 8 X#endif X#define NUMBERKEYS 9 X#define FACEKEYS 10 X#define KEYS 11 X/* magic values for Facemethods */ X#define NUMBERFACES 12 X#define ALPHAFACES 13 X#define NUMBERALPHAFACES 14 X#define ALPHANUMBERFACES 15 X#define FACES 16 X Xunsigned long Time_start; Xint Movecnt = 0; X Xchar *malloc (); /* to satisfy lint */ Xlong time (); X Xint Sizex = UNSPECIFIED; /* x-size of the board */ Xint Sizey = UNSPECIFIED; /* y-size of the board */ Xint Size2; /* total surface of the board */ Xint Movemethod = UNSPECIFIED; Xint Cursormethod = UNSPECIFIED; Xint Facemethod = UNSPECIFIED; Xchar *Movename; /* name of the selected Movemethod */ Xchar *Cursorname; /* name of the selected Cursormethod */ Xchar *Cursorkeys; /* the user-defined Cursorkeys */ Xchar *Facename; /* name of the selected Facemethod */ Xchar *Facechars; /* the user-defined tile-faces */ Xint Up; /* key-codes for cursor movements */ Xint Down; Xint Left; Xint Right; Xint Newpos; /* new position for empty field */ Xchar *Myname; /* name used to invoke this program */ X/* X * All possible keyword arguments are listed in the Argopts table. X * This is done in order to X * - ensure that every keyword appears at only one place in the program X * (this makes the program easier to modify/maintain) X * - put most of the knowledge about incompatible arguments in one place X * instead of scattering it all over the program. X * - simplify the argument parser X * (I haven't been completely succesfull in this respect...) X */ Xstruct argtablemember X{ X char *a_name; /* what the user types */ X int a_type; /* what kind of spec. it is */ X int a_magic; /* magic value */ X} Argopts[] = X{ X { X "moveblank", MOVEMETHOD, MOVEBLANK X }, X { X "movetile", MOVEMETHOD, MOVETILE X }, X { /* SELECTTILE must be before FACEKEYS */ X "selecttile", MOVEMETHOD, SELECTTILE X }, X { X "vikeys", CURSORMETHOD, VIKEYS X }, X#ifdef ARROWKEYS X { X "arrowkeys", CURSORMETHOD, ARROWK X }, X#endif X { X "numberkeys", CURSORMETHOD, NUMBERKEYS X }, X { X "facekeys", CURSORMETHOD, FACEKEYS X }, X { X "keys", CURSORMETHOD, KEYS X }, X { X "numberfaces", FACEMETHOD, NUMBERFACES X }, X { X "alphafaces", FACEMETHOD, ALPHAFACES X }, X { X "numberalphafaces", FACEMETHOD, NUMBERALPHAFACES X }, X { X "alphanumberfaces", FACEMETHOD, ALPHANUMBERFACES X }, X { X "faces", FACEMETHOD, FACES X }, X { X (char *) 0, 0, 0 X } X}; X Xdie () /* nice exit on ctrl-C */ X{ X#ifndef NOSIGNAL X signal (SIGINT, SIG_IGN); /* ignore ctrl-C */ X#endif X mvprintw (LINES - 1, 0, "Goodbye. "); X clrtoeol (); X refresh (); X endwin (); /* shutdown curses, restore ttymode */ X exit (0); X} X Xmain (argc, argv) /* scan the arguments, call game() */ Xchar **argv; X{ X struct argtablemember *atmp;/* to walk Argopts table */ X int swap; /* to revers directions */ X X Myname = *argv; /* save this for usage () */ X /* X * scan the arguments X */ X while (*++argv != ((char *) 0)) X { /* walk argument list */ X for (atmp = Argopts; atmp -> a_name != (char *) 0; atmp++) X { /* scan the keyword list */ X if (strcmp (*argv, atmp -> a_name) == 0) X { /* found keyword */ X switch (atmp -> a_type) X { X case MOVEMETHOD: X if (Movemethod != UNSPECIFIED) X conflict (Movename, atmp -> a_name); X Movename = atmp -> a_name; X Movemethod = atmp -> a_magic; X break; X case CURSORMETHOD: X if (Cursormethod != UNSPECIFIED) X conflict (Cursorname, atmp -> a_name); X Cursorname = atmp -> a_name; X Cursormethod = atmp -> a_magic; X if (atmp -> a_magic == KEYS) X { X if ((Cursorkeys = *++argv) == (char *) 0) X novalue (*--argv);/* never returns */ X if (strlen (Cursorkeys) != 4) X { X printf ("%s need 4 cursorkeys\n", X Cursorname); X usage (); X } X } X break; X case FACEMETHOD: X if (Facemethod != UNSPECIFIED) X conflict (Facename, atmp -> a_name); X Facename = atmp -> a_name; X Facemethod = atmp -> a_magic; X if (atmp -> a_magic == FACES) X if ((Facechars = *++argv) == (char *) 0) X novalue (*--argv); X break; X default: X printf ("aargh: bad switch (atmp -> a_type = %d)\n", X atmp -> a_type); X exit (1); X } X break; X } X } X if (atmp -> a_name == (char *) 0)/* not a keyword */ X if (isdigit (**argv) && (Sizex == UNSPECIFIED)) X { /* it's a boardsize specification */ X if (sscanf (*argv, "%dx%d", &Sizex, &Sizey) != 2) X if (sscanf (*argv, "%d", &Sizex) == 1) X Sizey = Sizex; X else X { X printf ("bad argument %s\n", *argv); X usage (); X } X } X else /* it's garbage */ X { X printf ("bad argument %s\n", *argv); X usage (); X } X } X X /* X * insert default values X */ X if (Sizex == UNSPECIFIED) X Sizex = Sizey = DEFSIZE; X X if (Cursormethod == UNSPECIFIED) X { /* find first CURSORMETHOD in Argopts */ X for (atmp = Argopts; atmp -> a_name != (char *) 0; atmp++) X if (atmp -> a_type == CURSORMETHOD) X break; X if (atmp -> a_name == (char *) 0) X { X printf ("aargh: can't find default Cursormethod\n"); X exit (1); X } X Cursormethod = atmp -> a_magic; X Cursorname = atmp -> a_name; X } X X if (Facemethod == UNSPECIFIED) X { /* find first FACEMETHOD in Argopts */ X for (atmp = Argopts; atmp -> a_name != (char *) 0; atmp++) X if (atmp -> a_type == FACEMETHOD) X break; X if (atmp -> a_name == (char *) 0) X { X printf ("aargh: can't find default Facemethod\n"); X exit (1); X } X Facemethod = atmp -> a_magic; X Facename = atmp -> a_name; X } X X if (Movemethod == UNSPECIFIED) X if (Cursormethod == FACEKEYS) X Movemethod = SELECTTILE;/* by implication */ X else X { /* find first MOVEMETHOD in Argopts */ X for (atmp = Argopts; atmp -> a_name != (char *) 0; atmp++) X if (atmp -> a_type == MOVEMETHOD) X break; X if (atmp -> a_name == (char *) 0) X { X printf ("aargh: can't find default Movemethod\n"); X exit (1); X } X Movemethod = atmp -> a_magic; X Movename = atmp -> a_name; X } X X if ((Cursormethod == FACEKEYS) && (Movemethod != SELECTTILE)) X conflict (Cursorname, Movename);/* does not return */ X switch (Cursormethod) X { X case VIKEYS: X Up = 'k'; X Down = 'j'; X Left = 'h'; X Right = 'l'; X break; X#ifdef ARROWKEYS X case ARROWK: X Up = KEY_UP; X Down = KEY_DOWN; X Left = KEY_LEFT; X Right = KEY_RIGHT; X break; X#endif X case NUMBERKEYS: X Up = '8'; X Down = '2'; X Left = '4'; X Right = '6'; X break; X case FACEKEYS: X Up = 1000; /* values must not correspond to any ASCII */ X Down = 1001; /* value, otherwise no restrictions */ X Left = 1002; X Right = 1003; X break; X case KEYS: X Up = Cursorkeys[0]; X Down = Cursorkeys[1]; X Left = Cursorkeys[2]; X Right = Cursorkeys[3]; X break; X default: X printf ("aargh: bad switch (Cursormethod = %d)\n", X Cursormethod); X exit (1); X } X X if (Movemethod == MOVETILE) X { /* revers cursor directions */ X swap = Up; X Up = Down; X Down = swap; X swap = Left; X Left = Right; X Right = swap; X } X X Size2 = Sizex * Sizey; /* surface area of the board */ X X switch (Facemethod) X { X case NUMBERFACES: X if (Size2 <= 10) /* tiles can be labeled with 1 char */ X { X Facechars = "123456789"; X Facemethod = NUMBERALPHAFACES; X } X break; X case ALPHAFACES: X Facechars = "abcdefghijklmnopqrstuvwxyz"; X break; X case NUMBERALPHAFACES: X Facechars = "0123456789abcdefghijklmnopqrstuvwxyz"; X break; X case ALPHANUMBERFACES: X Facechars = "abcdefghijklmnopqrstuvwxyz0123456789"; X break; X } X X if ((Size2 - 1) > ((Facemethod == NUMBERFACES) ? 99 : X strlen (Facechars))) X { X printf ("sorry, too many tiles - not enough labels\n"); X exit (0); X } X if ((Sizex < 2) || (Sizey < 2)) X { X printf ("sorry, board is too small for a non-trivial game\n"); X exit (0); X } X X /* X * Initialize the curses screen handling functions X */ X#ifndef NOSIGNAL X signal (SIGINT, SIG_IGN); /* protect this critical section */ X#endif X initscr (); X crmode (); X noecho (); X#ifdef ARROWKEYS X keypad (stdscr, TRUE); X#endif X#ifndef NOSIGNAL X signal (SIGINT, die); /* die() knows how to restore ttymode */ X#endif X /* X * From now on use die() to exit the program, otherwise the X * ttymode will not be restored. X * X * Now that curses has been initialized we can check whether the board X * fits on the screen. X */ X if (Sizex > (COLS - 1) / 5) /* COLS works only after initscr() */ X { X mvprintw (LINES - 2, 0, X "sorry, your screen is not wide enough\n"); X die (); X } X if (Sizey > (LINES - 4) / 2) X { X mvprintw (LINES - 2, 0, X "sorry, your screen is not tall enough\n"); X die (); X } X X game (); /* play the game */ X X mvprintw (LINES - 2, 0, X "You've reached the solution in %d moves.\n", Movecnt); X die (); /* restore ttymode and exit */ X} X Xusage () X{ X int curtype = UNSPECIFIED; X int prevtype = UNSPECIFIED; X char *selecttilename = (char *) 0; X struct argtablemember *atmp;/* to walk Argopts table */ X X printf ("usage: %s [<width[x<height>]] %s", X Myname, "[movemethod] [cursormethod] [facemethod]\n"); X for (atmp = Argopts; atmp -> a_name != (char *) 0; atmp++) X { X if (atmp -> a_magic == SELECTTILE) X selecttilename = atmp -> a_name; X if (atmp -> a_type != curtype) X { X curtype = atmp -> a_type; X if (curtype == MOVEMETHOD) X printf ("possible movemethods:\n"); X if (curtype == CURSORMETHOD) X printf ("possible cursormethods:\n"); X if (curtype == FACEMETHOD) X printf ("possible facemethods:\n"); X } X switch (atmp -> a_magic) X { X case KEYS: X printf ("\t\t%s <up><down><left><right>", atmp -> a_name); X break; X case FACES: X printf ("\t\t%s <facecharacters>", atmp -> a_name); X break; X case FACEKEYS: X if (selecttilename == (char *) 0) X { X printf ("aargh: can't find string for SELECTTILE\n"); X exit (1); X } X printf ("\t\t%-20.20s (implies movemethod %s)", X atmp -> a_name, selecttilename); X break; X default: X printf ("\t\t%-20.20s", atmp -> a_name); X break; X } X if (curtype != prevtype) X printf (" (this is default)\n"); X else X printf ("\n"); X prevtype = curtype; X } X exit (0); X} X Xconflict (word1, word2) Xchar *word1; Xchar *word2; X{ X printf ("You may not specify both %s and %s\n", word1, word2); X usage (); X} X Xnovalue (word) Xchar *word; X{ X printf ("%s requires an argument\n", word); X usage (); X} X Xgame () /* initialize board, execute moves */ X{ X register int i, j; /* generally used indices and counters */ X int *board; /* pointer to malloc-ed board */ X int empty; /* position of empty field */ X int swap; /* used to swap two tiles */ X int nswap = 0; /* to determine reachability */ X int steps; /* number of tiles that move */ X int unchanged = 0; /* used to indicate that board has changed */ X int cursorx; /* to save cursorposition while */ X int cursory; /* printing error messages */ X int m; /* move, first character / direction */ X int m2; /* second character of move */ X /* X * Set up the board and shuffle the tiles X */ X board = (int *) malloc ((unsigned) Size2 * sizeof (int)); X if (board == NULL) X { X printf ("aargh: malloc failed\n"); X die (); X } X srand ((int) time ((long) 0));/* initialize random number generator */ X for (i = 1; i < Size2; i++) /* put tiles on the board in their */ X board[i - 1] = i; /* final positions */ X for (i = Size2 - 2; i > 0; i--)/* permutate tiles */ X { X j = rand () % i; X swap = board[i]; X board[i] = board[j]; X board[j] = swap; X } X /* X * Check that final position can be reached from current permutation X */ X for (i = 0; i < Size2 - 1; i++) X for (j = 0; j < i; j++) X if (board[j] > board[i]) X nswap++; X if ((nswap % 2) != 0) /* this position is unreachable */ X { /* swap two adjacent tiles */ X swap = board[1]; X board[1] = board[0]; X board[0] = swap; X } X empty = Size2 - 1; /* empty field starts in lower right */ X board[empty] = BLANK; /* corner */ X Newpos = empty; X Time_start = time ((long *) 0);/* start the clock */ X X while (1) /* until final position is reached */ X { X if (unchanged == 0) /* the board must be (re-)printed */ X { X printboard (board); /* also puts cursor at Newpos */ X unchanged++; /* the board on the screen is up to date */ X /* X * Check if final position is reached X */ X for (i = 0; i < Size2 - 1; i++) X if (board[i] != i + 1) X break; /* final position is not yet reached */ X if (i == Size2 - 1) /* all tiles are in final positions */ X return; /* game ends */ X } X /* X * Let the user make a move X */ X m = getch (); X if (m == DIECHAR) X die (); X if (Movemethod == SELECTTILE) X { X if (Cursormethod == FACEKEYS) X { X if (Facemethod == NUMBERFACES) X { X if (!isdigit (m)) X { X getyx (stdscr, cursory, cursorx); X mvprintw (LINES - 1, 0, X "use one of the keys"); X for (i = 0; (i <= Size2) && (i < 10); i++) X printw (" %d", i); X clrtoeol (); X move (cursory, cursorx); X refresh (); X continue; X } X if ((m - '0') > (Size2 / 10)) X m -= '0'; X else /* we need a second digit */ X { X m = (m - '0') * 10; X m2 = getch (); X if (m == DIECHAR) X die (); X if ((!isdigit (m2)) || (m + m2 - '0' >= Size2)) X { X getyx (stdscr, cursory, cursorx); X mvprintw (LINES - 1, 0, X "use one of the keys"); X for (i = 0; (i < Size2 % 10) && (i + m < Size2); X i++) X printw (" %d", i); X clrtoeol (); X move (cursory, cursorx); X refresh (); X continue; X } X m += m2 - '0'; X } X /* X * find out where this tile is on the board X */ X for (Newpos = 0; Newpos < Size2; Newpos++) X if (board[Newpos] == m) X break;/* found tile */ X if (Newpos == Size2)/* no tile with face m */ X { X mvprintw (LINES - 2, 0, X "aargh: can't find tile %d on board\n", m); X die (); X } X } X else X { X /* X * Facemethod != NUMBERFACES X * This means that a single keystroke identifies the X * tile that is to be moved. X */ X for (Newpos = 0; Newpos < Size2; Newpos++) X if (board[Newpos] > 0) X if (Facechars[board[Newpos] - 1] == m) X break;/* found tile */ X if (Newpos == Size2) X { X getyx (stdscr, cursory, cursorx); X mvprintw (LINES - 1, 0, X "use one of the keys "); X for (i = 0; (i < Size2 - 1) && (i < 30); i++) X printw ("%c", Facechars[i]); X if (i < Size2 - 1) X printw ("..."); X clrtoeol (); X move (cursory, cursorx); X refresh (); X continue; X } X } X } X else /* Cursormethod != FACEKEYS */ X { X if (m == Up) X { X if (Newpos >= Sizex) X Newpos -= Sizex; X unchanged = 0;/* board must be reprinted */ X continue; X } X if (m == Down) X { X if (Newpos + Sizex < Size2) X Newpos += Sizex; X unchanged = 0; X continue; X } X if (m == Left) X { X if ((Newpos % Sizex) != 0) X Newpos--; X unchanged = 0; X continue; X } X if (m == Right) X { X if (((Newpos + 1) % Sizex) != 0) X Newpos++; X unchanged = 0; X continue; X } X /* X * If a key not in the set { Up, Down, Left, Right } was X * typed we fall through and try to move the empty field to X * Newpos. X */ X } X /* X * The user has indicated a new location for the empty field. X * The new position of the empty field in the array board is in X * Newpos. X * We must now check that the new position is on the same row X * or the same column as the current position and we must X * determine how many tiles must be moved. X */ X if (Newpos == empty) X continue; /* nothing changed */ X steps = 0; X if (Newpos > empty) X { X if ((empty % Sizex + Newpos - empty) < Sizex) X { X m = Right; X steps = Newpos - empty; X } X else X if (((Newpos - empty) % Sizex) == 0) X { X m = Down; X steps = (Newpos - empty) / Sizex; X } X } X else X { X if (empty % Sizex + Newpos - empty >= 0) X { X m = Left; X steps = empty - Newpos; X } X else X if (((empty - Newpos) % Sizex) == 0) X { X m = Up; X steps = (empty - Newpos) / Sizex; X } X } X if (steps == 0) X { X getyx (stdscr, cursory, cursorx); X mvprintw (LINES - 1, 0, X "tile must be in same row as empty field\n"); X move (cursory, cursorx); X refresh (); X X continue; X } X } X else /* Movemethod is MOVEBLANK or MOVETILE */ X steps = 1; /* one step per move */ X /* X * m should now be one of the four directions, but it may be an X * illegal key. This can not happen if Movemethod == SELECTTILE. X * X * steps indicates how many tiles are to be moved X */ X if ((m != Up) && (m != Down) && (m != Left) && (m != Right)) X { X getyx (stdscr, cursory, cursorx); X#ifdef ARROWKEYS X if (Cursormethod == ARROWK) X mvprintw (LINES - 1, 0, X "Use the arrow keys for up, down, left and right"); X else X#endif X if (Movemethod == MOVETILE) X { X mvprintw (LINES - 1, 0, X "use %c for up, %c for down, ", Down, Up); X printw ("%c for left and %c for right", Right, Left); X } X else X { X mvprintw (LINES - 1, 0, X "use %c for up, %c for down, ", Up, Down); X printw ("%c for left and %c for right", Left, Right); X } X clrtoeol (); X move (cursory, cursorx); X refresh (); X continue; X } X /* X * m contains the direction to move X * steps contains the number of tiles to move X * Apply the move to the board. X */ X if (m == Up) X if (empty >= Sizex) X while (steps-- > 0) X { X board[empty] = board[empty - Sizex]; X board[empty - Sizex] = BLANK; X empty -= Sizex; X Movecnt++; X } X if (m == Down) X if (empty + Sizex < Size2) X while (steps-- > 0) X { X board[empty] = board[empty + Sizex]; X board[empty + Sizex] = BLANK; X empty += Sizex; X Movecnt++; X } X if (m == Left) X if ((empty % Sizex) != 0) X while (steps-- > 0) X { X board[empty] = board[empty - 1]; X board[empty - 1] = BLANK; X empty--; X Movecnt++; X } X if (m == Right) X if (((empty + 1) % Sizex) != 0) X while (steps-- > 0) X { X board[empty] = board[empty + 1]; X board[empty + 1] = BLANK; X empty++; X Movecnt++; X } X if (steps == 1) /* you ran into a wall */ X { X getyx (stdscr, cursory, cursorx); X mvprintw (LINES - 1, 0, X "Your can't cross that wall\n"); X clrtoeol (); X move (cursory, cursorx); X refresh (); X continue; X } X if (steps != -1) /* something is very wrong */ X { X mvprintw (LINES - 2, 0, X "aargh: couldn't move enough tiles (steps = %d)\n", X steps); X die (); X } X Newpos = empty; X unchanged = 0; /* the board must be reprinted */ X } X} X Xprintboard (board) Xint *board; X{ X register int i, j; X int tilewidth; X unsigned long time_used; X unsigned minutes; X unsigned seconds; X unsigned long time_now; X X tilewidth = ((Facemethod == NUMBERFACES) && (Size2 > 10)) ? 5 : 4; X mvprintw ((LINES - 4 - 2 * Sizey) / 2, X (COLS - 1 - tilewidth * Sizex) / 2, X "+"); /* print top edge of board */ X for (j = 0; j < Sizex; j++) X if (tilewidth == 5) X printw ("----+"); X else X printw ("---+"); X for (i = 0; i < Sizey; i++) X { X mvprintw ((LINES - 4 - 2 * Sizey) / 2 + i * 2 + 1, X (COLS - 1 - tilewidth * Sizex) / 2, "| "); X for (j = 0; j < Sizex; j++) X if (tilewidth == 5) X if (board[Sizex * i + j] != BLANK) X if ((Size2 > 9) && (Cursormethod == FACEKEYS) && X (board[Sizex * i + j] <= Size2 / 10)) X printw ("%02d | ", board[Sizex * i + j]); X else X printw ("%2d | ", board[Sizex * i + j]); X else X printw (" | "); X else X if (board[Sizex * i + j] != BLANK) X printw ("%c | ", Facechars[board[Sizex * i + j] - 1]); X else X printw (" | "); X mvprintw ((LINES - 4 - 2 * Sizey) / 2 + i * 2 + 2, X (COLS - 1 - tilewidth * Sizex) / 2, "+"); X for (j = 0; j < Sizex; j++) X if (tilewidth == 5) X printw ("----+"); X else X printw ("---+"); X } X mvprintw (LINES - 1, 0, "\n");/* erase error messages */ X /* X * Update the clock X */ X time_now = time ((long *) 0); X time_used = time_now - Time_start; X minutes = time_used / 60; X seconds = time_used % 60; X mvprintw (LINES - 3, 0, "Time used: %02d min %02d sec Move: %d", X minutes, seconds, Movecnt); X /* X * Put cursor on the position indicated by Newpos X */ X move ((LINES - 4 - 2 * Sizey) / 2 + (Newpos / Sizex) * 2 + 1, X (COLS - 1 - tilewidth * Sizex) / 2 + X (Newpos % Sizex) * tilewidth + tilewidth / 2); X refresh (); /* put all this on the screen */ X} END_OF_FILE if test 23213 -ne `wc -c <'puzzle15.c'`; then echo shar: \"'puzzle15.c'\" unpacked with wrong size! fi # end of 'puzzle15.c' fi echo shar: End of shell archive. exit 0