2 * Knight's Tour - a brain game
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.
9 * $Id: knight.c,v 1.18 2000/10/15 18:36:34 Brian.Raiter Exp $
12 #include <test.priv.h>
22 /* where to start the instructions */
30 /* notification line */
33 /* virtual color values */
38 #define CX(x) (2 + 4 * (x))
39 #define CY(y) (1 + 2 * (y))
40 #define cellmove(y, x) wmove(boardwin, CY(y), CX(x))
41 #define CXINV(x) (((x) - 1) / 4)
42 #define CYINV(y) (((y) - 2) / 2)
48 static short board[BDEPTH][BWIDTH]; /* the squares */
49 static int rw, col; /* current row and column */
50 static int lastrow, lastcol; /* last location visited */
51 static cell history[BDEPTH * BWIDTH + 1]; /* choice history */
52 static int movecount; /* count of moves so far */
53 static WINDOW *boardwin; /* the board window */
54 static WINDOW *helpwin; /* the help window */
55 static WINDOW *msgwin; /* the message window */
56 static chtype trail = '#'; /* trail character */
57 static chtype plus = '+'; /* cursor hot-spot character */
58 static chtype minus = '-'; /* possible-move character */
61 static void init(void);
62 static void play(void);
63 static void dosquares(void);
64 static void drawmove(char, int, int, int, int);
65 static bool evalmove(int, int);
66 static bool chkmoves(void);
67 static bool chksqr(int, int);
71 main(int argc GCC_UNUSED, char *argv[]GCC_UNUSED)
84 srand((unsigned) getpid());
86 cbreak(); /* immediate char return */
87 noecho(); /* no immediate echo */
88 boardwin = newwin(BDEPTH * 2 + 1, BWIDTH * 4 + 1, BOARDY, BOARDX);
89 helpwin = newwin(0, 0, INSTRY, INSTRX);
90 msgwin = newwin(1, INSTRX - 1, NOTIFYY, 0);
91 scrollok(msgwin, TRUE);
92 keypad(boardwin, TRUE);
98 #if HAVE_USE_DEFAULT_COLORS
99 if (use_default_colors() == OK)
103 (void) init_pair(TRAIL_COLOR, COLOR_CYAN, bg);
104 (void) init_pair(PLUS_COLOR, COLOR_RED, bg);
105 (void) init_pair(MINUS_COLOR, COLOR_GREEN, bg);
107 trail |= COLOR_PAIR(TRAIL_COLOR);
108 plus |= COLOR_PAIR(PLUS_COLOR);
109 minus |= COLOR_PAIR(MINUS_COLOR);
111 #ifdef NCURSES_MOUSE_VERSION
112 (void) mousemask(BUTTON1_CLICKED, (mmask_t *) NULL);
113 #endif /* NCURSES_MOUSE_VERSION */
120 /* game explanation -- initial help screen */
122 (void) waddstr(helpwin, "Knight's move is a solitaire puzzle. Your\n");
123 (void) waddstr(helpwin, "objective is to visit each square of the \n");
124 (void) waddstr(helpwin, "chessboard exactly once by making knight's\n");
125 (void) waddstr(helpwin, "moves (one square right or left followed \n");
126 (void) waddstr(helpwin, "by two squares up or down, or two squares \n");
127 (void) waddstr(helpwin, "right or left followed by one square up or\n");
128 (void) waddstr(helpwin, "down). You may start anywhere.\n\n");
130 (void) waddstr(helpwin, "Use arrow keys to move the cursor around.\n");
131 (void) waddstr(helpwin, "When you want to move your knight to the \n");
132 (void) waddstr(helpwin, "cursor location, press <space> or Enter.\n");
133 (void) waddstr(helpwin, "Illegal moves will be rejected with an \n");
134 (void) waddstr(helpwin, "audible beep.\n\n");
135 (void) waddstr(helpwin, "The program will detect if you solve the\n");
136 (void) waddstr(helpwin, "puzzle; also inform you when you run out\n");
137 (void) waddstr(helpwin, "of legal moves.\n\n");
139 (void) mvwaddstr(helpwin, NOTIFYY - INSTRY, 0,
140 "Press `?' to go to keystroke help.");
145 /* keystroke help screen */
147 (void) waddstr(helpwin, "Possible moves are shown with `-'.\n\n");
149 (void) waddstr(helpwin, "You can move around with the arrow keys or\n");
150 (void) waddstr(helpwin, "with the rogue/hack movement keys. Other\n");
151 (void) waddstr(helpwin, "commands allow you to undo moves or redraw.\n");
152 (void) waddstr(helpwin, "Your mouse may work; try left-button to\n");
153 (void) waddstr(helpwin, "move to the square under the pointer.\n\n");
155 (void) waddstr(helpwin, "x,q -- exit y k u 7 8 9\n");
156 (void) waddstr(helpwin, "r -- redraw screen \\|/ \\|/ \n");
157 (void) waddstr(helpwin, "bksp -- undo move h-+-l 4-+-6\n");
158 (void) waddstr(helpwin, " /|\\ /|\\ \n");
159 (void) waddstr(helpwin, " b j n 1 2 3\n");
161 (void) waddstr(helpwin, "\nYou can place your knight on the selected\n");
162 (void) waddstr(helpwin, "square with spacebar, Enter, or the keypad\n");
163 (void) waddstr(helpwin, "center key. You can quit with `x' or `q'.\n");
165 (void) mvwaddstr(helpwin, NOTIFYY - INSTRY, 0,
166 "Press `?' to go to game explanation");
173 bool keyhelp; /* TRUE if keystroke help is up */
174 int c, ny = 0, nx = 0;
178 /* clear screen and draw board */
184 wnoutrefresh(stdscr);
185 wnoutrefresh(helpwin);
186 wnoutrefresh(msgwin);
187 wnoutrefresh(boardwin);
190 for (i = 0; i < BDEPTH; i++)
191 for (j = 0; j < BWIDTH; j++) {
194 waddch(boardwin, minus);
196 memset(history, '\0', sizeof(history));
197 history[0].y = history[0].x = -1;
198 lastrow = lastcol = -2;
203 if (rw != lastrow || col != lastcol) {
204 if (lastrow >= 0 && lastcol >= 0) {
205 cellmove(lastrow, lastcol);
206 if (board[lastrow][lastcol])
207 waddch(boardwin, trail);
209 waddch(boardwin, oldch);
213 oldch = winch(boardwin);
219 waddch(boardwin, plus);
224 c = wgetch(boardwin);
232 ny = rw + BDEPTH - 1;
245 nx = col + BWIDTH - 1;
256 ny = rw + BDEPTH - 1;
257 nx = col + BWIDTH - 1;
263 nx = col + BWIDTH - 1;
268 ny = rw + BDEPTH - 1;
278 #ifdef NCURSES_MOUSE_VERSION
284 if (myevent.y >= CY(0) && myevent.y <= CY(BDEPTH)
285 && myevent.x >= CX(0) && myevent.x <= CX(BWIDTH)) {
286 nx = CXINV(myevent.x);
287 ny = CYINV(myevent.y);
295 #endif /* NCURSES_MOUSE_VERSION */
300 if (evalmove(rw, col)) {
302 history[movecount - 1].y,
303 history[movecount - 1].x,
305 history[movecount].y = rw;
306 history[movecount].x = col;
318 clearok(curscr, TRUE);
319 wnoutrefresh(stdscr);
320 wnoutrefresh(boardwin);
321 wnoutrefresh(msgwin);
322 wnoutrefresh(helpwin);
329 if (movecount == 1) {
332 waddstr(msgwin, "\nNo previous move.");
335 int oldy = history[movecount - 1].y;
336 int oldx = history[movecount - 1].x;
338 board[oldy][oldx] = FALSE;
340 ny = history[movecount - 1].y;
341 nx = history[movecount - 1].x;
342 drawmove(' ', oldy, oldx, ny, nx);
344 /* avoid problems if we just changed the current cell */
345 cellmove(lastrow, lastcol);
346 oldch = winch(boardwin);
377 for (i = 0; i < BDEPTH; i++)
378 for (j = 0; j < BWIDTH; j++)
379 if (board[i][j] != 0)
381 if (count == (BWIDTH * BDEPTH))
382 wprintw(msgwin, "\nYou won. Care to try again? ");
384 wprintw(msgwin, "\n%d squares filled. Try again? ", count);
386 (tolower(wgetch(msgwin)) == 'y');
394 mvaddstr(0, 20, "KNIGHT'S MOVE -- a logical solitaire");
396 move(BOARDY, BOARDX);
397 waddch(boardwin, ACS_ULCORNER);
398 for (j = 0; j < 7; j++) {
399 waddch(boardwin, ACS_HLINE);
400 waddch(boardwin, ACS_HLINE);
401 waddch(boardwin, ACS_HLINE);
402 waddch(boardwin, ACS_TTEE);
404 waddch(boardwin, ACS_HLINE);
405 waddch(boardwin, ACS_HLINE);
406 waddch(boardwin, ACS_HLINE);
407 waddch(boardwin, ACS_URCORNER);
409 for (i = 1; i < BDEPTH; i++) {
410 move(BOARDY + i * 2 - 1, BOARDX);
411 waddch(boardwin, ACS_VLINE);
412 for (j = 0; j < BWIDTH; j++) {
413 waddch(boardwin, ' ');
414 waddch(boardwin, ' ');
415 waddch(boardwin, ' ');
416 waddch(boardwin, ACS_VLINE);
418 move(BOARDY + i * 2, BOARDX);
419 waddch(boardwin, ACS_LTEE);
420 for (j = 0; j < BWIDTH - 1; j++) {
421 waddch(boardwin, ACS_HLINE);
422 waddch(boardwin, ACS_HLINE);
423 waddch(boardwin, ACS_HLINE);
424 waddch(boardwin, ACS_PLUS);
426 waddch(boardwin, ACS_HLINE);
427 waddch(boardwin, ACS_HLINE);
428 waddch(boardwin, ACS_HLINE);
429 waddch(boardwin, ACS_RTEE);
432 move(BOARDY + i * 2 - 1, BOARDX);
433 waddch(boardwin, ACS_VLINE);
434 for (j = 0; j < BWIDTH; j++) {
435 waddch(boardwin, ' ');
436 waddch(boardwin, ' ');
437 waddch(boardwin, ' ');
438 waddch(boardwin, ACS_VLINE);
441 move(BOARDY + i * 2, BOARDX);
442 waddch(boardwin, ACS_LLCORNER);
443 for (j = 0; j < BWIDTH - 1; j++) {
444 waddch(boardwin, ACS_HLINE);
445 waddch(boardwin, ACS_HLINE);
446 waddch(boardwin, ACS_HLINE);
447 waddch(boardwin, ACS_BTEE);
449 waddch(boardwin, ACS_HLINE);
450 waddch(boardwin, ACS_HLINE);
451 waddch(boardwin, ACS_HLINE);
452 waddch(boardwin, ACS_LRCORNER);
456 mark_possibles(int prow, int pcol, chtype mark)
458 if (chksqr(prow + 2, pcol + 1)) {
459 cellmove(prow + 2, pcol + 1);
460 waddch(boardwin, mark);
462 if (chksqr(prow + 2, pcol - 1)) {
463 cellmove(prow + 2, pcol - 1);
464 waddch(boardwin, mark);
466 if (chksqr(prow - 2, pcol + 1)) {
467 cellmove(prow - 2, pcol + 1);
468 waddch(boardwin, mark);
470 if (chksqr(prow - 2, pcol - 1)) {
471 cellmove(prow - 2, pcol - 1);
472 waddch(boardwin, mark);
474 if (chksqr(prow + 1, pcol + 2)) {
475 cellmove(prow + 1, pcol + 2);
476 waddch(boardwin, mark);
478 if (chksqr(prow + 1, pcol - 2)) {
479 cellmove(prow + 1, pcol - 2);
480 waddch(boardwin, mark);
482 if (chksqr(prow - 1, pcol + 2)) {
483 cellmove(prow - 1, pcol + 2);
484 waddch(boardwin, mark);
486 if (chksqr(prow - 1, pcol - 2)) {
487 cellmove(prow - 1, pcol - 2);
488 waddch(boardwin, mark);
493 drawmove(char tchar, int oldy, int oldx, int row, int column)
494 /* place the stars, update board & currents */
496 if (movecount <= 1) {
499 for (i = 0; i < BDEPTH; i++)
500 for (j = 0; j < BWIDTH; j++) {
502 if (winch(boardwin) == minus)
503 waddch(boardwin, movecount ? ' ' : minus);
506 cellmove(oldy, oldx);
507 waddch(boardwin, '\b');
508 waddch(boardwin, tchar);
509 waddch(boardwin, tchar);
510 waddch(boardwin, tchar);
511 mark_possibles(oldy, oldx, ' ');
514 if (row != -1 && column != -1) {
515 cellmove(row, column);
516 waddch(boardwin, '\b');
517 waddch(boardwin, trail);
518 waddch(boardwin, trail);
519 waddch(boardwin, trail);
520 mark_possibles(row, column, minus);
521 board[row][column] = TRUE;
524 wprintw(msgwin, "\nMove %d", movecount);
528 evalmove(int row, int column)
533 else if (board[row][column] == TRUE) {
534 waddstr(msgwin, "\nYou've already been there.");
537 int rdif = iabs(row - history[movecount - 1].y);
538 int cdif = iabs(column - history[movecount - 1].x);
540 if (!((rdif == 1) && (cdif == 2)) && !((rdif == 2) && (cdif == 1))) {
541 waddstr(msgwin, "\nThat's not a legal knight's move.");
551 /* check to see if valid moves are available */
553 if (chksqr(rw + 2, col + 1))
555 if (chksqr(rw + 2, col - 1))
557 if (chksqr(rw - 2, col + 1))
559 if (chksqr(rw - 2, col - 1))
561 if (chksqr(rw + 1, col + 2))
563 if (chksqr(rw + 1, col - 2))
565 if (chksqr(rw - 1, col + 2))
567 if (chksqr(rw - 1, col - 2))
582 chksqr(int r1, int c1)
584 if ((r1 < 0) || (r1 > BDEPTH - 1))
586 if ((c1 < 0) || (c1 > BWIDTH - 1))
588 return ((!board[r1][c1]) ? TRUE : FALSE);
591 /* knight.c ends here */