#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 );
}