#ident "$Id: game.c,v 1.5 2006/06/28 19:31:03 pwh Rel $" /* * Interactive Tower of Hanoi puzzle logic. */ #include #include #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 ); }