b1215fcc9d954bda9df593e87d437dcbc970147e
[ncurses.git] / test / knight.c
1 /****************************************************************************
2  * Copyright (c) 1998-2012,2013 Free Software Foundation, Inc.              *
3  *                                                                          *
4  * Permission is hereby granted, free of charge, to any person obtaining a  *
5  * copy of this software and associated documentation files (the            *
6  * "Software"), to deal in the Software without restriction, including      *
7  * without limitation the rights to use, copy, modify, merge, publish,      *
8  * distribute, distribute with modifications, sublicense, and/or sell       *
9  * copies of the Software, and to permit persons to whom the Software is    *
10  * furnished to do so, subject to the following conditions:                 *
11  *                                                                          *
12  * The above copyright notice and this permission notice shall be included  *
13  * in all copies or substantial portions of the Software.                   *
14  *                                                                          *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS  *
16  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF               *
17  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.   *
18  * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,   *
19  * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR    *
20  * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR    *
21  * THE USE OR OTHER DEALINGS IN THE SOFTWARE.                               *
22  *                                                                          *
23  * Except as contained in this notice, the name(s) of the above copyright   *
24  * holders shall not be used in advertising or otherwise to promote the     *
25  * sale, use or other dealings in this Software without prior written       *
26  * authorization.                                                           *
27  ****************************************************************************/
28 /*
29  * Knight's Tour - a brain game
30  *
31  * The original of this game was anonymous.  It had an unbelievably bogus
32  * interface, you actually had to enter square coordinates!  Redesign by
33  * Eric S. Raymond <esr@snark.thyrsus.com> July 22 1995.  Mouse support
34  * added September 20th 1995.
35  *
36  * $Id: knight.c,v 1.35 2013/02/03 00:16:59 tom Exp $
37  */
38
39 #include <test.priv.h>
40
41 /* board size */
42 #define BDEPTH  8
43 #define BWIDTH  8
44
45 /* where to start the instructions */
46 #define INSTRY  2
47 #define INSTRX  35
48
49 /* corner of board */
50 #define BOARDY  2
51 #define BOARDX  0
52
53 /* notification line */
54 #define NOTIFYY 21
55
56 /* virtual color values */
57 #define TRAIL_COLOR     1
58 #define PLUS_COLOR      2
59 #define MINUS_COLOR     3
60
61 #define CX(x)           (2 + 4 * (x))
62 #define CY(y)           (1 + 2 * (y))
63 #define cellmove(y, x)  wmove(boardwin, CY(y), CX(x))
64 #define CXINV(x)        (((x) - 1) / 4)
65 #define CYINV(y)        (((y) - 2) / 2)
66
67 typedef struct {
68     short x, y;
69 } cell;
70
71 static WINDOW *boardwin;        /* the board window */
72 static WINDOW *helpwin;         /* the help window */
73 static WINDOW *msgwin;          /* the message window */
74 static cell history[BDEPTH * BWIDTH + 1];       /* choice history */
75 static chtype minus = '-';      /* possible-move character */
76 static chtype oldch;
77 static chtype plus = '+';       /* cursor hot-spot character */
78 static chtype trail = '#';      /* trail character */
79 static int movecount;           /* count of moves so far */
80 static int trialcount;          /* count of trials so far */
81 static short board[BDEPTH][BWIDTH];     /* the squares */
82 /* *INDENT-OFF* */
83 static const struct {
84     int y;
85     int x;
86 } offsets[] = {
87     {  2,  1 },
88     {  1,  2 },
89     { -1,  2 },
90     { -2,  1 },
91     { -2, -1 },
92     { -1, -2 },
93     {  1, -2 },
94     {  2, -1 },
95 };
96 /* *INDENT-ON* */
97
98 static void
99 init_program(void)
100 {
101     setlocale(LC_ALL, "");
102
103     srand((unsigned) getpid());
104     initscr();
105     cbreak();                   /* immediate char return */
106     noecho();                   /* no immediate echo */
107     boardwin = newwin(BDEPTH * 2 + 1, BWIDTH * 4 + 1, BOARDY, BOARDX);
108     helpwin = newwin(0, 0, INSTRY, INSTRX);
109     msgwin = newwin(1, INSTRX - 1, NOTIFYY, 0);
110     scrollok(msgwin, TRUE);
111     keypad(boardwin, TRUE);
112
113     if (has_colors()) {
114         int bg = COLOR_BLACK;
115
116         start_color();
117 #if HAVE_USE_DEFAULT_COLORS
118         if (use_default_colors() == OK)
119             bg = -1;
120 #endif
121
122         (void) init_pair(TRAIL_COLOR, (short) COLOR_CYAN, (short) bg);
123         (void) init_pair(PLUS_COLOR, (short) COLOR_RED, (short) bg);
124         (void) init_pair(MINUS_COLOR, (short) COLOR_GREEN, (short) bg);
125
126         trail |= (chtype) COLOR_PAIR(TRAIL_COLOR);
127         plus |= (chtype) COLOR_PAIR(PLUS_COLOR);
128         minus |= (chtype) COLOR_PAIR(MINUS_COLOR);
129     }
130 #ifdef NCURSES_MOUSE_VERSION
131     (void) mousemask(BUTTON1_CLICKED, (mmask_t *) NULL);
132 #endif /* NCURSES_MOUSE_VERSION */
133
134     oldch = minus;
135 }
136
137 static void
138 help1(void)
139 /* game explanation -- initial help screen */
140 {
141     (void) waddstr(helpwin, "Knight's move is a solitaire puzzle.  Your\n");
142     (void) waddstr(helpwin, "objective is to visit each square of the  \n");
143     (void) waddstr(helpwin, "chessboard exactly once by making knight's\n");
144     (void) waddstr(helpwin, "moves (one square right or left followed  \n");
145     (void) waddstr(helpwin, "by two squares up or down, or two squares \n");
146     (void) waddstr(helpwin, "right or left followed by one square up or\n");
147     (void) waddstr(helpwin, "down).  You may start anywhere.\n\n");
148
149     (void) waddstr(helpwin, "Use arrow keys to move the cursor around.\n");
150     (void) waddstr(helpwin, "When you want to move your knight to the \n");
151     (void) waddstr(helpwin, "cursor location, press <space> or Enter.\n");
152     (void) waddstr(helpwin, "Illegal moves will be rejected with an  \n");
153     (void) waddstr(helpwin, "audible beep.\n\n");
154     (void) waddstr(helpwin, "The program will detect if you solve the\n");
155     (void) waddstr(helpwin, "puzzle; also inform you when you run out\n");
156     (void) waddstr(helpwin, "of legal moves.\n\n");
157
158     MvWAddStr(helpwin, NOTIFYY - INSTRY, 0,
159               "Press `?' to go to keystroke help.");
160 }
161
162 static void
163 help2(void)
164 /* keystroke help screen */
165 {
166     (void) waddstr(helpwin, "Possible moves are shown with `-'.\n\n");
167
168     (void) waddstr(helpwin, "You can move around with the arrow keys or\n");
169     (void) waddstr(helpwin, "with the rogue/hack movement keys.  Other\n");
170     (void) waddstr(helpwin, "commands allow you to undo moves or redraw.\n");
171     (void) waddstr(helpwin, "Your mouse may work; try left-button to\n");
172     (void) waddstr(helpwin, "move to the square under the pointer.\n\n");
173
174     (void) waddstr(helpwin, "x,q -- exit             y k u    7 8 9\n");
175     (void) waddstr(helpwin, "r -- redraw screen       \\|/      \\|/ \n");
176     (void) waddstr(helpwin, "bksp -- undo move       h-+-l    4-+-6\n");
177     (void) waddstr(helpwin, "a -- autojump            /|\\      /|\\ \n");
178     (void) waddstr(helpwin, "                        b j n    1 2 3\n");
179
180     (void) waddstr(helpwin, "\nYou can place your knight on the selected\n");
181     (void) waddstr(helpwin, "square with spacebar, Enter, or the keypad\n");
182     (void) waddstr(helpwin, "center key.  Use F/B to review the path.\n");
183
184     MvWAddStr(helpwin, NOTIFYY - INSTRY, 0,
185               "Press `?' to go to game explanation");
186 }
187
188 static void
189 show_help(bool * keyhelp)
190 {
191     werase(helpwin);
192     if (*keyhelp) {
193         help1();
194         *keyhelp = FALSE;
195     } else {
196         help2();
197         *keyhelp = TRUE;
198     }
199     wrefresh(helpwin);
200 }
201
202 static bool
203 chksqr(int r1, int c1)
204 {
205     if ((r1 < 0) || (r1 > BDEPTH - 1))
206         return (FALSE);
207     if ((c1 < 0) || (c1 > BWIDTH - 1))
208         return (FALSE);
209     return ((!board[r1][c1]) ? TRUE : FALSE);
210 }
211
212 static bool
213 chkmoves(int rw, int col)
214 /* check to see if valid moves are available */
215 {
216     unsigned n;
217
218     for (n = 0; n < SIZEOF(offsets); n++)
219         if (chksqr(rw + offsets[n].y, col + offsets[n].x))
220             return (TRUE);
221     return (FALSE);
222 }
223
224 static void
225 dosquares(void)
226 {
227     int i, j;
228
229     MvAddStr(0, 20, "KNIGHT'S MOVE -- a logical solitaire");
230
231     move(BOARDY, BOARDX);
232     waddch(boardwin, ACS_ULCORNER);
233     for (j = 0; j < 7; j++) {
234         waddch(boardwin, ACS_HLINE);
235         waddch(boardwin, ACS_HLINE);
236         waddch(boardwin, ACS_HLINE);
237         waddch(boardwin, ACS_TTEE);
238     }
239     waddch(boardwin, ACS_HLINE);
240     waddch(boardwin, ACS_HLINE);
241     waddch(boardwin, ACS_HLINE);
242     waddch(boardwin, ACS_URCORNER);
243
244     for (i = 1; i < BDEPTH; i++) {
245         move(BOARDY + i * 2 - 1, BOARDX);
246         waddch(boardwin, ACS_VLINE);
247         for (j = 0; j < BWIDTH; j++) {
248             waddch(boardwin, ' ');
249             waddch(boardwin, ' ');
250             waddch(boardwin, ' ');
251             waddch(boardwin, ACS_VLINE);
252         }
253         move(BOARDY + i * 2, BOARDX);
254         waddch(boardwin, ACS_LTEE);
255         for (j = 0; j < BWIDTH - 1; j++) {
256             waddch(boardwin, ACS_HLINE);
257             waddch(boardwin, ACS_HLINE);
258             waddch(boardwin, ACS_HLINE);
259             waddch(boardwin, ACS_PLUS);
260         }
261         waddch(boardwin, ACS_HLINE);
262         waddch(boardwin, ACS_HLINE);
263         waddch(boardwin, ACS_HLINE);
264         waddch(boardwin, ACS_RTEE);
265     }
266
267     move(BOARDY + i * 2 - 1, BOARDX);
268     waddch(boardwin, ACS_VLINE);
269     for (j = 0; j < BWIDTH; j++) {
270         waddch(boardwin, ' ');
271         waddch(boardwin, ' ');
272         waddch(boardwin, ' ');
273         waddch(boardwin, ACS_VLINE);
274     }
275
276     move(BOARDY + i * 2, BOARDX);
277     waddch(boardwin, ACS_LLCORNER);
278     for (j = 0; j < BWIDTH - 1; j++) {
279         waddch(boardwin, ACS_HLINE);
280         waddch(boardwin, ACS_HLINE);
281         waddch(boardwin, ACS_HLINE);
282         waddch(boardwin, ACS_BTEE);
283     }
284     waddch(boardwin, ACS_HLINE);
285     waddch(boardwin, ACS_HLINE);
286     waddch(boardwin, ACS_HLINE);
287     waddch(boardwin, ACS_LRCORNER);
288 }
289
290 static void
291 mark_possibles(int prow, int pcol, chtype mark)
292 {
293     unsigned n;
294
295     for (n = 0; n < SIZEOF(offsets); n++) {
296         if (chksqr(prow + offsets[n].y, pcol + offsets[n].x)) {
297             cellmove(prow + offsets[n].y, pcol + offsets[n].x);
298             waddch(boardwin, mark);
299         }
300     }
301 }
302
303 static bool
304 find_next_move(int *y, int *x)
305 {
306     unsigned j, k;
307     int found = -1;
308     int first = -1;
309     int next = -1;
310     int oldy, oldx;
311     int newy, newx;
312     bool result = FALSE;
313
314     if (movecount > 1) {
315         oldy = history[movecount - 1].y;
316         oldx = history[movecount - 1].x;
317         for (j = 0; j < SIZEOF(offsets) * 2; j++) {
318             k = j % SIZEOF(offsets);
319             newy = oldy + offsets[k].y;
320             newx = oldx + offsets[k].x;
321             if (chksqr(newy, newx)) {
322                 if (first < 0)
323                     first = (int) k;
324                 if (newy == *y
325                     && newx == *x) {
326                     found = (int) k;
327                 } else if (found >= 0) {
328                     next = (int) k;
329                     break;
330                 }
331             }
332         }
333         if (found < 0)
334             next = first;
335         if (next >= 0) {
336             *y = oldy + offsets[next].y;
337             *x = oldx + offsets[next].x;
338         }
339         result = TRUE;
340     }
341     return result;
342 }
343
344 static void
345 count_next_moves(int y, int x)
346 {
347     int count = 0;
348     unsigned j;
349
350     wprintw(msgwin, "\nMove %d", movecount);
351     for (j = 0; j < SIZEOF(offsets); j++) {
352         int newy = y + offsets[j].y;
353         int newx = x + offsets[j].x;
354         if (chksqr(newy, newx)) {
355             ++count;
356         }
357     }
358     wprintw(msgwin, ", gives %d choices", count);
359     wclrtoeol(msgwin);
360 }
361
362 static void
363 unmarkcell(int row, int column)
364 {
365     cellmove(row, column);
366     waddch(boardwin, '\b');
367     waddch(boardwin, ' ');
368     waddch(boardwin, minus);
369     waddch(boardwin, ' ');
370 }
371
372 static void
373 markcell(chtype tchar, int row, int column)
374 {
375     cellmove(row, column);
376     waddch(boardwin, '\b');
377     waddch(boardwin, tchar);
378     waddch(boardwin, tchar);
379     waddch(boardwin, tchar);
380 }
381
382 static void
383 drawmove(chtype tchar, int oldy, int oldx, int row, int column)
384 /* place the stars, update board & currents */
385 {
386     if (movecount <= 1) {
387         int i, j;
388
389         for (i = 0; i < BDEPTH; i++) {
390             for (j = 0; j < BWIDTH; j++) {
391                 if (movecount == 0) {
392                     unmarkcell(i, j);
393                 } else {
394                     cellmove(i, j);
395                     if (winch(boardwin) == minus)
396                         waddch(boardwin, movecount ? ' ' : minus);
397                 }
398             }
399         }
400     } else {
401         markcell(tchar, oldy, oldx);
402         mark_possibles(oldy, oldx, ' ');
403     }
404
405     if (row >= 0 && column >= 0) {
406         markcell(trail, row, column);
407         mark_possibles(row, column, minus);
408         board[row][column] = TRUE;
409     }
410
411     wprintw(msgwin, "\nMove %d", movecount);
412     if (trialcount != movecount)
413         wprintw(msgwin, " (%d tries)", trialcount);
414     wclrtoeol(msgwin);
415 }
416
417 static int
418 iabs(int num)
419 {
420     if (num < 0)
421         return (-num);
422     else
423         return (num);
424 }
425
426 static bool
427 evalmove(int row, int column)
428 /* evaluate move */
429 {
430     if (movecount == 1)
431         return (TRUE);
432     else if (board[row][column] == TRUE) {
433         waddstr(msgwin, "\nYou've already been there.");
434         return (FALSE);
435     } else {
436         int rdif = iabs(row - history[movecount - 1].y);
437         int cdif = iabs(column - history[movecount - 1].x);
438
439         if (!((rdif == 1) && (cdif == 2)) && !((rdif == 2) && (cdif == 1))) {
440             waddstr(msgwin, "\nThat's not a legal knight's move.");
441             return (FALSE);
442         }
443     }
444
445     return (TRUE);
446 }
447
448 static int
449 completed(void)
450 {
451     int i, j, count = 0;
452
453     for (i = 0; i < BDEPTH; i++)
454         for (j = 0; j < BWIDTH; j++)
455             if (board[i][j] != 0)
456                 count += 1;
457     return (count == (BWIDTH * BDEPTH) ? -1 : count);
458 }
459
460 static void
461 no_previous_move(void)
462 {
463     waddstr(msgwin, "\nNo previous move.");
464     beep();
465 }
466
467 static void
468 play(void)
469 /* play the game */
470 {
471     bool keyhelp;               /* TRUE if keystroke help is up */
472     int i, j, count;
473     int lastcol = 0;            /* last location visited */
474     int lastrow = 0;
475     int ny = 0, nx = 0;
476     int review = 0;             /* review history */
477     int rw = 0, col = 0;        /* current row and column */
478
479     do {
480         /* clear screen and draw board */
481         werase(boardwin);
482         werase(helpwin);
483         werase(msgwin);
484         dosquares();
485         help1();
486         wnoutrefresh(stdscr);
487         wnoutrefresh(helpwin);
488         wnoutrefresh(msgwin);
489         wnoutrefresh(boardwin);
490         doupdate();
491
492         movecount = 0;
493         for (i = 0; i < BDEPTH; i++) {
494             for (j = 0; j < BWIDTH; j++) {
495                 board[i][j] = FALSE;
496                 unmarkcell(i, j);
497             }
498         }
499         memset(history, 0, sizeof(history));
500         history[0].y = history[0].x = -1;
501         history[1].y = history[1].x = -1;
502         lastrow = lastcol = -2;
503         movecount = 1;
504         trialcount = 1;
505         keyhelp = FALSE;
506         show_help(&keyhelp);
507
508         for (;;) {
509             if (rw != lastrow || col != lastcol) {
510                 if (lastrow >= 0 && lastcol >= 0) {
511                     cellmove(lastrow, lastcol);
512                     if (board[lastrow][lastcol])
513                         waddch(boardwin, trail);
514                     else
515                         waddch(boardwin, oldch);
516                 }
517
518                 cellmove(rw, col);
519                 oldch = winch(boardwin);
520
521                 lastrow = rw;
522                 lastcol = col;
523             }
524             cellmove(rw, col);
525             waddch(boardwin, plus);
526             cellmove(rw, col);
527
528             wrefresh(msgwin);
529
530             switch (wgetch(boardwin)) {
531             case 'k':
532             case '8':
533             case KEY_UP:
534                 ny = rw + BDEPTH - 1;
535                 nx = col;
536                 break;
537             case 'j':
538             case '2':
539             case KEY_DOWN:
540                 ny = rw + 1;
541                 nx = col;
542                 break;
543             case 'h':
544             case '4':
545             case KEY_LEFT:
546                 ny = rw;
547                 nx = col + BWIDTH - 1;
548                 break;
549             case 'l':
550             case '6':
551             case KEY_RIGHT:
552                 ny = rw;
553                 nx = col + 1;
554                 break;
555             case 'y':
556             case '7':
557             case KEY_A1:
558                 ny = rw + BDEPTH - 1;
559                 nx = col + BWIDTH - 1;
560                 break;
561             case 'b':
562             case '1':
563             case KEY_C1:
564                 ny = rw + 1;
565                 nx = col + BWIDTH - 1;
566                 break;
567             case 'u':
568             case '9':
569             case KEY_A3:
570                 ny = rw + BDEPTH - 1;
571                 nx = col + 1;
572                 break;
573             case 'n':
574             case '3':
575             case KEY_C3:
576                 ny = rw + 1;
577                 nx = col + 1;
578                 break;
579
580 #ifdef NCURSES_MOUSE_VERSION
581             case KEY_MOUSE:
582                 {
583                     MEVENT myevent;
584
585                     getmouse(&myevent);
586                     if (myevent.y >= CY(0) && myevent.y <= CY(BDEPTH)
587                         && myevent.x >= CX(0) && myevent.x <= CX(BWIDTH)) {
588                         nx = CXINV(myevent.x);
589                         ny = CYINV(myevent.y);
590                         ungetch('\n');
591                         break;
592                     } else {
593                         beep();
594                         continue;
595                     }
596                 }
597 #endif /* NCURSES_MOUSE_VERSION */
598
599             case KEY_B2:
600             case '\n':
601             case ' ':
602                 review = 0;
603                 if (evalmove(rw, col)) {
604                     drawmove(trail,
605                              history[movecount - 1].y,
606                              history[movecount - 1].x,
607                              rw, col);
608                     history[movecount].y = (short) rw;
609                     history[movecount].x = (short) col;
610                     movecount++;
611                     trialcount++;
612
613                     if (!chkmoves(rw, col)) {
614                         if (completed() < 0) {
615                             waddstr(msgwin, "\nYou won.");
616                         } else {
617                             waddstr(msgwin,
618                                     "\nNo further moves are possible.");
619                         }
620                     }
621                 } else {
622                     beep();
623                 }
624                 break;
625
626             case KEY_UNDO:
627             case KEY_BACKSPACE:
628             case '\b':
629                 review = 0;
630                 if (movecount <= 0) {
631                     no_previous_move();
632                 } else if (movecount <= 1) {
633                     ny = history[movecount].y;
634                     nx = history[movecount].x;
635                     if (nx < 0 || ny < 0) {
636                         ny = (lastrow >= 0) ? lastrow : 0;
637                         nx = (lastcol >= 0) ? lastcol : 0;
638                     }
639                     movecount = 0;
640                     board[ny][nx] = FALSE;
641                     oldch = minus;
642                     drawmove(' ', ny, nx, -1, -1);
643                     movecount = 1;
644                     trialcount = 1;
645                     no_previous_move();
646                 } else {
647                     int oldy = history[movecount - 1].y;
648                     int oldx = history[movecount - 1].x;
649
650                     if (!board[rw][col]) {
651                         cellmove(rw, col);
652                         waddch(boardwin, ' ');
653                     }
654
655                     board[oldy][oldx] = FALSE;
656                     --movecount;
657                     ny = history[movecount - 1].y;
658                     nx = history[movecount - 1].x;
659                     if (nx < 0 || ny < 0) {
660                         ny = oldy;
661                         nx = oldx;
662                     }
663                     drawmove(' ', oldy, oldx, ny, nx);
664
665                     /* avoid problems if we just changed the current cell */
666                     cellmove(lastrow, lastcol);
667                     oldch = winch(boardwin);
668                 }
669                 break;
670
671             case 'a':
672                 nx = col;
673                 ny = rw;
674                 if (find_next_move(&ny, &nx))
675                     count_next_moves(ny, nx);
676                 else
677                     beep();
678                 break;
679
680             case 'F':
681                 if (review > 0) {
682                     review--;
683                     ny = history[movecount - review - 1].y;
684                     nx = history[movecount - review - 1].x;
685                 } else {
686                     beep();
687                 }
688                 break;
689
690             case 'B':
691                 if (review < movecount - 2) {
692                     review++;
693                     ny = history[movecount - review - 1].y;
694                     nx = history[movecount - review - 1].x;
695                 } else {
696                     beep();
697                 }
698                 break;
699
700             case KEY_REDO:
701             case '\f':
702             case 'r':
703                 clearok(curscr, TRUE);
704                 wnoutrefresh(stdscr);
705                 wnoutrefresh(boardwin);
706                 wnoutrefresh(msgwin);
707                 wnoutrefresh(helpwin);
708                 doupdate();
709                 break;
710
711             case 'q':
712             case 'x':
713                 goto dropout;
714
715             case '?':
716                 show_help(&keyhelp);
717                 break;
718
719             default:
720                 beep();
721                 break;
722             }
723
724             col = nx % BWIDTH;
725             rw = ny % BDEPTH;
726         }
727
728       dropout:
729         if ((count = completed()) < 0)
730             wprintw(msgwin, "\nYou won.  Care to try again? ");
731         else
732             wprintw(msgwin, "\n%d squares filled.  Try again? ", count);
733         wclrtoeol(msgwin);
734     } while
735         (tolower(wgetch(msgwin)) == 'y');
736 }
737
738 int
739 main(int argc GCC_UNUSED, char *argv[]GCC_UNUSED)
740 {
741     init_program();
742
743     play();
744
745     endwin();
746     ExitProgram(EXIT_SUCCESS);
747 }
748
749 /* knight.c ends here */