home *** CD-ROM | disk | FTP | other *** search
/ Geek Gadgets 1 / ADE-1.bin / ade-dist / ncurses-1.9.9e-src.tgz / tar.out / fsf / ncurses / test / knight.c < prev    next >
C/C++ Source or Header  |  1996-09-28  |  14KB  |  556 lines

  1. /*
  2.  * Knight's Tour - a brain game
  3.  *
  4.  * The original of this game was anonymous.  It had an unbelievably bogus
  5.  * interface, you actually had to enter square coordinates!  Redesign by
  6.  * Eric S. Raymond <esr@snark.thyrsus.com> July 22 1995.  Mouse support
  7.  * added September 20th 1995.
  8.  */
  9.  
  10. #include <curses.h>
  11. #include <ctype.h>
  12. #include <signal.h>
  13. #include <stdlib.h>
  14. #include <unistd.h>
  15. #include <string.h>
  16.  
  17. /* board size */
  18. #define BDEPTH    8
  19. #define BWIDTH    8
  20.  
  21. /* where to start the instructions */
  22. #define INSTRY    2
  23. #define INSTRX    35
  24.  
  25. /* corner of board */
  26. #define BOARDY    2
  27. #define BOARDX    0
  28.  
  29. /* notification line */
  30. #define NOTIFYY    21
  31.  
  32. /* virtual color values */
  33. #define TRAIL_COLOR    1
  34. #define PLUS_COLOR    2
  35. #define MINUS_COLOR    3
  36.  
  37. #define CX(x)    (2 + 4 * (x))
  38. #define CY(y)    (1 + 2 * (y))
  39. #define cellmove(y, x)    wmove(boardwin, CY(y), CX(x))
  40. #define CXINV(x)    (((x) - 1) / 4)
  41. #define CYINV(y)    (((y) - 2) / 2)
  42.  
  43. typedef struct
  44. {
  45.     short    x, y;
  46. }
  47. cell;
  48.  
  49. static short    board[BDEPTH][BWIDTH];    /* the squares */
  50. static int    rw,col;            /* current row and column */
  51. static int    lastrow,lastcol;       /* last location visited */
  52. static cell    history[BDEPTH*BWIDTH];    /* choice history */
  53. static int    movecount;        /* count of moves so far */
  54. static WINDOW    *boardwin;        /* the board window */
  55. static WINDOW    *helpwin;        /* the help window */
  56. static WINDOW    *msgwin;        /* the message window */
  57. static chtype    trail = '#';        /* trail character */
  58. static chtype    plus = '+';        /* cursor hot-spot character */
  59. static chtype    minus = '-';        /* possible-move character */
  60. static chtype    oldch;
  61.  
  62. static void init(void);
  63. static void play(void);
  64. static void dosquares(void);
  65. static void drawmove(char, int, int, int, int);
  66. static bool evalmove(int, int);
  67. static bool chkmoves(void);
  68. static bool chksqr(int, int);
  69. static int  iabs(int);
  70.  
  71. int main(int argc, char *argv[])
  72. {
  73.     init();
  74.  
  75.     play();
  76.  
  77.     endwin();
  78.     exit(0);
  79. }
  80.  
  81. static void init (void)
  82. {
  83.     srand ((unsigned)getpid());
  84.     initscr ();
  85.     cbreak ();            /* immediate char return */
  86.     noecho ();            /* no immediate echo */
  87.     boardwin = newwin(BDEPTH * 2 + 1, BWIDTH * 4 + 1, BOARDY, BOARDX);
  88.     helpwin = newwin(0, 0, INSTRY, INSTRX);
  89.     msgwin = newwin(1, INSTRX-1, NOTIFYY, 0);
  90.     keypad(boardwin, TRUE);
  91.  
  92.     if (has_colors())
  93.     {
  94.     start_color();
  95.  
  96.     (void) init_pair(TRAIL_COLOR, COLOR_CYAN,  COLOR_BLACK);
  97.     (void) init_pair(PLUS_COLOR,  COLOR_RED,   COLOR_BLACK);
  98.     (void) init_pair(MINUS_COLOR, COLOR_GREEN, COLOR_BLACK);
  99.  
  100.     trail |= COLOR_PAIR(TRAIL_COLOR);
  101.     plus  |= COLOR_PAIR(PLUS_COLOR);
  102.     minus |= COLOR_PAIR(MINUS_COLOR);
  103.     }
  104.  
  105. #ifdef NCURSES_MOUSE_VERSION
  106.     (void) mousemask(BUTTON1_CLICKED, (mmask_t *)NULL);
  107. #endif /* NCURSES_MOUSE_VERSION*/
  108.  
  109.     oldch = minus;
  110. }
  111.  
  112. static void help1(void)
  113. /* game explanation -- initial help screen */
  114. {
  115.     (void)waddstr(helpwin, "Knight's move is a solitaire puzzle.  Your\n");
  116.     (void)waddstr(helpwin, "objective is to visit each square of the  \n");
  117.     (void)waddstr(helpwin, "chessboard exactly once by making knight's\n");
  118.     (void)waddstr(helpwin, "moves (one square right or left followed  \n");
  119.     (void)waddstr(helpwin, "by two squares up or down, or two squares \n");
  120.     (void)waddstr(helpwin, "right or left followed by one square up or\n");
  121.     (void)waddstr(helpwin, "down).  You may start anywhere.\n\n");
  122.  
  123.     (void)waddstr(helpwin, "Use arrow keys to move the cursor around.\n");
  124.     (void)waddstr(helpwin, "When you want to move your knight to the \n");
  125.     (void)waddstr(helpwin, "cursor location, press <space> or Enter.\n");
  126.     (void)waddstr(helpwin, "Illegal moves will be rejected with an  \n");
  127.     (void)waddstr(helpwin, "audible beep.\n\n");
  128.     (void)waddstr(helpwin, "The program will detect if you solve the\n");
  129.     (void)waddstr(helpwin, "puzzle; also inform you when you run out\n");
  130.     (void)waddstr(helpwin, "of legal moves.\n\n");
  131.  
  132.     (void)mvwaddstr(helpwin, NOTIFYY-INSTRY, 0,
  133.             "Press `?' to go to keystroke help."); 
  134. }
  135.  
  136. static void help2(void)
  137. /* keystroke help screen */
  138. {
  139.     (void)waddstr(helpwin, "Possible moves are shown with `-'.\n\n");
  140.  
  141.     (void)waddstr(helpwin, "You can move around with the arrow keys or\n");
  142.     (void)waddstr(helpwin, "with the rogue/hack movement keys.  Other\n");
  143.     (void)waddstr(helpwin, "commands allow you to undo moves or redraw.\n");
  144.     (void)waddstr(helpwin, "Your mouse may work; try left-button to\n");
  145.     (void)waddstr(helpwin, "move to the square under the pointer.\n\n");
  146.  
  147.     (void)waddstr(helpwin, "x,q -- exit             y k u    7 8 9\n");
  148.     (void)waddstr(helpwin, "r -- redraw screen       \\|/      \\|/ \n");
  149.     (void)waddstr(helpwin, "u -- undo move          h-+-l    4-+-6\n");
  150.     (void)waddstr(helpwin, "                         /|\\      /|\\ \n");
  151.     (void)waddstr(helpwin, "                        b j n    1 2 3\n");
  152.  
  153.     (void)waddstr(helpwin,"\nYou can place your knight on the selected\n");
  154.     (void)waddstr(helpwin, "square with spacebar, Enter, or the keypad\n");
  155.     (void)waddstr(helpwin, "center key.  You can quit with `x' or `q'.\n");
  156.  
  157.     (void)mvwaddstr(helpwin, NOTIFYY-INSTRY, 0,
  158.             "Press `?' to go to game explanation"); 
  159. }
  160.  
  161. static void play (void)
  162. /* play the game */
  163. {
  164.     bool        keyhelp; /* TRUE if keystroke help is up */
  165.     int    c, ny = 0, nx = 0;
  166.     int i, j, count;
  167.  
  168.     do {
  169.        /* clear screen and draw board */
  170.        werase(boardwin);
  171.        werase(helpwin);
  172.        werase(msgwin);
  173.        dosquares();
  174.        help1();
  175.        wnoutrefresh(stdscr);
  176.        wnoutrefresh(helpwin);
  177.        wnoutrefresh(msgwin);
  178.        wnoutrefresh(boardwin);
  179.        doupdate();
  180.  
  181.        for (i = 0; i < BDEPTH; i++)
  182.            for (j = 0; j < BWIDTH; j++)
  183.            {
  184.            board[i][j] = FALSE;
  185.            cellmove(i, j);
  186.            waddch(boardwin, minus);
  187.            }
  188.        memset(history, '\0', sizeof(history));
  189.        history[0].y = history[0].x = -1;
  190.        lastrow = lastcol = -2;
  191.        movecount = 1;
  192.        keyhelp = FALSE;
  193.  
  194.        for (;;)
  195.        {
  196.            if (rw != lastrow || col != lastcol)
  197.            {
  198.            if (lastrow >= 0 && lastcol >= 0)
  199.            {
  200.                cellmove(lastrow, lastcol);
  201.                if (board[lastrow][lastcol])
  202.                waddch(boardwin, trail);
  203.                else
  204.                waddch(boardwin, oldch);
  205.            }
  206.  
  207.            cellmove(rw, col);
  208.            oldch = winch(boardwin);
  209.  
  210.            lastrow = rw;
  211.            lastcol= col;
  212.            }
  213.            cellmove(rw, col);
  214.            waddch(boardwin, plus);
  215.            cellmove(rw, col);
  216.  
  217.            wrefresh(msgwin);
  218.  
  219.            c = wgetch(boardwin);
  220.  
  221.            werase(msgwin);
  222.  
  223.            switch (c)
  224.            {
  225.            case 'k': case '8':
  226.            case KEY_UP:
  227.            ny = rw+BDEPTH-1; nx = col;
  228.            break;
  229.            case 'j': case '2':
  230.            case KEY_DOWN:
  231.            ny = rw+1;        nx = col;
  232.            break;
  233.            case 'h': case '4':
  234.            case KEY_LEFT:
  235.            ny = rw;          nx = col+BWIDTH-1;
  236.            break;
  237.            case 'l': case '6':
  238.            case KEY_RIGHT:
  239.            ny = rw;          nx = col+1;
  240.            break;
  241.            case 'y': case '7':
  242.            case KEY_A1:
  243.            ny = rw+BDEPTH-1; nx = col+BWIDTH-1;
  244.            break;
  245.            case 'b': case '1':
  246.            case KEY_C1:
  247.            ny = rw+1;        nx = col+BWIDTH-1;
  248.            break;
  249.            case 'u': case '9':
  250.            case KEY_A3:
  251.            ny = rw+BDEPTH-1; nx = col+1;
  252.            break;
  253.            case 'n': case '3':
  254.            case KEY_C3:
  255.            ny = rw+1;        nx = col+1;
  256.            break;
  257.  
  258. #ifdef NCURSES_MOUSE_VERSION
  259.            case KEY_MOUSE:
  260.            {
  261.                MEVENT    myevent;
  262.  
  263.                getmouse(&myevent);
  264.                if (myevent.y >= CY(0) && myevent.y <= CY(BDEPTH)
  265.                && myevent.x >= CX(0) && myevent.x <= CX(BWIDTH))
  266.                {
  267.                nx = CXINV(myevent.x);
  268.                ny = CYINV(myevent.y);
  269.                ungetch('\n');
  270.                break;
  271.                }
  272.                else
  273.                {
  274.                beep();
  275.                continue;
  276.                }
  277.            }
  278. #endif /* NCURSES_MOUSE_VERSION */
  279.  
  280.            case KEY_B2:
  281.            case '\n':
  282.            case ' ':
  283.            if (evalmove(rw, col))
  284.            {
  285.                drawmove(trail,
  286.                 history[movecount-1].y, history[movecount-1].x,
  287.                 rw, col);
  288.                history[movecount].y = rw; 
  289.                history[movecount].x = col; 
  290.                movecount++;
  291.  
  292.                if (!chkmoves()) 
  293.                goto dropout;
  294.            }
  295.            else
  296.                beep();
  297.            break;
  298.  
  299.            case KEY_REDO:
  300.            case '\f':
  301.            case 'r':
  302.            clearok(curscr, TRUE);
  303.            wnoutrefresh(stdscr);
  304.            wnoutrefresh(boardwin);
  305.            wnoutrefresh(msgwin);
  306.            wnoutrefresh(helpwin);
  307.            doupdate();
  308.            break;
  309.  
  310.            case KEY_UNDO:
  311.            case KEY_BACKSPACE:
  312.            case '\b':
  313.            if (movecount == 1)
  314.            {
  315.                ny = lastrow;
  316.                nx = lastcol;
  317.                waddstr(msgwin, "\nNo previous move.");
  318.                beep();
  319.            }
  320.            else
  321.            {
  322.                int oldy = history[movecount-1].y;
  323.                int oldx = history[movecount-1].x;
  324.  
  325.                board[oldy][oldx] = FALSE;
  326.                --movecount;
  327.                ny = history[movecount-1].y;
  328.                nx = history[movecount-1].x;
  329.                drawmove(' ', oldy, oldx, ny, nx);
  330.  
  331.                /* avoid problems if we just changed the current cell */
  332.                cellmove(lastrow, lastcol);
  333.                oldch = winch(boardwin);
  334.            }
  335.            break;
  336.  
  337.            case 'q':
  338.            case 'x':
  339.            goto dropout;
  340.  
  341.            case '?':
  342.            werase(helpwin);
  343.            if (keyhelp)
  344.            {
  345.                help1();
  346.                keyhelp = FALSE;
  347.            }
  348.            else
  349.            {
  350.                help2();
  351.                keyhelp = TRUE;
  352.            }
  353.            wrefresh(helpwin);
  354.            break;
  355.  
  356.            default:
  357.            beep();
  358.            break;
  359.            }
  360.  
  361.            col = nx % BWIDTH;
  362.            rw = ny % BDEPTH;
  363.        }
  364.  
  365.        dropout:
  366.        count = 0;
  367.        for (i = 0; i < BDEPTH; i++)
  368.            for (j = 0; j < BWIDTH; j++)
  369.            if (board[i][j] != 0)
  370.                count += 1;
  371.        if (count == (BWIDTH * BDEPTH))
  372.            wprintw(msgwin, "\nYou won.  Care to try again? ");
  373.        else
  374.            wprintw(msgwin, "\n%d squares filled.  Try again? ", count);
  375.        } while
  376.        (tolower(wgetch(msgwin)) == 'y');
  377. }
  378.  
  379. static void dosquares (void)
  380. {
  381.     int i, j;
  382.  
  383.     mvaddstr(0, 20, "KNIGHT'S MOVE -- a logical solitaire");
  384.  
  385.     move(BOARDY,BOARDX);
  386.     waddch(boardwin, ACS_ULCORNER);
  387.     for (j = 0; j < 7; j++)
  388.     {
  389.     waddch(boardwin, ACS_HLINE);
  390.     waddch(boardwin, ACS_HLINE);
  391.     waddch(boardwin, ACS_HLINE);
  392.     waddch(boardwin, ACS_TTEE);
  393.     }
  394.     waddch(boardwin, ACS_HLINE);
  395.     waddch(boardwin, ACS_HLINE);
  396.     waddch(boardwin, ACS_HLINE);
  397.     waddch(boardwin, ACS_URCORNER);
  398.  
  399.     for (i = 1; i < BDEPTH; i++)
  400.     {
  401.     move(BOARDY + i * 2 - 1, BOARDX);
  402.     waddch(boardwin, ACS_VLINE); 
  403.     for (j = 0; j < BWIDTH; j++)
  404.     {
  405.         waddch(boardwin, ' ');
  406.         waddch(boardwin, ' ');
  407.         waddch(boardwin, ' ');
  408.         waddch(boardwin, ACS_VLINE);
  409.     }
  410.     move(BOARDY + i * 2, BOARDX);
  411.     waddch(boardwin, ACS_LTEE); 
  412.     for (j = 0; j < BWIDTH - 1; j++)
  413.     {
  414.         waddch(boardwin, ACS_HLINE);
  415.         waddch(boardwin, ACS_HLINE);
  416.         waddch(boardwin, ACS_HLINE);
  417.         waddch(boardwin, ACS_PLUS);
  418.     }
  419.     waddch(boardwin, ACS_HLINE);
  420.     waddch(boardwin, ACS_HLINE);
  421.     waddch(boardwin, ACS_HLINE);
  422.     waddch(boardwin, ACS_RTEE);
  423.     }
  424.  
  425.     move(BOARDY + i * 2 - 1, BOARDX);
  426.     waddch(boardwin, ACS_VLINE);
  427.     for (j = 0; j < BWIDTH; j++)
  428.     {
  429.     waddch(boardwin, ' ');
  430.     waddch(boardwin, ' ');
  431.     waddch(boardwin, ' ');
  432.     waddch(boardwin, ACS_VLINE);
  433.     }
  434.  
  435.     move(BOARDY + i * 2, BOARDX);
  436.     waddch(boardwin, ACS_LLCORNER);
  437.     for (j = 0; j < BWIDTH - 1; j++)
  438.     {
  439.     waddch(boardwin, ACS_HLINE);
  440.     waddch(boardwin, ACS_HLINE);
  441.     waddch(boardwin, ACS_HLINE);
  442.     waddch(boardwin, ACS_BTEE);
  443.     }
  444.     waddch(boardwin, ACS_HLINE);
  445.     waddch(boardwin, ACS_HLINE);
  446.     waddch(boardwin, ACS_HLINE);
  447.     waddch(boardwin, ACS_LRCORNER);
  448. }
  449.  
  450. static void mark_possibles(int prow, int pcol, chtype mark)
  451. {
  452.     if (chksqr(prow+2,pcol+1)){cellmove(prow+2,pcol+1);waddch(boardwin,mark);};
  453.     if (chksqr(prow+2,pcol-1)){cellmove(prow+2,pcol-1);waddch(boardwin,mark);};
  454.     if (chksqr(prow-2,pcol+1)){cellmove(prow-2,pcol+1);waddch(boardwin,mark);};
  455.     if (chksqr(prow-2,pcol-1)){cellmove(prow-2,pcol-1);waddch(boardwin,mark);};
  456.     if (chksqr(prow+1,pcol+2)){cellmove(prow+1,pcol+2);waddch(boardwin,mark);};
  457.     if (chksqr(prow+1,pcol-2)){cellmove(prow+1,pcol-2);waddch(boardwin,mark);};
  458.     if (chksqr(prow-1,pcol+2)){cellmove(prow-1,pcol+2);waddch(boardwin,mark);};
  459.     if (chksqr(prow-1,pcol-2)){cellmove(prow-1,pcol-2);waddch(boardwin,mark);};
  460. }
  461.  
  462. static void drawmove(char tchar, int oldy, int oldx, int row, int column)
  463. /* place the stars, update board & currents */
  464. {
  465.     if (movecount <= 1)
  466.     {
  467.     int i, j;
  468.  
  469.     for (i = 0; i < BDEPTH; i++)
  470.         for (j = 0; j < BWIDTH; j++)
  471.         {
  472.         cellmove(i, j);
  473.         if (winch(boardwin) == minus)
  474.             waddch(boardwin, movecount ? ' ' : minus);
  475.         }
  476.     }
  477.     else
  478.     {
  479.     cellmove(oldy, oldx);
  480.     waddch(boardwin, '\b');
  481.     waddch(boardwin, tchar);
  482.     waddch(boardwin, tchar);
  483.     waddch(boardwin, tchar);
  484.     mark_possibles(oldy, oldx, ' ');
  485.     }
  486.  
  487.     if (row != -1 && column != -1)
  488.     {
  489.     cellmove(row, column);
  490.     waddch(boardwin, '\b');
  491.     waddch(boardwin, trail);
  492.     waddch(boardwin, trail);
  493.     waddch(boardwin, trail);
  494.     mark_possibles(row, column, minus);
  495.     board[row][column] = TRUE;
  496.     }
  497.  
  498.     wprintw(msgwin, "\nMove %d", movecount);
  499. }
  500.  
  501. static bool evalmove(int row, int column)
  502. /* evaluate move */
  503. {
  504.     if (movecount == 1)
  505.     return(TRUE);
  506.     else if (board[row][column] == TRUE)
  507.     {
  508.     waddstr(msgwin, "\nYou've already been there.");
  509.     return(FALSE);
  510.     }
  511.     else
  512.     {
  513.     int    rdif = iabs(row  - history[movecount-1].y);
  514.     int    cdif = iabs(column - history[movecount-1].x);
  515.  
  516.     if (!((rdif == 1) && (cdif == 2)) && !((rdif == 2) && (cdif == 1)))
  517.     {
  518.         waddstr(msgwin, "\nThat's not a legal knight's move.");
  519.         return(FALSE);
  520.     }
  521.     }
  522.  
  523.     return(TRUE);
  524. }
  525.  
  526. static bool chkmoves (void)
  527. /* check to see if valid moves are available */
  528. {
  529.     if (chksqr(rw+2,col+1)) return(TRUE);
  530.     if (chksqr(rw+2,col-1)) return(TRUE);
  531.     if (chksqr(rw-2,col+1)) return(TRUE);
  532.     if (chksqr(rw-2,col-1)) return(TRUE);
  533.     if (chksqr(rw+1,col+2)) return(TRUE);
  534.     if (chksqr(rw+1,col-2)) return(TRUE);
  535.     if (chksqr(rw-1,col+2)) return(TRUE);
  536.     if (chksqr(rw-1,col-2)) return(TRUE);
  537.     return (FALSE);
  538. }
  539.  
  540. static int iabs(int num)
  541. {
  542.     if (num < 0) return (-num);
  543.         else return (num);
  544. }
  545.  
  546. static bool chksqr (int r1, int c1)
  547. {
  548.     if ((r1 < 0) || (r1 > BDEPTH - 1))
  549.     return(FALSE);
  550.     if ((c1 < 0) || (c1 > BWIDTH - 1))
  551.     return(FALSE);
  552.     return (!board[r1][c1]);
  553. }
  554.  
  555. /* knight.c ends here */
  556.