#ident "$Id: panex.c,v 1.7 2004/11/17 22:27:51 pwh Rel $"

/*
 * Panex Puzzle.
 */

#include        <stdio.h>
#include	<stdlib.h>
#include	<unistd.h>
#include	<curses.h>
#include	<string.h>

#include	"panex.h"

#define		DEFAULT_HEIGHT	4
#define		SOLVED		"Solved!"
#define		HELP_SCREEN	1
#define		INSTRUCTIONS	2


static void printVersion ( void )

{
   fprintf ( stdout, "%s\nRelease:  %s\nDate:  %s\n", PUZZLE_TITLE, RELSTRING,
								RELDATE );
}


static void printInstructions ( void )

{
   fprintf ( stdout, "\n\tObject:\n\t-------\n\
\tMoving disks one at a time, exchange the position of the two towers.\n\
\tUse the arrow keys (on a keypad to the right of the main keyboard or\n\
\tturn off Num Lock and use the number keypad) to control disk movement.\n\n\
\tRules:\n\t------\n\
\tNo disk can go below its starting level.  The smallest disks go no\n\
\tlower than the next to top position while the largest disks can go\n\
\tall the way to the bottom of a peg.  Disks may not pass through each\n\
\tother.  If a small disk occupies an upper level of a peg and the lower\n\
\tlevels of the peg are unoccupied, you may not place a larger disk below\n\
\tthe smaller disk without first moving the smaller disk out of the way.\n\
\tA peg is \"full\" when its top level is occupied.  Once a peg is \"full\"\n\
\tno more disks may be placed on the peg or in the case of the center\n\
\tpeg moved over it.\n" );
}


static void printHelp ( const char *progname )

{
   fprintf ( stdout, "\n\tUse:\n\t----\n\
\t%s [-s|-] [-S|F|X|W] [tower_height ...]\n\t%s -h|i\n\n\
\tOptions:\n\t--------\n\
\t-s  Display the solution.\n\
\t-   Display the solution to the screen as narrative.\n\
\t-S|F|X|W  Animation speed (Slow/Fast/eXtreme/Warp).\n\
\ttower_height specifies the number of disks in the tower.  Specifying\n\
\t    multiple tower heights on the command line presents the puzzle\n\
\t    once for each height specified.  If no tower height is specified\n\
\t    then a %d disk tower is used.\n\
\t-h  Display help screen (this screen).\n\
\t-i  Display instructions.\n",
	progname, progname, DEFAULT_HEIGHT );
}


static const char *basename ( const char *path )

{
   int	start = strlen ( path );

   while ( start > 0 &&  path [start - 1] != '/' ) --start;

   return ( path + start );
}


main ( int argc, char **argv )

{
   int		status = 0;
   int		height = DEFAULT_HEIGHT;
   int		solve = 0;
   int		interactive = 1;
   int		animation = NORMAL_SPEED;
   const char	*progName = basename ( *argv );

   if ( argc > 1 ) {

	int	help = 0;
	int	sleep_events = 0;

	++argv;

	while ( *argv && **argv == '-' ) {

		int	opt_ptr = 1;
		int	opt_ch;

		while ( opt_ch = argv [0] [opt_ptr] ) {

			switch ( opt_ch ) {

			   case 'h':
			   case 'H':

				help |= HELP_SCREEN;
				break;

			   case 'i':
			   case 'I':

				help |= INSTRUCTIONS;
				break;

			   case 'S':

				animation = SLOW_SPEED;
				break;

			   case 'F':

				animation = HIGH_SPEED;
				break;

			   case 'X':

				animation = XTREME_SPEED;
				break;

			   case 'W':

				animation = WARP_SPEED;
				break;

			   case 's':

				solve = DEFAULT_SOLUTION;

				switch ( argv [0] [++opt_ptr] ) {

				   case 'o':

					solve = OBVIOUS_SOLUTION;
					break;

				   case 'r':

					solve = RECURSIVE_SOLUTION;
					break;

				   case 'a':

					solve = ALTERNATING_SOLUTION;
					break;

				   case 'A':

					solve = BETTER_ALTERNATING_SOLUTION;
					break;


				   case 'z':

					solve = ZIGZAG_SOLUTION;
					break;

				   case 'Z':

					solve = BETTER_ZIGZAG_SOLUTION;
					break;

				   case 'b':

					solve = BOTTOM_UP_SOLUTION;
					break;

				   case 'B':

					solve = BETTER_BOTTOM_UP_SOLUTION;
					break;

				   case 'e':

					solve = EFFICIENT_SOLUTION;
					break;

				   default:

					--opt_ptr;
					break;
				}

				break;

			   case '-':

				interactive = 0;
				break;

			   default:

				fprintf ( stderr,
				"Invalid command line option (%c) ignored\n",
								opt_ch );
				sleep_events++;
				break;
			}

			++opt_ptr;
		}

		if ( opt_ptr == 1 ) interactive = 0;

		++argv;
		--argc;
	}

	if ( help ) {

		printVersion ();

		if ( help & HELP_SCREEN ) printHelp ( progName );

		if ( help & INSTRUCTIONS ) printInstructions ();

		fputc ( '\n', stdout );

		while ( *argv ) ++argv;

	} else {

		if ( ! *argv ) --argv;
		if ( sleep_events ) sleep ( sleep_events << 1 );
	}
   }

   while ( ! status && *argv ) {

	PUZZLE	*puzzle;

	if ( argc > 1 ) {

		if ( ( height = strtol ( *argv, ( char ** ) NULL, 10 ) ) < 1 )
			fprintf ( stderr,
				"Invalid tower height (%s) on command line.\n",
									*argv );
	}

	++argv;

	if ( height > 0 && ( puzzle = openPuzzle ( height, interactive,
							animation ) ) ) {

		interactive = isInteractive ( puzzle );

		if ( solve || ! interactive ) {

			if ( ! solvePuzzle ( puzzle, puzzle->height, solve )
						|| ! isSolved ( puzzle ) ) {

				status = 1;

				if ( ! interactive ) {

					fprintf ( stdout,
						"ERROR - not a solution!\n" );
				}

			} else if ( interactive ) {

				char	*p = SOLVED;

				move ( puzzle->y0 + 2, puzzle->xc
						- ( sizeof ( SOLVED ) >> 1 ) );
				while ( *p ) addch ( *p++ );
				move ( 0, 0 );
			}

		} else {

			if ( ! playGame ( puzzle ) ) status = 1;
		}

		closePuzzle ( puzzle );

	} else status = 1;
   }

   return ( status );
}


Last updated: Friday, June 12, 2015 03:21:43 AM