#ident "$Id: hanoi.c,v 1.5 2006/05/30 21:12:33 pwh Rel $"
/*
* Tower of Hanoi Puzzle.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include "hanoi.h"
#define DEFAULT_HEIGHT 6
#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 printHelp ( const char *progname )
{
fprintf ( stdout, "\n\tUse:\n\t----\n\
\t%s [-s|-] [-S|N|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|N|F|X|W Animation speed (Slow/Normal/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 void printInstructions ( void )
{
fprintf ( stdout, "\n\tObject:\n\t-------\n\
\tMoving disks one at a time, move the tower from the left peg to the\n\
\tright peg. Use the arrow keys (on a keypad to the right of the main\n\
\tkeyboard or turn off Num Lock and use the number keypad) to control\n\
\tdisk movement.\n\n\
\tRules:\n\t------\n\
\tThe only restriction on moves is that you may never put a larger disk\n\
\ton top of a smaller disk.\n" );
}
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 showSolution = 0;
int animated = 1;
int speed = 0;
const char *progName = basename ( *argv );
if ( argc > 1 ) {
int help = 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':
speed = SLOW_SPEED;
break;
case 'N':
speed = NORMAL_SPEED;
break;
case 'F':
speed = HIGH_SPEED;
break;
case 'X':
speed = XTREME_SPEED;
break;
case 'W':
speed = WARP_SPEED;
break;
case 's':
showSolution = 1;
break;
case '-':
animated = 0;
break;
default:
fprintf ( stderr,
"Invalid command line option (%c) ignored\n",
opt_ch );
break;
}
++opt_ptr;
}
if ( opt_ptr == 1 ) animated = 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;
}
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, animated, speed ) ) ) {
animated = isAnimated ( puzzle );
if ( showSolution || ! animated ) {
if ( ! solvePuzzle ( puzzle, speed )
|| ! isSolved ( puzzle ) ) {
if ( ! errno ) status = 1;
if ( ! animated ) {
fprintf ( stdout,
"ERROR - not a solution!\n" );
}
} else if ( animated ) {
char *p = SOLVED;
move ( puzzle->y0 + 2, puzzle->xc
- ( sizeof ( SOLVED ) >> 1 ) );
while ( *p ) addch ( *p++ );
move ( 0, 0 );
}
} else {
if ( ! speed ) setSpeed ( puzzle, WARP_SPEED );
if ( ! playGame ( puzzle ) ) status = 1;
}
closePuzzle ( puzzle );
} else status = 1;
}
return ( status );
}