added support for game continuation over network + fixed major bug in checkmate anticipation when the king is attacked diagonally

Mon, 16 Jun 2014 15:41:06 +0200

author
Mike Becker <universe@uap-core.de>
date
Mon, 16 Jun 2014 15:41:06 +0200
changeset 51
84f2e380a434
parent 50
41017d0a72c5
child 52
26707039d5a6

added support for game continuation over network + fixed major bug in checkmate anticipation when the king is attacked diagonally

src/chess/pgn.h file | annotate | diff | comparison | revisions
src/chess/rules.c file | annotate | diff | comparison | revisions
src/client.c file | annotate | diff | comparison | revisions
src/game.c file | annotate | diff | comparison | revisions
src/game.h file | annotate | diff | comparison | revisions
src/main.c file | annotate | diff | comparison | revisions
src/network.h file | annotate | diff | comparison | revisions
src/server.c file | annotate | diff | comparison | revisions
src/terminal-chess.h file | annotate | diff | comparison | revisions
--- a/src/chess/pgn.h	Mon Jun 16 13:45:31 2014 +0200
+++ b/src/chess/pgn.h	Mon Jun 16 15:41:06 2014 +0200
@@ -41,7 +41,6 @@
 int read_pgn(FILE *stream, GameState *gamestate, GameInfo *gameinfo);
 size_t write_pgn(FILE* stream, GameState *gamestate, GameInfo *gameinfo);
 
-
 #ifdef	__cplusplus
 }
 #endif
--- a/src/chess/rules.c	Mon Jun 16 13:45:31 2014 +0200
+++ b/src/chess/rules.c	Mon Jun 16 15:41:06 2014 +0200
@@ -40,7 +40,7 @@
         *lastmovecopy = *(simulation.lastmove);
         simulation.movelist = simulation.lastmove = lastmovecopy;
     }
-        
+
     return simulation;
 }
 
@@ -421,13 +421,13 @@
                     }
                 } else {
                     /* bishop aspect */
-                    int dr = move->torow > move->fromrow ? 1 : -1;
-                    int df = move->tofile > move->fromfile ? 1 : -1;
+                    int dr = threat->torow > threat->fromrow ? 1 : -1;
+                    int df = threat->tofile > threat->fromfile ? 1 : -1;
 
-                    uint8_t row = move->fromrow;
-                    uint8_t file = move->fromfile;
-                    while (!canescape && file != move->tofile - df
-                        && row != move->torow - dr) {
+                    uint8_t row = threat->fromrow;
+                    uint8_t file = threat->fromfile;
+                    while (!canescape && file != threat->tofile - df
+                        && row != threat->torow - dr) {
                         row += dr;
                         file += df;
                         canescape |= is_protected(&simulation, row, file,
--- a/src/client.c	Mon Jun 16 13:45:31 2014 +0200
+++ b/src/client.c	Mon Jun 16 15:41:06 2014 +0200
@@ -73,9 +73,10 @@
         return EXIT_FAILURE;
     }
 
-    if (net_recieve_code(server.fd) == NETCODE_GAMEINFO) {
-        net_recieve_data(server.fd, &(settings->gameinfo),
-            sizeof(settings->gameinfo));
+    uint8_t code = net_recieve_code(server.fd);
+    if (code == NETCODE_GAMEINFO) {
+        // Start new game
+        net_recieve_data(server.fd, &(settings->gameinfo), sizeof(GameInfo));
         dump_gameinfo(&(settings->gameinfo));
         if (prompt_yesno("Accept challenge")) {
             net_send_code(server.fd, NETCODE_ACCEPT);
@@ -83,6 +84,28 @@
         } else {
             net_send_code(server.fd, NETCODE_DECLINE);
         }
+    } else if (code == NETCODE_PGNDATA) {
+        net_recieve_data(server.fd, &(settings->gameinfo), sizeof(GameInfo));
+        dump_gameinfo(&(settings->gameinfo));
+        uint16_t mc;
+        net_recieve_data(server.fd, &mc, sizeof(mc));
+        Move *moves = calloc(mc, sizeof(Move));
+        net_recieve_data(server.fd, moves, mc*sizeof(Move));
+        GameState continuegame;
+        gamestate_init(&continuegame);
+        for (size_t i = 0 ; i < mc ; i++) {
+            apply_move(&continuegame, &(moves[i]));
+        }
+        free(moves);
+        addch('\n');
+        dump_moveinfo(&continuegame);
+        if (prompt_yesno(
+                "\n\nServer wants to continue a game. Accept challenge")) {
+            net_send_code(server.fd, NETCODE_ACCEPT);
+            game_continue(settings, server.fd, &continuegame);
+        } else {
+            net_send_code(server.fd, NETCODE_DECLINE);
+        }
     } else {
         addstr("Server sent invalid gameinfo.");
         net_destroy(&server);
--- a/src/game.c	Mon Jun 16 13:45:31 2014 +0200
+++ b/src/game.c	Mon Jun 16 15:41:06 2014 +0200
@@ -174,7 +174,7 @@
     
     char filename[64];
     int y = getcury(stdscr);
-    if (getnstr(filename, 64) == OK) {
+    if (getnstr(filename, 64) == OK && filename[0] != '\0') {
         move(y, 0);
         FILE *file = fopen(filename, "w");
         if (file) {
@@ -322,8 +322,7 @@
                 int eval_result = eval_move(gamestate, movestr, &move, mycolor);
                 switch (eval_result) {
                 case VALID_MOVE_SYNTAX:
-                    net_send_data(opponent, NETCODE_MOVE, &move,
-                        sizeof(Move)-8);
+                    net_send_data(opponent, NETCODE_MOVE, &move, sizeof(Move));
                     code = net_recieve_code(opponent);
                     move.check = code == NETCODE_CHECK ||
                         code == NETCODE_CHECKMATE;
@@ -423,13 +422,11 @@
                 }
                 break;
             case NETCODE_MOVE:
-                net_recieve_data(opponent, &move, sizeof(Move)-8);
+                net_recieve_data(opponent, &move, sizeof(Move));
                 code = validate_move(gamestate, &move);
                 if (code == VALID_MOVE_SEMANTICS) {
                     apply_move(gamestate, &move);
-                    if (move.check) {
-                        net_send_code(opponent, NETCODE_CHECK);
-                    } else if (gamestate->checkmate) {
+                    if (gamestate->checkmate) {
                         net_send_code(opponent, NETCODE_CHECKMATE);
                         printw("\rCheckmate!");
                         clrtoeol();
@@ -439,6 +436,8 @@
                         printw("\rStalemate!");
                         clrtoeol();
                         return 1;
+                    } else if (move.check) {
+                        net_send_code(opponent, NETCODE_CHECK);
                     } else {
                         net_send_code(opponent, NETCODE_ACCEPT);
                     }
@@ -523,28 +522,34 @@
     post_game(&gamestate, &(settings->gameinfo));
 }
 
-void game_start(Settings *settings, int opponent) {
+void game_continue(Settings *settings, int opponent, GameState *gamestate) {
     inputy = getmaxy(stdscr) - 6;
     
-    _Bool myturn = is_server(settings) ==
-        (settings->gameinfo.servercolor == WHITE);
-    uint8_t mycolor = myturn ? WHITE : BLACK;
+    uint8_t mycolor = is_server(settings) ? settings->gameinfo.servercolor :
+        opponent_color(settings->gameinfo.servercolor);
     
-    GameState gamestate;
-    gamestate_init(&gamestate);
+    _Bool myturn = (gamestate->lastmove ?
+        (gamestate->lastmove->move.piece & COLOR_MASK) : WHITE) != mycolor;
     
     _Bool running;
     do {
         clear();
-        draw_board(&gamestate, mycolor);
+        draw_board(gamestate, mycolor);
         if (myturn) {
-            running = !sendmove(&gamestate, &(settings->gameinfo),
+            running = !sendmove(gamestate, &(settings->gameinfo),
                 opponent, mycolor);
         } else {
-            running = !recvmove(&gamestate, &(settings->gameinfo), opponent);
+            running = !recvmove(gamestate, &(settings->gameinfo), opponent);
         }
         myturn ^= TRUE;
     }  while (running);
     
-    post_game(&gamestate, &(settings->gameinfo));
+    post_game(gamestate, &(settings->gameinfo));
 }
+
+void game_start(Settings *settings, int opponent) {
+    GameState gamestate;
+    gamestate_init(&gamestate);
+    
+    game_continue(settings, opponent, &gamestate);
+}
--- a/src/game.h	Mon Jun 16 13:45:31 2014 +0200
+++ b/src/game.h	Mon Jun 16 15:41:06 2014 +0200
@@ -38,6 +38,7 @@
 #endif
 
 void game_start(Settings *settings, int opponent);
+void game_continue(Settings *settings, int opponent, GameState *gamestate);
 void game_start_singlemachine(Settings *settings);
 
 #ifdef	__cplusplus
--- a/src/main.c	Mon Jun 16 13:45:31 2014 +0200
+++ b/src/main.c	Mon Jun 16 15:41:06 2014 +0200
@@ -116,12 +116,6 @@
             fprintf(stderr, "The options -c and -S are mutually exclusive\n");
             return 1;
         }
-        // TODO: implement
-        if (!settings->singlemachine) {
-            fprintf(stderr, "Game continuation currently not supported for "
-                "network games.\n");
-            return 1;
-        }
     }
     
     return 0;
@@ -157,6 +151,24 @@
     refresh();
 }
 
+void dump_moveinfo(GameState *gamestate) {
+    int i = 1;
+    for (MoveList *movelist = gamestate->movelist ;
+        movelist ; movelist = movelist->next) {        
+        if (++i % 2 == 0) {
+            printw("%d. %s", i/2, movelist->move.string);
+        } else {
+            printw(" %s", movelist->move.string);
+        }
+        if (i % 20)  {
+            addch(' ');
+        } else {
+            addch('\n');
+        }
+    }
+    refresh();
+}
+
 void leavescr() {
     endwin();
 }
--- a/src/network.h	Mon Jun 16 13:45:31 2014 +0200
+++ b/src/network.h	Mon Jun 16 15:41:06 2014 +0200
@@ -40,6 +40,7 @@
 #define NETCODE_ACCEPT 0x02
 #define NETCODE_DECLINE 0x04
 #define NETCODE_GAMEINFO 0x10
+#define NETCODE_PGNDATA 0x11
 #define NETCODE_MOVE 0x20
 #define NETCODE_CHECK 0x22
 #define NETCODE_CHECKMATE 0x24
@@ -49,7 +50,7 @@
 #define NETCODE_TIMEOVER 0x44
 #define NETCODE_CONNLOST 0x80
 
-#define NETCODE_VERSION 15
+#define NETCODE_VERSION 16
 
 typedef struct {
     int fd; /* -1, if we are the client */
--- a/src/server.c	Mon Jun 16 13:45:31 2014 +0200
+++ b/src/server.c	Mon Jun 16 15:41:06 2014 +0200
@@ -30,6 +30,8 @@
 #include "terminal-chess.h"
 #include "game.h"
 #include <ncurses.h>
+#include <errno.h>
+#include <string.h>
 
 static int server_open(Server *server, char *port) {
     printw("\nListening for client...\n");
@@ -64,6 +66,31 @@
     Server server;
     
     dump_gameinfo(&(settings->gameinfo));
+    GameState continuegame;
+    gamestate_init(&continuegame);
+    if (settings->continuepgn) {
+        // preload PGN data before handshake
+        FILE *pgnfile = fopen(settings->continuepgn, "r");
+        if (pgnfile) {
+            int result = read_pgn(pgnfile, &continuegame,
+                &(settings->gameinfo));
+            fclose(pgnfile);
+            if (result != EXIT_SUCCESS) {
+                addstr("Invalid PGN file content.\n");
+                return EXIT_FAILURE;
+            }
+            if (!is_game_running(&continuegame)) {
+                addstr("Game has ended. Use -S to analyze it.\n");
+                return EXIT_FAILURE;
+            }
+            addch('\n');
+            dump_moveinfo(&continuegame);
+            addch('\n');
+        } else {
+            printw("Can't read PGN file (%s)\n", strerror(errno));
+            return EXIT_FAILURE;
+        }
+    }
     
     if (server_open(&server, settings->port)) {
         net_destroy(&server);
@@ -76,16 +103,49 @@
     }
 
     int fd = server.client->fd;
-    net_send_data(fd, NETCODE_GAMEINFO,
-        &(settings->gameinfo), sizeof(GameInfo));
+    if (settings->continuepgn) {
+        // Continue game, send PGN data
+        uint16_t mc = 0;
+        MoveList *movelist = continuegame.movelist;
+        while (movelist) {
+            mc++;
+            movelist = movelist->next;
+        }
+        
+        Move* moves = calloc(mc, sizeof(Move));
+        
+        movelist = continuegame.movelist;
+        mc = 0;
+        while (movelist) {
+            moves[mc] = movelist->move;
+            mc++;
+            movelist = movelist->next;
+        }
+        
+        size_t pgndata_size = sizeof(GameInfo)+sizeof(mc)+mc*sizeof(Move);
+        char *pgndata = malloc(pgndata_size);
+        memcpy(pgndata, &(settings->gameinfo), sizeof(GameInfo));
+        memcpy(pgndata+sizeof(GameInfo), &mc, sizeof(mc));
+        memcpy(pgndata+sizeof(GameInfo)+sizeof(mc), moves, mc*sizeof(Move));
+        free(moves);
+        net_send_data(fd, NETCODE_PGNDATA, pgndata, pgndata_size);
+        free(pgndata);
+    } else {
+        // Start new game
+        net_send_data(fd, NETCODE_GAMEINFO,
+            &(settings->gameinfo), sizeof(GameInfo));
+    }
     addstr("\rClient connected - awaiting challenge acceptance...");
     refresh();
     int code = net_recieve_code(fd);
     if (code == NETCODE_ACCEPT) {
         addstr("\rClient connected - challenge accepted.");
         clrtoeol();
-        
-        game_start(settings, fd);
+        if (settings->continuepgn) {
+            game_continue(settings, fd, &continuegame);
+        } else {
+            game_start(settings, fd);
+        }
     } else if (code == NETCODE_DECLINE) {
         addstr("\rClient connected - challenge declined.");
         clrtoeol();
--- a/src/terminal-chess.h	Mon Jun 16 13:45:31 2014 +0200
+++ b/src/terminal-chess.h	Mon Jun 16 15:41:06 2014 +0200
@@ -53,6 +53,7 @@
 #define is_server(settings) !((settings)->serverhost)
 
 void dump_gameinfo(GameInfo *gameinfo);
+void dump_moveinfo(GameState *gamestate);
 
 int server_run(Settings* settings);
 int client_run(Settings* settings);

mercurial