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.26 2002/10/19 22:11:24 tom Exp $
12 #include <test.priv.h>
18 /* where to start the instructions */
26 /* notification line */
29 /* virtual color values */
34 #define CX(x) (2 + 4 * (x))
35 #define CY(y) (1 + 2 * (y))
36 #define cellmove(y, x) wmove(boardwin, CY(y), CX(x))
37 #define CXINV(x) (((x) - 1) / 4)
38 #define CYINV(y) (((y) - 2) / 2)
44 static WINDOW
*boardwin
; /* the board window */
45 static WINDOW
*helpwin
; /* the help window */
46 static WINDOW
*msgwin
; /* the message window */
47 static cell history
[BDEPTH
* BWIDTH
+ 1]; /* choice history */
48 static chtype minus
= '-'; /* possible-move character */
50 static chtype plus
= '+'; /* cursor hot-spot character */
51 static chtype trail
= '#'; /* trail character */
52 static int movecount
; /* count of moves so far */
53 static int trialcount
; /* count of trials so far */
54 static short board
[BDEPTH
][BWIDTH
]; /* the squares */
74 setlocale(LC_ALL
, "");
76 srand((unsigned) getpid());
78 cbreak(); /* immediate char return */
79 noecho(); /* no immediate echo */
80 boardwin
= newwin(BDEPTH
* 2 + 1, BWIDTH
* 4 + 1, BOARDY
, BOARDX
);
81 helpwin
= newwin(0, 0, INSTRY
, INSTRX
);
82 msgwin
= newwin(1, INSTRX
- 1, NOTIFYY
, 0);
83 scrollok(msgwin
, TRUE
);
84 keypad(boardwin
, TRUE
);
90 #if HAVE_USE_DEFAULT_COLORS
91 if (use_default_colors() == OK
)
95 (void) init_pair(TRAIL_COLOR
, COLOR_CYAN
, bg
);
96 (void) init_pair(PLUS_COLOR
, COLOR_RED
, bg
);
97 (void) init_pair(MINUS_COLOR
, COLOR_GREEN
, bg
);
99 trail
|= COLOR_PAIR(TRAIL_COLOR
);
100 plus
|= COLOR_PAIR(PLUS_COLOR
);
101 minus
|= COLOR_PAIR(MINUS_COLOR
);
103 #ifdef NCURSES_MOUSE_VERSION
104 (void) mousemask(BUTTON1_CLICKED
, (mmask_t
*) NULL
);
105 #endif /* NCURSES_MOUSE_VERSION */
112 /* game explanation -- initial help screen */
114 (void) waddstr(helpwin
, "Knight's move is a solitaire puzzle. Your\n");
115 (void) waddstr(helpwin
, "objective is to visit each square of the \n");
116 (void) waddstr(helpwin
, "chessboard exactly once by making knight's\n");
117 (void) waddstr(helpwin
, "moves (one square right or left followed \n");
118 (void) waddstr(helpwin
, "by two squares up or down, or two squares \n");
119 (void) waddstr(helpwin
, "right or left followed by one square up or\n");
120 (void) waddstr(helpwin
, "down). You may start anywhere.\n\n");
122 (void) waddstr(helpwin
, "Use arrow keys to move the cursor around.\n");
123 (void) waddstr(helpwin
, "When you want to move your knight to the \n");
124 (void) waddstr(helpwin
, "cursor location, press <space> or Enter.\n");
125 (void) waddstr(helpwin
, "Illegal moves will be rejected with an \n");
126 (void) waddstr(helpwin
, "audible beep.\n\n");
127 (void) waddstr(helpwin
, "The program will detect if you solve the\n");
128 (void) waddstr(helpwin
, "puzzle; also inform you when you run out\n");
129 (void) waddstr(helpwin
, "of legal moves.\n\n");
131 (void) mvwaddstr(helpwin
, NOTIFYY
- INSTRY
, 0,
132 "Press `?' to go to keystroke help.");
137 /* keystroke help screen */
139 (void) waddstr(helpwin
, "Possible moves are shown with `-'.\n\n");
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");
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
, "bksp -- undo move h-+-l 4-+-6\n");
150 (void) waddstr(helpwin
, "a -- autojump /|\\ /|\\ \n");
151 (void) waddstr(helpwin
, " b j n 1 2 3\n");
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. Use F/B to review the path.\n");
157 (void) mvwaddstr(helpwin
, NOTIFYY
- INSTRY
, 0,
158 "Press `?' to go to game explanation");
162 show_help(bool * keyhelp
)
176 chksqr(int r1
, int c1
)
178 if ((r1
< 0) || (r1
> BDEPTH
- 1))
180 if ((c1
< 0) || (c1
> BWIDTH
- 1))
182 return ((!board
[r1
][c1
]) ? TRUE
: FALSE
);
186 chkmoves(int rw
, int col
)
187 /* check to see if valid moves are available */
191 for (n
= 0; n
< SIZEOF(offsets
); n
++)
192 if (chksqr(rw
+ offsets
[n
].y
, col
+ offsets
[n
].x
))
202 mvaddstr(0, 20, "KNIGHT'S MOVE -- a logical solitaire");
204 move(BOARDY
, BOARDX
);
205 waddch(boardwin
, ACS_ULCORNER
);
206 for (j
= 0; j
< 7; j
++) {
207 waddch(boardwin
, ACS_HLINE
);
208 waddch(boardwin
, ACS_HLINE
);
209 waddch(boardwin
, ACS_HLINE
);
210 waddch(boardwin
, ACS_TTEE
);
212 waddch(boardwin
, ACS_HLINE
);
213 waddch(boardwin
, ACS_HLINE
);
214 waddch(boardwin
, ACS_HLINE
);
215 waddch(boardwin
, ACS_URCORNER
);
217 for (i
= 1; i
< BDEPTH
; i
++) {
218 move(BOARDY
+ i
* 2 - 1, BOARDX
);
219 waddch(boardwin
, ACS_VLINE
);
220 for (j
= 0; j
< BWIDTH
; j
++) {
221 waddch(boardwin
, ' ');
222 waddch(boardwin
, ' ');
223 waddch(boardwin
, ' ');
224 waddch(boardwin
, ACS_VLINE
);
226 move(BOARDY
+ i
* 2, BOARDX
);
227 waddch(boardwin
, ACS_LTEE
);
228 for (j
= 0; j
< BWIDTH
- 1; j
++) {
229 waddch(boardwin
, ACS_HLINE
);
230 waddch(boardwin
, ACS_HLINE
);
231 waddch(boardwin
, ACS_HLINE
);
232 waddch(boardwin
, ACS_PLUS
);
234 waddch(boardwin
, ACS_HLINE
);
235 waddch(boardwin
, ACS_HLINE
);
236 waddch(boardwin
, ACS_HLINE
);
237 waddch(boardwin
, ACS_RTEE
);
240 move(BOARDY
+ i
* 2 - 1, BOARDX
);
241 waddch(boardwin
, ACS_VLINE
);
242 for (j
= 0; j
< BWIDTH
; j
++) {
243 waddch(boardwin
, ' ');
244 waddch(boardwin
, ' ');
245 waddch(boardwin
, ' ');
246 waddch(boardwin
, ACS_VLINE
);
249 move(BOARDY
+ i
* 2, BOARDX
);
250 waddch(boardwin
, ACS_LLCORNER
);
251 for (j
= 0; j
< BWIDTH
- 1; j
++) {
252 waddch(boardwin
, ACS_HLINE
);
253 waddch(boardwin
, ACS_HLINE
);
254 waddch(boardwin
, ACS_HLINE
);
255 waddch(boardwin
, ACS_BTEE
);
257 waddch(boardwin
, ACS_HLINE
);
258 waddch(boardwin
, ACS_HLINE
);
259 waddch(boardwin
, ACS_HLINE
);
260 waddch(boardwin
, ACS_LRCORNER
);
264 mark_possibles(int prow
, int pcol
, chtype mark
)
268 for (n
= 0; n
< SIZEOF(offsets
); n
++) {
269 if (chksqr(prow
+ offsets
[n
].y
, pcol
+ offsets
[n
].x
)) {
270 cellmove(prow
+ offsets
[n
].y
, pcol
+ offsets
[n
].x
);
271 waddch(boardwin
, mark
);
277 find_next_move(int *y
, int *x
)
287 oldy
= history
[movecount
- 1].y
;
288 oldx
= history
[movecount
- 1].x
;
289 for (j
= 0; j
< SIZEOF(offsets
) * 2; j
++) {
290 k
= j
% SIZEOF(offsets
);
291 newy
= oldy
+ offsets
[k
].y
;
292 newx
= oldx
+ offsets
[k
].x
;
293 if (chksqr(newy
, newx
)) {
299 } else if (found
>= 0) {
308 *y
= oldy
+ offsets
[next
].y
;
309 *x
= oldx
+ offsets
[next
].x
;
317 unmarkcell(int row
, int column
)
319 cellmove(row
, column
);
320 waddch(boardwin
, '\b');
321 waddch(boardwin
, ' ');
322 waddch(boardwin
, minus
);
323 waddch(boardwin
, ' ');
327 markcell(chtype tchar
, int row
, int column
)
329 cellmove(row
, column
);
330 waddch(boardwin
, '\b');
331 waddch(boardwin
, tchar
);
332 waddch(boardwin
, tchar
);
333 waddch(boardwin
, tchar
);
337 drawmove(chtype tchar
, int oldy
, int oldx
, int row
, int column
)
338 /* place the stars, update board & currents */
340 if (movecount
<= 1) {
343 for (i
= 0; i
< BDEPTH
; i
++) {
344 for (j
= 0; j
< BWIDTH
; j
++) {
345 if (movecount
== 0) {
349 if (winch(boardwin
) == minus
)
350 waddch(boardwin
, movecount
? ' ' : minus
);
355 markcell(tchar
, oldy
, oldx
);
356 mark_possibles(oldy
, oldx
, ' ');
359 if (row
!= -1 && column
!= -1) {
360 markcell(trail
, row
, column
);
361 mark_possibles(row
, column
, minus
);
362 board
[row
][column
] = TRUE
;
365 wprintw(msgwin
, "\nMove %d", movecount
);
366 if (trialcount
!= movecount
)
367 wprintw(msgwin
, " (%d tries)", trialcount
);
381 evalmove(int row
, int column
)
386 else if (board
[row
][column
] == TRUE
) {
387 waddstr(msgwin
, "\nYou've already been there.");
390 int rdif
= iabs(row
- history
[movecount
- 1].y
);
391 int cdif
= iabs(column
- history
[movecount
- 1].x
);
393 if (!((rdif
== 1) && (cdif
== 2)) && !((rdif
== 2) && (cdif
== 1))) {
394 waddstr(msgwin
, "\nThat's not a legal knight's move.");
407 for (i
= 0; i
< BDEPTH
; i
++)
408 for (j
= 0; j
< BWIDTH
; j
++)
409 if (board
[i
][j
] != 0)
411 return (count
== (BWIDTH
* BDEPTH
) ? -1 : count
);
415 no_previous_move(void)
417 waddstr(msgwin
, "\nNo previous move.");
425 bool keyhelp
; /* TRUE if keystroke help is up */
427 int lastcol
= 0; /* last location visited */
430 int review
= 0; /* review history */
431 int rw
= 0, col
= 0; /* current row and column */
434 /* clear screen and draw board */
440 wnoutrefresh(stdscr
);
441 wnoutrefresh(helpwin
);
442 wnoutrefresh(msgwin
);
443 wnoutrefresh(boardwin
);
447 for (i
= 0; i
< BDEPTH
; i
++) {
448 for (j
= 0; j
< BWIDTH
; j
++) {
453 memset(history
, 0, sizeof(history
));
454 history
[0].y
= history
[0].x
= -1;
455 history
[1].y
= history
[1].x
= -1;
456 lastrow
= lastcol
= -2;
463 if (rw
!= lastrow
|| col
!= lastcol
) {
464 if (lastrow
>= 0 && lastcol
>= 0) {
465 cellmove(lastrow
, lastcol
);
466 if (board
[lastrow
][lastcol
])
467 waddch(boardwin
, trail
);
469 waddch(boardwin
, oldch
);
473 oldch
= winch(boardwin
);
479 waddch(boardwin
, plus
);
484 switch (wgetch(boardwin
)) {
488 ny
= rw
+ BDEPTH
- 1;
501 nx
= col
+ BWIDTH
- 1;
512 ny
= rw
+ BDEPTH
- 1;
513 nx
= col
+ BWIDTH
- 1;
519 nx
= col
+ BWIDTH
- 1;
524 ny
= rw
+ BDEPTH
- 1;
534 #ifdef NCURSES_MOUSE_VERSION
540 if (myevent
.y
>= CY(0) && myevent
.y
<= CY(BDEPTH
)
541 && myevent
.x
>= CX(0) && myevent
.x
<= CX(BWIDTH
)) {
542 nx
= CXINV(myevent
.x
);
543 ny
= CYINV(myevent
.y
);
551 #endif /* NCURSES_MOUSE_VERSION */
557 if (evalmove(rw
, col
)) {
559 history
[movecount
- 1].y
,
560 history
[movecount
- 1].x
,
562 history
[movecount
].y
= rw
;
563 history
[movecount
].x
= col
;
567 if (!chkmoves(rw
, col
)) {
568 if (completed() < 0) {
569 waddstr(msgwin
, "\nYou won.");
572 "\nNo further moves are possible.");
584 if (movecount
<= 0) {
586 } else if (movecount
<= 1) {
587 ny
= history
[movecount
].y
;
588 nx
= history
[movecount
].x
;
589 if (nx
< 0 || ny
< 0) {
594 board
[ny
][nx
] = FALSE
;
596 drawmove(' ', ny
, nx
, -1, -1);
601 int oldy
= history
[movecount
- 1].y
;
602 int oldx
= history
[movecount
- 1].x
;
604 if (!board
[rw
][col
]) {
606 waddch(boardwin
, ' ');
609 board
[oldy
][oldx
] = FALSE
;
611 ny
= history
[movecount
- 1].y
;
612 nx
= history
[movecount
- 1].x
;
613 if (nx
< 0 || ny
< 0) {
617 drawmove(' ', oldy
, oldx
, ny
, nx
);
619 /* avoid problems if we just changed the current cell */
620 cellmove(lastrow
, lastcol
);
621 oldch
= winch(boardwin
);
628 find_next_move(&ny
, &nx
);
634 ny
= history
[movecount
- review
- 1].y
;
635 nx
= history
[movecount
- review
- 1].x
;
642 if (review
< movecount
- 2) {
644 ny
= history
[movecount
- review
- 1].y
;
645 nx
= history
[movecount
- review
- 1].x
;
654 clearok(curscr
, TRUE
);
655 wnoutrefresh(stdscr
);
656 wnoutrefresh(boardwin
);
657 wnoutrefresh(msgwin
);
658 wnoutrefresh(helpwin
);
680 if ((count
= completed()) < 0)
681 wprintw(msgwin
, "\nYou won. Care to try again? ");
683 wprintw(msgwin
, "\n%d squares filled. Try again? ", count
);
686 (tolower(wgetch(msgwin
)) == 'y');
690 main(int argc GCC_UNUSED
, char *argv
[]GCC_UNUSED
)
697 ExitProgram(EXIT_SUCCESS
);
700 /* knight.c ends here */