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