src/game.c

changeset 122
e65d9b5e9324
parent 117
ee539a9691f0
--- a/src/game.c	Tue May 19 17:59:24 2026 +0200
+++ b/src/game.c	Tue May 19 18:03:06 2026 +0200
@@ -185,7 +185,6 @@
 
     printw("\rFilename: ");
     clrtoeol();
-    refresh();
     
     char filename[64];
     int y = getcury(stdscr);
@@ -226,16 +225,10 @@
         
         if (asyncgetnstr(movestr, &bufpos, MOVESTR_BUFLEN)) {
             if (strncmp(movestr, "resign", MOVESTR_BUFLEN) == 0) {
-                gamestate->resign = 1;
-                printw("%s resigned!", curcolorstr);
-                clrtobot();
-                refresh();
+                gamestate->resign = true;
                 return 1;
             } else if (strncmp(movestr, "remis", MOVESTR_BUFLEN) == 0) {
-                gamestate->remis = 1;
-                printw("Game ends remis.");
-                clrtobot();
-                refresh();
+                gamestate->remis = true;
                 return 1;
             } else if (strncmp(movestr, "savepgn", MOVESTR_BUFLEN) == 0) {
                 save_pgn(gamestate, gameinfo);
@@ -247,12 +240,8 @@
                     if (result == VALID_MOVE_SEMANTICS) {
                         apply_move(gamestate, &move);
                         if (gamestate->checkmate) {
-                            printw("Checkmate!");
-                            clrtoeol();
                             return 1;
                         } else if (gamestate->stalemate) {
-                            printw("Stalemate!");
-                            clrtoeol();
                             return 1;
                         } else {
                             return 0;
@@ -323,13 +312,10 @@
                 resign_suggested = true;
                 break;
             case NETCODE_RESIGN:
-                gamestate->resign = 1;
-                printw("\rYour opponent resigned!");
-                clrtoeol();
+                gamestate->resign = true;
                 return 1;
             case NETCODE_CONNLOST:
-                printw("\rYour opponent has left the game.");
-                clrtoeol();
+                gamestate->ragequit = true;
                 return 1;
             case NETCODE_ERROR:
                 printw("\rCannot perform asynchronous network IO");
@@ -347,10 +333,7 @@
             bool was_premove = use_premove;
             use_premove = false;
             if (strncmp(movestr, "resign", MOVESTR_BUFLEN) == 0) {
-                gamestate->resign = 1;
-                printw("You resigned!");
-                clrtoeol();
-                refresh();
+                gamestate->resign = true;
                 net_send_code(opponent, NETCODE_RESIGN);
                 return 1;
             } else if (strncmp(movestr, "savepgn", MOVESTR_BUFLEN) == 0) {
@@ -358,10 +341,7 @@
             } else if (strncmp(movestr, "remis", MOVESTR_BUFLEN) == 0) {
                 if (remis_suggested) {
                     net_send_code(opponent, NETCODE_REMIS);
-                    gamestate->remis = 1;
-                    printw("\rRemis accepted!");
-                    clrtoeol();
-                    refresh();
+                    gamestate->remis = true;
                     return 1;
                 } if (!remis_rejected) {
                     net_send_code(opponent, NETCODE_REMIS);
@@ -369,15 +349,10 @@
                     refresh();
                     code = net_recieve_code(opponent);
                     if (code == NETCODE_ACCEPT) {
-                        gamestate->remis = 1;
-                        printw("\rRemis accepted!");
-                        clrtoeol();
-                        refresh();
+                        gamestate->remis = true;
                         return 1;
                     } else if (code == NETCODE_CONNLOST) {
-                        printw("\rYour opponent left the game.");
-                        clrtoeol();
-                        refresh();
+                        gamestate->ragequit = true;
                         return 1;
                     } else {
                         remis_rejected = true;
@@ -404,13 +379,7 @@
                             || code == NETCODE_CHECKMATE
                             || code == NETCODE_STALEMATE) {
                         apply_move(gamestate, &move);
-                        if (gamestate->checkmate) {
-                            printw("Checkmate!");
-                            clrtoeol();
-                            return 1;
-                        } else if (gamestate->stalemate) {
-                            printw("Stalemate!");
-                            clrtoeol();
+                        if (gamestate->checkmate || gamestate->stalemate) {
                             return 1;
                         } else {
                             return 0;
@@ -468,10 +437,7 @@
         /* allow the player to prepare a move */
         if (asyncgetnstr(movestr, &bufpos, MOVESTR_BUFLEN)) {
             if (strncmp(movestr, "resign", MOVESTR_BUFLEN) == 0) {
-                gamestate->resign = 1;
-                printw("You resigned!");
-                clrtoeol();
-                refresh();
+                gamestate->resign = true;
                 net_send_code(opponent, NETCODE_RESIGN);
                 return 1;
             } else if (strncmp(movestr, "taunt", MOVESTR_BUFLEN) == 0) {
@@ -505,26 +471,19 @@
             timecontrol(gamestate, gameinfo);
             return 1;
         case NETCODE_RESIGN:
-            gamestate->resign = 1;
-            printw("\rYour opponent resigned!");
-            clrtoeol();
+            gamestate->resign = true;
             return 1;
         case NETCODE_CONNLOST:
-            printw("\rYour opponent has left the game.");
-            clrtoeol();
+            gamestate->ragequit = true;
             return 1;
         case NETCODE_REMIS:
             if (remis_suggested) {
-                gamestate->remis = 1;
-                printw("\rRemis accepted!");
-                clrtoeol();
+                gamestate->remis = true;
                 return 1;
             } else {
                 if (prompt_yesno(
                     "\rYour opponent offers remis - do you accept")) {
-                    gamestate->remis = 1;
-                    printw("\rRemis accepted!");
-                    clrtoeol();
+                    gamestate->remis = true;
                     net_send_code(opponent, NETCODE_ACCEPT);
                     return 1;
                 } else {
@@ -540,13 +499,9 @@
                 apply_move(gamestate, &move);
                 if (gamestate->checkmate) {
                     net_send_code(opponent, NETCODE_CHECKMATE);
-                    printw("\rCheckmate!");
-                    clrtoeol();
                     return 1;
                 } else if (gamestate->stalemate) {
                     net_send_code(opponent, NETCODE_STALEMATE);
-                    printw("\rStalemate!");
-                    clrtoeol();
                     return 1;
                 } else if (move.check) {
                     net_send_code(opponent, NETCODE_CHECK);
@@ -575,31 +530,82 @@
 }
 
 void game_review(Settings* settings, GameState *gamestate) {
+    const unsigned page_moves = 10;
     GameInfo *gameinfo = &(settings->gameinfo);
+    GameState viewedstate = {0};
+    unsigned viewedmove = gamestate->movecount;
+    bool redraw = true;
 
-    move(0,0);
-    draw_board(gamestate, WHITE, settings->unicode);
-    
-    mvaddstr(getmaxy(stdscr)-1, 0,
-        "Press 'q' to quit or 's' to save a PGN file...");
-    refresh();
-    flushinp();
-    
     noecho();
     int c;
     do {
+        if (redraw) {
+            gamestate_cleanup(&viewedstate);
+            gamestate_at_move(gamestate, viewedmove, &viewedstate);
+
+            erase(); /* don't use clear() to avoid flickering */
+            draw_board(&viewedstate, WHITE, settings->unicode);
+            timecontrol(&viewedstate, gameinfo);
+
+            move(getmaxy(stdscr)-5, 0);
+            const char *curcolorstr =
+                gamestate->movecount % 2 == 0 ? "White" : "Black";
+            if (gamestate->resign) {
+                printw("%s resigned.\n", curcolorstr);
+            } else if (gamestate->remis) {
+                addstr("The game ended remis.\n");
+            } else if (gamestate->stalemate) {
+                addstr("The game ended in a stalemate.\n");
+            } else if (gamestate->checkmate) {
+                printw("%s has lost the game.\n", curcolorstr);
+            } else if (gamestate->ragequit) {
+                printw("Your opponent disconnected.\n");
+            }
+            addstr("\nPress 'q' to quit, 's' to save the position as PGN, or\n"
+                "arrow keys, home/end, page up/down to review the game.\n");
+            flushinp();
+            redraw = false;
+        }
         c = getch();
         if (c == 's') {
             addch('\r');
             echo();
-            save_pgn(gamestate, gameinfo);
-            addstr(" Press 'q' to quit...");
+            save_pgn(&viewedstate, gameinfo);
             noecho();
+            redraw = true;
+        } else if (c == KEY_UP || c == KEY_LEFT) {
+            if (viewedmove > 0) {
+                viewedmove--;
+                redraw = true;
+            }
+        } else if (c == KEY_DOWN || c == KEY_RIGHT) {
+            if (viewedmove < gamestate->movecount) {
+                viewedmove++;
+                redraw = true;
+            }
+        } else if (c == KEY_HOME) {
+            viewedmove = 0;
+            redraw = true;
+        } else if (c == KEY_END) {
+            viewedmove = gamestate->movecount;
+            redraw = true;
+        } else if (c == KEY_PPAGE) {
+            if (viewedmove > page_moves) {
+                viewedmove -= page_moves;
+            } else {
+                viewedmove = 0;
+            }
+            redraw = true;
+        } else if (c == KEY_NPAGE) {
+            viewedmove += page_moves;
+            if (viewedmove > gamestate->movecount) {
+                viewedmove = gamestate->movecount;
+            }
+            redraw = true;
         }
     } while (c != 'q');
     echo();
-    
-    gamestate_cleanup(gamestate);
+    gamestate_cleanup(&viewedstate);
 }
 
 void game_play_singlemachine(Settings *settings) {
@@ -642,6 +648,7 @@
     }  while (running);
     
     game_review(settings, &gamestate);
+    gamestate_cleanup(&gamestate);
 }
 
 void game_play(Settings *settings, GameState *gamestate, int opponent) {

mercurial