fix how agreeing on a draw works default tip

Mon, 24 Aug 2026 16:13:51 +0200

author
Mike Becker <universe@uap-core.de>
date
Mon, 24 Aug 2026 16:13:51 +0200
changeset 192
e57e2874fb2a
parent 191
6da16865a270

fix how agreeing on a draw works

fixes #980

PROTOCOL.md file | annotate | diff | comparison | revisions
src/main.c file | annotate | diff | comparison | revisions
src/network.h file | annotate | diff | comparison | revisions
--- a/PROTOCOL.md	Mon Aug 24 15:48:19 2026 +0200
+++ b/PROTOCOL.md	Mon Aug 24 16:13:51 2026 +0200
@@ -6,7 +6,7 @@
 This document describes the protocol in detail, including the message formats
 and the expected behavior of clients and the server.
 
-The current protocol version is 24.
+The current protocol version is 25.
 
 ## Message Format
 
@@ -166,6 +166,8 @@
 The game MAY be ended at any time by any (including the waiting) player by
 [resignation](#resign).
 
+The game MAY also be ended after by agreeing on a [draw](#offer-a-draw).
+
 #### Checkmate
 
 Whe the active player determines they were checkmated with the last move,
@@ -215,25 +217,13 @@
 
 ### Offer a Draw
 
-_TODO: draw offers by the active player are bugged - see issue #980_
-
-_TODO: the description is very confusing and should be cleaned up after fixing issue #980_
-
 Both the active and the waiting player MAY send a `REMIS` message any time to
 offer a draw.
 
-When the waiting player receives such an offer, they MAY choose to answer it
-with either an `ACCEPT` or a `DECLINE` message.
+When both players have sent a `REMIS` message to the other player within the
+same move, the draw is agreed upon and both players SHALL end the game.
 
-When the offer was accepted, both players SHALL end the game.
-Otherwise, they SHALL continue playing.
-When an offer was declined, the requesting player SHOULD NOT send another offer
-for the same position.
-
-When the active player receives such an offer, they MAY ignore it.
-No confirmation is sent.
-Instead, if the player chooses to accept the offer, they SHALL send a `REMIS`
-message on their own.
+Playing a move SHALL revoke any draw offers.
 
 ### Propose Resignation
 
--- a/src/main.c	Mon Aug 24 15:48:19 2026 +0200
+++ b/src/main.c	Mon Aug 24 16:13:51 2026 +0200
@@ -552,6 +552,7 @@
 
     size_t bufpos = 0;
     char movestr[MOVESTR_BUFLEN];
+    bool remis_offered = false;
     bool remis_rejected = false;
     bool remis_suggested = false;
     bool resign_suggested = false;
@@ -585,6 +586,8 @@
             printw("The opponent offers remis. Type remis to accept. \n\n");
         } else if (remis_rejected) {
             printw("Remis offer rejected.                            \n\n");
+        } else if (remis_offered) {
+            printw("Remis offer sent.                                \n\n");
         } else {
             printw("Or use a command: remis, resign, savepgn         \n\n");
         }
@@ -595,6 +598,10 @@
         code = net_recieve_code_async(opponent);
         switch (code) {
             case NETCODE_REMIS:
+                if (remis_offered) {
+                    gamestate->remis = true;
+                    return 1;
+                }
                 remis_suggested = true;
                 break;
             case NETCODE_TAUNT:
@@ -642,19 +649,9 @@
                     return 1;
                 } if (!remis_rejected) {
                     net_send_code(opponent, NETCODE_REMIS);
-                    printw("Remis offer sent - waiting for acceptance...");
-                    refresh();
-                    code = net_recieve_code(opponent);
-                    if (code == NETCODE_ACCEPT) {
-                        gamestate->remis = true;
-                        return 1;
-                    } else if (code == NETCODE_CONNLOST) {
-                        gamestate->ragequit = true;
-                        return 1;
-                    } else {
-                        remis_rejected = true;
-                    }
+                    remis_offered = true;
                 }
+                /* prevent spamming the remis message */
             } else if (movestr[0] == 0) {
                 /* ignore empty move strings and ask again */
             } else {
@@ -703,25 +700,31 @@
 
     size_t bufpos = 0;
     char movestr[MOVESTR_BUFLEN];
-    bool remis_suggested = false, resign_suggested = false;
+    bool remis_suggested = false, remis_offered = false;
+    bool resign_suggested = false;
     while (1) {
         timecontrol(gamestate);
 
         move(inputy, 0);
         printw("Waiting for opponent. Use chess notation to prepare a move.\n");
         if (*gamestate->premove) {
-            printw("Current pre-move: %s                             \n\n",
+            printw("Current pre-move: %s                             \n",
                 gamestate->premove);
         } else if (remis_suggested && !resign_suggested) {
-            printw("Suggested remis.                                 \n\n");
+            printw("Suggested remis.                                 \n");
         } else if (resign_suggested) {
             if (remis_suggested) {
-                printw("Suggested to resign or at least to accept remis. \n\n");
+                printw("Suggested to resign or at least to accept remis. \n");
             } else {
-                printw("Suggested to resign.                             \n\n");
+                printw("Suggested to resign.                             \n");
             }
         } else {
-            printw("Or use a command: remis, resign, taunt, savepgn  \n\n");
+            printw("Or use a command: remis, resign, taunt, savepgn  \n");
+        }
+        if (remis_offered) {
+            printw("Opponent offered remis. Type 'remis' to accept.\n");
+        } else {
+            printw("\n");
         }
         printw("Prepare your next move: ");
         clrtoeol();
@@ -741,8 +744,13 @@
                 resign_suggested = true;
                 net_send_code(opponent, NETCODE_TAUNT);
             } else if (strncmp(movestr, "remis", MOVESTR_BUFLEN) == 0) {
-                remis_suggested = true;
                 net_send_code(opponent, NETCODE_REMIS);
+                if (remis_offered) {
+                    gamestate->remis = true;
+                    return 1;
+                } else {
+                    remis_suggested = true;
+                }
             } else if (strncmp(movestr, "savepgn", MOVESTR_BUFLEN) == 0) {
                 save_pgn(gamestate);
             } else if (movestr[0] == 0) {
@@ -782,14 +790,7 @@
                 gamestate->remis = true;
                 return 1;
             } else {
-                if (prompt_yesno(
-                    "\rYour opponent offers remis - do you accept")) {
-                    gamestate->remis = true;
-                    net_send_code(opponent, NETCODE_ACCEPT);
-                    return 1;
-                } else {
-                    net_send_code(opponent, NETCODE_DECLINE);
-                }
+                remis_offered = true;
             }
             break;
         /* validate "the game has ended" claims */
--- a/src/network.h	Mon Aug 24 15:48:19 2026 +0200
+++ b/src/network.h	Mon Aug 24 16:13:51 2026 +0200
@@ -57,7 +57,7 @@
 #define NETCODE_CONNLOST 0x80
 #define NETCODE_ERROR 0xFF
 
-#define NETCODE_VERSION 24
+#define NETCODE_VERSION 25
 
 typedef struct {
     int fd; /* -1, if we are the client */

mercurial