#ident "$Id: game.c,v 1.5 2006/06/28 19:31:03 pwh Rel $"

/*
 * Interactive Tower of Hanoi puzzle logic.
 */

#include        <stdio.h>
#include	<errno.h>

#include	"hanoi.h"

#define		CONGRATULATIONS	"CONGRATULATIONS!!!"
#define		CONFIRM		"Exit game (Y/N)?"
#define		TAUNT		"See, wasn't that easy?"

/* Move stack. */
#define	STACK_SIZE	32767

typedef struct {

	int	src;
	int	dest;

} MOVE;

typedef struct {

	int	top;
	int	contents;
	MOVE	moves [STACK_SIZE];

} MOVE_STACK;


static void resetMoveStack ( MOVE_STACK *stack )

{
   stack->top = stack->contents = 0;
}


static void pushMove ( MOVE_STACK *stack, int src, int dest )

{
   stack->moves [stack->top].src = src;
   stack->moves [stack->top].dest = dest;
   if ( ++stack->top == STACK_SIZE ) stack->top = 0;

   if ( stack->contents < STACK_SIZE ) ++stack->contents;
}


static const MOVE *popMove ( MOVE_STACK *stack )

{
   const MOVE	*move = NULL;

   if ( stack->contents ) {

	--stack->contents;

	if ( --stack->top < 0 ) stack->top += STACK_SIZE;

	move = stack->moves + stack->top;
   }

   return ( move );
}


static int cursorVisible ( PUZZLE *puzzle, int peg )

{
   return ( ! puzzle->xDisk || puzzle->xDisk->y != puzzle->y0 + 2
	|| puzzle->xp [peg] < puzzle->xDisk->x - puzzle->xDisk->size - 1
	|| puzzle->xp [peg] > puzzle->xDisk->x + puzzle->xDisk->size + 1 );
}


int playGame ( PUZZLE *puzzle )

{
   int	status;
   int	yt = puzzle->y0 + 2;
   int	c = 'r';

   while ( c == 'r' ) {

	int		peg = LEFT_PEG;
	int		src = -1;
	int		dest = -1;
	int		drop = -1;
	int		undo = -1;
	int		solveIt = 0;
	int		moveDone = 1;
	int		done = 0;
	MOVE_STACK	moveHistory;
	const MOVE	*lastMove;

	status = 1;
	resetMoveStack ( &moveHistory );

	move ( yt, puzzle->xp [LEFT_PEG] );
	addch ( ACS_DIAMOND );
	move ( 0, 0 );
	refresh ();

	while ( ! done ) {

		switch ( c = getch () ) {

		   case 'v':
		   case 'j':
		   case KEY_DOWN:	/* Drop the disk. */

			if ( src != -1 && drop == -1 ) {

				drop = peg;
				moveDone = 0;

				if ( src != peg ) pushMove ( &moveHistory,
								src, peg );
			} else beep ();

			break;

		   case '^':
		   case 'k':
		   case KEY_UP:		/* Pickup a disk. */

			if ( src == -1 ) {

				src = peg;
				moveDone = 0;
				
			} else beep ();

			break;

		   case '<':
		   case 'h':
		   case KEY_LEFT:	/* Move to the left. */

			if ( peg > LEFT_PEG ) {

				if ( cursorVisible ( puzzle, peg ) ) {

					move ( yt, puzzle->xp [peg] );
					addch ( ' ' );
				}

				--peg;

				if ( src != -1 ) {

					dest = peg;
					moveDone = 0;
				}

			} else beep ();

			break;

		   case '>':
		   case 'l':
		   case KEY_RIGHT:	/* Move to the right. */

			if ( peg < RIGHT_PEG ) {

				if ( cursorVisible ( puzzle, peg ) ) {

					move ( yt, puzzle->xp [peg] );
					addch ( ' ' );
				}

				++peg;

				if ( src != -1 ) {

					dest = peg;
					moveDone = 0;
				}

			} else beep ();

			break;

		   case 's':		/* Solve the puzzle. */

			src = dest = drop = undo = -1;
			solveIt = 1;
			moveDone = 0;
			break;

		   case 'u':		/* Undo the last move. */

			if ( undo != -1 ) {

				beep ();
				break;

			} else if ( src == -1 ) {

				if ( lastMove = popMove ( &moveHistory ) ) {

					int	dest;

					dest = lastMove->dest;
					undo = lastMove->src;

					if ( undoMove ( puzzle, undo, dest ) ) {

						src = dest;
						drop = undo;
						moveDone = 0;

					} else {

						pushMove ( &moveHistory,
								undo, dest );
						undo = -1;
						beep ();
					}

				} else beep();

				break;
			}

		   case ESC:		/* Interrupt the current move. */

			if ( src != -1 && undo == -1 ) {

				if ( drop != -1 ) popMove ( &moveHistory );
				undo = drop = src;
				moveDone = 0;

				break;
			}

		   case 'q':		/* Quit the game. */

			move ( yt - 1, puzzle->xc
						- ( sizeof ( CONFIRM ) >> 1 ) );
			{
				char	*p = CONFIRM;

				while ( *p ) addch ( *p++ );
			}

			refresh ();

			while ( ( done = getch () ) == ERR );

			if ( done == 'y' || done == 'Y' ) {

				puzzle->moves = 0;
				if ( c == 'q' ) status = 0;

			} else {

				done = sizeof ( CONFIRM );
				move ( yt - 1, puzzle->xc
						- ( sizeof ( CONFIRM ) >> 1 ) );

				do { addch ( ' ' ); } while ( --done > 0 );

				break;
			}

		   case 'r':		/* Restart the game. */

			solveIt = 0;
			moveDone = 1;
			done = 1;
			break;

		   case 'S':		/* Slow. */

			setSpeed ( puzzle, SLOW_SPEED );
			break;

		   case 'N':		/* Normal speed. */

			setSpeed ( puzzle, NORMAL_SPEED );
			break;

		   case 'F':		/* Fast. */

			setSpeed ( puzzle, HIGH_SPEED );
			break;

		   case 'X':		/* Extremely fast. */

			setSpeed ( puzzle, XTREME_SPEED );
			break;

		   case 'W':		/* Warp speed. */

			setSpeed ( puzzle, WARP_SPEED );
			break;

		   case ERR:		/* No input. */

			break;

		   default:		/* Invalid input. */

			beep ();
			break;
		}

		if ( ! done && ! solveIt && cursorVisible ( puzzle, peg ) ) {

			move ( yt, puzzle->xp [peg] );
			addch ( ACS_DIAMOND );
		}

		if ( ! moveDone ) {

			if ( solveIt ) {

				if ( cursorVisible ( puzzle, peg ) ) {

					move ( yt, puzzle->xp [peg] );
					addch ( ' ' );
				}

				done = 1;

				if ( ( status
					= solvePuzzle ( puzzle, WARP_SPEED ) )
						&& isSolved ( puzzle ) ) {

					char	*p = TAUNT;

					move ( yt, puzzle->xc
						- ( sizeof ( TAUNT ) >> 1 ) );
					while ( *p ) addch ( *p++ );

					c = ERR;

					moveDone = 1;

				} else if ( errno == EINTR ) {

					done = 0;
					status = 1;
				}

			} else if ( drop != -1 ) {

				if ( moveDone = dropDisk ( puzzle, drop ) ) {

					if ( isSolved ( puzzle ) ) {

						char	*p = CONGRATULATIONS;

						move ( yt, puzzle->xp [peg] );
						addch ( ' ' );

						move ( yt, puzzle->xc
						- ( sizeof ( CONGRATULATIONS )
								>> 1 ) );
						
						while ( *p ) addch ( *p++ );
						done = 1;
					}

					src = -1;
					dest = -1;
					drop = -1;
					undo = -1;

				} else if ( ! errno ) {

					beep ();
					popMove ( &moveHistory );
					moveDone = slideDisk ( puzzle, drop );
					drop = -1;
				}

			} else if ( dest != -1 ) moveDone
						= slideDisk ( puzzle, dest );

			else if ( src != -1 ) {

				if ( ! ( moveDone = pickupDisk ( puzzle, src ) )
								&& ! errno ) {
					beep ();
					src = -1;
					moveDone = 1;
				}
			}

			if ( ! done && ! solveIt
					&& cursorVisible ( puzzle, peg ) ) {

				move ( yt, puzzle->xp [peg] );
				addch ( ACS_DIAMOND );
			}
		}

		move ( 0, 0 );
		refresh ();
	}

	if ( c != 'q' && c != 'r' && c != ESC )
		while ( ( c = getch() ) == ERR );

	if ( c == 'r' ) {

		if ( ! resetPuzzle ( puzzle ) ) c = EOF;

	} else ungetch ( c );
   }

   return ( status );
}


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