[comp.sources.games] v06i026: puzzle15-2 - revised puzzle15

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