+ (void) waddstr(helpwin, "center key. Use F/B to review the path.\n");
+
+ MvWAddStr(helpwin, NOTIFYY - INSTRY, 0,
+ "Press `?' to go to game explanation");
+}
+
+static void
+show_help(bool * keyhelp)
+{
+ werase(helpwin);
+ if (*keyhelp) {
+ help1();
+ *keyhelp = FALSE;
+ } else {
+ help2();
+ *keyhelp = TRUE;
+ }
+ wrefresh(helpwin);
+}
+
+static inline bool
+isValidYX(int y, int x)
+{
+ return (y >= 0 && y < ylimit && x >= 0 && x < xlimit) ? TRUE : FALSE;
+}
+
+static inline bool
+isUnusedYX(SQUARES squares, int y, int x)
+{
+ return (isValidYX(y, x) && (!squares[y][x]) ? TRUE : FALSE);
+}
+
+static bool
+boardIsFilled(SQUARES squares, int y, int x)
+{
+ unsigned n;
+
+ for (n = 0; n < MAX_OFFSET; n++) {
+ if (isUnusedYX(squares, y + offsets[n].y, x + offsets[n].x)) {
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+
+static void
+drawBoard(void)
+{
+ int i, j;
+
+ MvAddStr(0, 20, "KNIGHT'S MOVE -- a logical solitaire");
+
+ move(BOARDY, BOARDX);
+ waddch(boardwin, ACS_ULCORNER);
+ for (j = 0; j < (ylimit - 1); j++) {
+ waddch(boardwin, ACS_HLINE);
+ waddch(boardwin, ACS_HLINE);
+ waddch(boardwin, ACS_HLINE);
+ waddch(boardwin, ACS_TTEE);
+ }
+ waddch(boardwin, ACS_HLINE);
+ waddch(boardwin, ACS_HLINE);
+ waddch(boardwin, ACS_HLINE);
+ waddch(boardwin, ACS_URCORNER);
+
+ for (i = 1; i < ylimit; i++) {
+ move(BOARDY + i * 2 - 1, BOARDX);
+ waddch(boardwin, ACS_VLINE);
+ for (j = 0; j < xlimit; j++) {
+ waddch(boardwin, ' ');
+ waddch(boardwin, ' ');
+ waddch(boardwin, ' ');
+ waddch(boardwin, ACS_VLINE);
+ }
+ move(BOARDY + i * 2, BOARDX);
+ waddch(boardwin, ACS_LTEE);
+ for (j = 0; j < xlimit - 1; j++) {
+ waddch(boardwin, ACS_HLINE);
+ waddch(boardwin, ACS_HLINE);
+ waddch(boardwin, ACS_HLINE);
+ waddch(boardwin, ACS_PLUS);
+ }
+ waddch(boardwin, ACS_HLINE);
+ waddch(boardwin, ACS_HLINE);
+ waddch(boardwin, ACS_HLINE);
+ waddch(boardwin, ACS_RTEE);
+ }
+
+ move(BOARDY + i * 2 - 1, BOARDX);
+ waddch(boardwin, ACS_VLINE);
+ for (j = 0; j < xlimit; j++) {
+ waddch(boardwin, ' ');
+ waddch(boardwin, ' ');
+ waddch(boardwin, ' ');
+ waddch(boardwin, ACS_VLINE);
+ }
+
+ move(BOARDY + i * 2, BOARDX);
+ waddch(boardwin, ACS_LLCORNER);
+ for (j = 0; j < xlimit - 1; j++) {
+ waddch(boardwin, ACS_HLINE);
+ waddch(boardwin, ACS_HLINE);
+ waddch(boardwin, ACS_HLINE);
+ waddch(boardwin, ACS_BTEE);
+ }
+ waddch(boardwin, ACS_HLINE);
+ waddch(boardwin, ACS_HLINE);
+ waddch(boardwin, ACS_HLINE);
+ waddch(boardwin, ACS_LRCORNER);
+}
+
+static void
+mark_possibles(SQUARES squares, int y, int x, chtype mark)
+{
+ unsigned n;
+
+ for (n = 0; n < MAX_OFFSET; n++) {
+ if (isUnusedYX(squares, y + offsets[n].y, x + offsets[n].x)) {
+ cellmove(y + offsets[n].y, x + offsets[n].x);
+ waddch(boardwin, mark);
+ }
+ }
+}
+
+static bool
+find_next_move(SQUARES squares, HISTORY * doneData, int doneSize, int *y, int *x)
+{
+ bool result = FALSE;
+
+ if (doneSize > 1) {
+ unsigned j;
+ int oldy = doneData[doneSize - 1].y;
+ int oldx = doneData[doneSize - 1].x;
+ int found = -1;
+ int first = -1;
+ int next = -1;
+
+ for (j = 0; j < MAX_OFFSET * 2; j++) {
+ unsigned k = j % MAX_OFFSET;
+ int newy = oldy + offsets[k].y;
+ int newx = oldx + offsets[k].x;
+ if (isUnusedYX(squares, newy, newx)) {
+ if (first < 0)
+ first = (int) k;
+ if (newy == *y
+ && newx == *x) {
+ found = (int) k;
+ } else if (found >= 0) {
+ next = (int) k;
+ break;
+ }
+ }
+ }
+ if (found < 0)
+ next = first;
+ if (next >= 0) {
+ *y = oldy + offsets[next].y;
+ *x = oldx + offsets[next].x;
+ }
+ result = TRUE;
+ }
+ return result;
+}
+
+static void
+count_next_moves(SQUARES squares, int count_moves, int y, int x)
+{
+ int count = 0;
+ unsigned j;
+
+ wprintw(msgwin, "\nMove %d", count_moves);
+ for (j = 0; j < MAX_OFFSET; j++) {
+ int newy = y + offsets[j].y;
+ int newx = x + offsets[j].x;
+ if (isUnusedYX(squares, newy, newx)) {
+ ++count;
+ }
+ }
+ wprintw(msgwin, ", gives %d choices", count);
+ wclrtoeol(msgwin);
+}
+
+static void
+unmarkcell(int row, int column)
+{
+ cellmove(row, column);
+ waddch(boardwin, '\b');
+ waddch(boardwin, ' ');
+ waddch(boardwin, minus);
+ waddch(boardwin, ' ');
+}
+
+static void
+markcell(chtype tchar, int row, int column)
+{
+ cellmove(row, column);
+ waddch(boardwin, '\b');
+ waddch(boardwin, tchar);
+ waddch(boardwin, tchar);
+ waddch(boardwin, tchar);
+}
+
+static void
+drawMove(SQUARES squares, int count_moves, chtype tchar, int oldy, int oldx, int
+ row, int column)
+/* place the stars, update board & currents */
+{
+ if (count_moves <= 1) {
+ int i, j;
+
+ for (i = 0; i < ylimit; i++) {
+ for (j = 0; j < xlimit; j++) {
+ if (count_moves == 0) {
+ unmarkcell(i, j);
+ } else {
+ cellmove(i, j);
+ if (winch(boardwin) == minus)
+ waddch(boardwin, ' ');
+ }
+ }
+ }
+ } else {
+ markcell(tchar, oldy, oldx);
+ mark_possibles(squares, oldy, oldx, ' ');
+ }
+
+ if (row >= 0 && column >= 0) {
+ markcell(trail, row, column);
+ mark_possibles(squares, row, column, minus);
+ squares[row][column] = TRUE;
+ }
+
+ wprintw(msgwin, "\nMove %d", count_moves);
+ if (count_tries != count_moves)
+ wprintw(msgwin, " (%d tries)", count_tries);
+ wclrtoeol(msgwin);
+}
+
+static int
+iabs(int num)
+{
+ if (num < 0)
+ return (-num);
+ else
+ return (num);
+}
+
+static bool
+evaluate_move(SQUARES squares, HISTORY * doneData, int doneSize, int row, int column)
+{
+ if (doneSize <= 1)
+ return (TRUE);
+ else if (squares[row][column] == TRUE) {
+ waddstr(msgwin, "\nYou've already been there.");
+ return (FALSE);
+ } else {
+ int rdif = iabs(row - doneData[doneSize - 1].y);
+ int cdif = iabs(column - doneData[doneSize - 1].x);
+
+ if (!((rdif == 1) && (cdif == 2)) && !((rdif == 2) && (cdif == 1))) {
+ waddstr(msgwin, "\nThat's not a legal knight's move.");
+ return (FALSE);
+ }
+ }
+
+ return (TRUE);
+}
+
+static int
+completed(SQUARES squares)
+{
+ int i, j, count = 0;
+
+ for (i = 0; i < ylimit; i++) {
+ for (j = 0; j < xlimit; j++) {
+ if (squares[i][j] != 0) {
+ count += 1;
+ }
+ }
+ }
+ return ((count == maxmoves) ? -1 : count);
+}
+
+static void
+no_previous_move(void)
+{
+ waddstr(msgwin, "\nNo previous move.");
+ beep();
+}
+
+/* Recursively try all possible moves, starting from (y,x) */
+static int
+recurBack(SQUARES squares, int y, int x, int total)
+{
+ int longest = total;
+ int best_x = x;
+ int best_y = y;
+ int result;
+
+ if (total < maxmoves) {
+ unsigned k;
+
+ for (k = 0; k < MAX_OFFSET; k++) {
+ int try_x = x + offsets[k].x;
+ int try_y = y + offsets[k].y;
+ if (isUnusedYX(squares, try_y, try_x)) {
+ ++test_test;
+ squares[try_y][try_x] = total + 1;
+ result = recurBack(squares, try_y, try_x, total + 1);
+ if (result > longest) {
+ longest = result;
+ best_x = try_x;
+ best_y = try_y;
+ }
+ if (result >= maxmoves)
+ break;
+ squares[try_y][try_x] = 0; /* allow retry... */
+ }
+ }
+ }
+
+ result = total;
+ if (longest > total) {
+ result = longest;
+ squares[best_y][best_x] = total + 1;
+ (void) recurBack(squares, best_y, best_x, total + 1);
+ if (result < maxmoves)
+ squares[best_y][best_x] = 0;
+ }
+
+ return result;
+}