Mon, 16 Jun 2014 15:41:06 +0200
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 |
1.1 --- a/src/chess/pgn.h Mon Jun 16 13:45:31 2014 +0200 1.2 +++ b/src/chess/pgn.h Mon Jun 16 15:41:06 2014 +0200 1.3 @@ -41,7 +41,6 @@ 1.4 int read_pgn(FILE *stream, GameState *gamestate, GameInfo *gameinfo); 1.5 size_t write_pgn(FILE* stream, GameState *gamestate, GameInfo *gameinfo); 1.6 1.7 - 1.8 #ifdef __cplusplus 1.9 } 1.10 #endif
2.1 --- a/src/chess/rules.c Mon Jun 16 13:45:31 2014 +0200 2.2 +++ b/src/chess/rules.c Mon Jun 16 15:41:06 2014 +0200 2.3 @@ -40,7 +40,7 @@ 2.4 *lastmovecopy = *(simulation.lastmove); 2.5 simulation.movelist = simulation.lastmove = lastmovecopy; 2.6 } 2.7 - 2.8 + 2.9 return simulation; 2.10 } 2.11 2.12 @@ -421,13 +421,13 @@ 2.13 } 2.14 } else { 2.15 /* bishop aspect */ 2.16 - int dr = move->torow > move->fromrow ? 1 : -1; 2.17 - int df = move->tofile > move->fromfile ? 1 : -1; 2.18 + int dr = threat->torow > threat->fromrow ? 1 : -1; 2.19 + int df = threat->tofile > threat->fromfile ? 1 : -1; 2.20 2.21 - uint8_t row = move->fromrow; 2.22 - uint8_t file = move->fromfile; 2.23 - while (!canescape && file != move->tofile - df 2.24 - && row != move->torow - dr) { 2.25 + uint8_t row = threat->fromrow; 2.26 + uint8_t file = threat->fromfile; 2.27 + while (!canescape && file != threat->tofile - df 2.28 + && row != threat->torow - dr) { 2.29 row += dr; 2.30 file += df; 2.31 canescape |= is_protected(&simulation, row, file,
3.1 --- a/src/client.c Mon Jun 16 13:45:31 2014 +0200 3.2 +++ b/src/client.c Mon Jun 16 15:41:06 2014 +0200 3.3 @@ -73,9 +73,10 @@ 3.4 return EXIT_FAILURE; 3.5 } 3.6 3.7 - if (net_recieve_code(server.fd) == NETCODE_GAMEINFO) { 3.8 - net_recieve_data(server.fd, &(settings->gameinfo), 3.9 - sizeof(settings->gameinfo)); 3.10 + uint8_t code = net_recieve_code(server.fd); 3.11 + if (code == NETCODE_GAMEINFO) { 3.12 + // Start new game 3.13 + net_recieve_data(server.fd, &(settings->gameinfo), sizeof(GameInfo)); 3.14 dump_gameinfo(&(settings->gameinfo)); 3.15 if (prompt_yesno("Accept challenge")) { 3.16 net_send_code(server.fd, NETCODE_ACCEPT); 3.17 @@ -83,6 +84,28 @@ 3.18 } else { 3.19 net_send_code(server.fd, NETCODE_DECLINE); 3.20 } 3.21 + } else if (code == NETCODE_PGNDATA) { 3.22 + net_recieve_data(server.fd, &(settings->gameinfo), sizeof(GameInfo)); 3.23 + dump_gameinfo(&(settings->gameinfo)); 3.24 + uint16_t mc; 3.25 + net_recieve_data(server.fd, &mc, sizeof(mc)); 3.26 + Move *moves = calloc(mc, sizeof(Move)); 3.27 + net_recieve_data(server.fd, moves, mc*sizeof(Move)); 3.28 + GameState continuegame; 3.29 + gamestate_init(&continuegame); 3.30 + for (size_t i = 0 ; i < mc ; i++) { 3.31 + apply_move(&continuegame, &(moves[i])); 3.32 + } 3.33 + free(moves); 3.34 + addch('\n'); 3.35 + dump_moveinfo(&continuegame); 3.36 + if (prompt_yesno( 3.37 + "\n\nServer wants to continue a game. Accept challenge")) { 3.38 + net_send_code(server.fd, NETCODE_ACCEPT); 3.39 + game_continue(settings, server.fd, &continuegame); 3.40 + } else { 3.41 + net_send_code(server.fd, NETCODE_DECLINE); 3.42 + } 3.43 } else { 3.44 addstr("Server sent invalid gameinfo."); 3.45 net_destroy(&server);
4.1 --- a/src/game.c Mon Jun 16 13:45:31 2014 +0200 4.2 +++ b/src/game.c Mon Jun 16 15:41:06 2014 +0200 4.3 @@ -174,7 +174,7 @@ 4.4 4.5 char filename[64]; 4.6 int y = getcury(stdscr); 4.7 - if (getnstr(filename, 64) == OK) { 4.8 + if (getnstr(filename, 64) == OK && filename[0] != '\0') { 4.9 move(y, 0); 4.10 FILE *file = fopen(filename, "w"); 4.11 if (file) { 4.12 @@ -322,8 +322,7 @@ 4.13 int eval_result = eval_move(gamestate, movestr, &move, mycolor); 4.14 switch (eval_result) { 4.15 case VALID_MOVE_SYNTAX: 4.16 - net_send_data(opponent, NETCODE_MOVE, &move, 4.17 - sizeof(Move)-8); 4.18 + net_send_data(opponent, NETCODE_MOVE, &move, sizeof(Move)); 4.19 code = net_recieve_code(opponent); 4.20 move.check = code == NETCODE_CHECK || 4.21 code == NETCODE_CHECKMATE; 4.22 @@ -423,13 +422,11 @@ 4.23 } 4.24 break; 4.25 case NETCODE_MOVE: 4.26 - net_recieve_data(opponent, &move, sizeof(Move)-8); 4.27 + net_recieve_data(opponent, &move, sizeof(Move)); 4.28 code = validate_move(gamestate, &move); 4.29 if (code == VALID_MOVE_SEMANTICS) { 4.30 apply_move(gamestate, &move); 4.31 - if (move.check) { 4.32 - net_send_code(opponent, NETCODE_CHECK); 4.33 - } else if (gamestate->checkmate) { 4.34 + if (gamestate->checkmate) { 4.35 net_send_code(opponent, NETCODE_CHECKMATE); 4.36 printw("\rCheckmate!"); 4.37 clrtoeol(); 4.38 @@ -439,6 +436,8 @@ 4.39 printw("\rStalemate!"); 4.40 clrtoeol(); 4.41 return 1; 4.42 + } else if (move.check) { 4.43 + net_send_code(opponent, NETCODE_CHECK); 4.44 } else { 4.45 net_send_code(opponent, NETCODE_ACCEPT); 4.46 } 4.47 @@ -523,28 +522,34 @@ 4.48 post_game(&gamestate, &(settings->gameinfo)); 4.49 } 4.50 4.51 -void game_start(Settings *settings, int opponent) { 4.52 +void game_continue(Settings *settings, int opponent, GameState *gamestate) { 4.53 inputy = getmaxy(stdscr) - 6; 4.54 4.55 - _Bool myturn = is_server(settings) == 4.56 - (settings->gameinfo.servercolor == WHITE); 4.57 - uint8_t mycolor = myturn ? WHITE : BLACK; 4.58 + uint8_t mycolor = is_server(settings) ? settings->gameinfo.servercolor : 4.59 + opponent_color(settings->gameinfo.servercolor); 4.60 4.61 - GameState gamestate; 4.62 - gamestate_init(&gamestate); 4.63 + _Bool myturn = (gamestate->lastmove ? 4.64 + (gamestate->lastmove->move.piece & COLOR_MASK) : WHITE) != mycolor; 4.65 4.66 _Bool running; 4.67 do { 4.68 clear(); 4.69 - draw_board(&gamestate, mycolor); 4.70 + draw_board(gamestate, mycolor); 4.71 if (myturn) { 4.72 - running = !sendmove(&gamestate, &(settings->gameinfo), 4.73 + running = !sendmove(gamestate, &(settings->gameinfo), 4.74 opponent, mycolor); 4.75 } else { 4.76 - running = !recvmove(&gamestate, &(settings->gameinfo), opponent); 4.77 + running = !recvmove(gamestate, &(settings->gameinfo), opponent); 4.78 } 4.79 myturn ^= TRUE; 4.80 } while (running); 4.81 4.82 - post_game(&gamestate, &(settings->gameinfo)); 4.83 + post_game(gamestate, &(settings->gameinfo)); 4.84 } 4.85 + 4.86 +void game_start(Settings *settings, int opponent) { 4.87 + GameState gamestate; 4.88 + gamestate_init(&gamestate); 4.89 + 4.90 + game_continue(settings, opponent, &gamestate); 4.91 +}
5.1 --- a/src/game.h Mon Jun 16 13:45:31 2014 +0200 5.2 +++ b/src/game.h Mon Jun 16 15:41:06 2014 +0200 5.3 @@ -38,6 +38,7 @@ 5.4 #endif 5.5 5.6 void game_start(Settings *settings, int opponent); 5.7 +void game_continue(Settings *settings, int opponent, GameState *gamestate); 5.8 void game_start_singlemachine(Settings *settings); 5.9 5.10 #ifdef __cplusplus
6.1 --- a/src/main.c Mon Jun 16 13:45:31 2014 +0200 6.2 +++ b/src/main.c Mon Jun 16 15:41:06 2014 +0200 6.3 @@ -116,12 +116,6 @@ 6.4 fprintf(stderr, "The options -c and -S are mutually exclusive\n"); 6.5 return 1; 6.6 } 6.7 - // TODO: implement 6.8 - if (!settings->singlemachine) { 6.9 - fprintf(stderr, "Game continuation currently not supported for " 6.10 - "network games.\n"); 6.11 - return 1; 6.12 - } 6.13 } 6.14 6.15 return 0; 6.16 @@ -157,6 +151,24 @@ 6.17 refresh(); 6.18 } 6.19 6.20 +void dump_moveinfo(GameState *gamestate) { 6.21 + int i = 1; 6.22 + for (MoveList *movelist = gamestate->movelist ; 6.23 + movelist ; movelist = movelist->next) { 6.24 + if (++i % 2 == 0) { 6.25 + printw("%d. %s", i/2, movelist->move.string); 6.26 + } else { 6.27 + printw(" %s", movelist->move.string); 6.28 + } 6.29 + if (i % 20) { 6.30 + addch(' '); 6.31 + } else { 6.32 + addch('\n'); 6.33 + } 6.34 + } 6.35 + refresh(); 6.36 +} 6.37 + 6.38 void leavescr() { 6.39 endwin(); 6.40 }
7.1 --- a/src/network.h Mon Jun 16 13:45:31 2014 +0200 7.2 +++ b/src/network.h Mon Jun 16 15:41:06 2014 +0200 7.3 @@ -40,6 +40,7 @@ 7.4 #define NETCODE_ACCEPT 0x02 7.5 #define NETCODE_DECLINE 0x04 7.6 #define NETCODE_GAMEINFO 0x10 7.7 +#define NETCODE_PGNDATA 0x11 7.8 #define NETCODE_MOVE 0x20 7.9 #define NETCODE_CHECK 0x22 7.10 #define NETCODE_CHECKMATE 0x24 7.11 @@ -49,7 +50,7 @@ 7.12 #define NETCODE_TIMEOVER 0x44 7.13 #define NETCODE_CONNLOST 0x80 7.14 7.15 -#define NETCODE_VERSION 15 7.16 +#define NETCODE_VERSION 16 7.17 7.18 typedef struct { 7.19 int fd; /* -1, if we are the client */
8.1 --- a/src/server.c Mon Jun 16 13:45:31 2014 +0200 8.2 +++ b/src/server.c Mon Jun 16 15:41:06 2014 +0200 8.3 @@ -30,6 +30,8 @@ 8.4 #include "terminal-chess.h" 8.5 #include "game.h" 8.6 #include <ncurses.h> 8.7 +#include <errno.h> 8.8 +#include <string.h> 8.9 8.10 static int server_open(Server *server, char *port) { 8.11 printw("\nListening for client...\n"); 8.12 @@ -64,6 +66,31 @@ 8.13 Server server; 8.14 8.15 dump_gameinfo(&(settings->gameinfo)); 8.16 + GameState continuegame; 8.17 + gamestate_init(&continuegame); 8.18 + if (settings->continuepgn) { 8.19 + // preload PGN data before handshake 8.20 + FILE *pgnfile = fopen(settings->continuepgn, "r"); 8.21 + if (pgnfile) { 8.22 + int result = read_pgn(pgnfile, &continuegame, 8.23 + &(settings->gameinfo)); 8.24 + fclose(pgnfile); 8.25 + if (result != EXIT_SUCCESS) { 8.26 + addstr("Invalid PGN file content.\n"); 8.27 + return EXIT_FAILURE; 8.28 + } 8.29 + if (!is_game_running(&continuegame)) { 8.30 + addstr("Game has ended. Use -S to analyze it.\n"); 8.31 + return EXIT_FAILURE; 8.32 + } 8.33 + addch('\n'); 8.34 + dump_moveinfo(&continuegame); 8.35 + addch('\n'); 8.36 + } else { 8.37 + printw("Can't read PGN file (%s)\n", strerror(errno)); 8.38 + return EXIT_FAILURE; 8.39 + } 8.40 + } 8.41 8.42 if (server_open(&server, settings->port)) { 8.43 net_destroy(&server); 8.44 @@ -76,16 +103,49 @@ 8.45 } 8.46 8.47 int fd = server.client->fd; 8.48 - net_send_data(fd, NETCODE_GAMEINFO, 8.49 - &(settings->gameinfo), sizeof(GameInfo)); 8.50 + if (settings->continuepgn) { 8.51 + // Continue game, send PGN data 8.52 + uint16_t mc = 0; 8.53 + MoveList *movelist = continuegame.movelist; 8.54 + while (movelist) { 8.55 + mc++; 8.56 + movelist = movelist->next; 8.57 + } 8.58 + 8.59 + Move* moves = calloc(mc, sizeof(Move)); 8.60 + 8.61 + movelist = continuegame.movelist; 8.62 + mc = 0; 8.63 + while (movelist) { 8.64 + moves[mc] = movelist->move; 8.65 + mc++; 8.66 + movelist = movelist->next; 8.67 + } 8.68 + 8.69 + size_t pgndata_size = sizeof(GameInfo)+sizeof(mc)+mc*sizeof(Move); 8.70 + char *pgndata = malloc(pgndata_size); 8.71 + memcpy(pgndata, &(settings->gameinfo), sizeof(GameInfo)); 8.72 + memcpy(pgndata+sizeof(GameInfo), &mc, sizeof(mc)); 8.73 + memcpy(pgndata+sizeof(GameInfo)+sizeof(mc), moves, mc*sizeof(Move)); 8.74 + free(moves); 8.75 + net_send_data(fd, NETCODE_PGNDATA, pgndata, pgndata_size); 8.76 + free(pgndata); 8.77 + } else { 8.78 + // Start new game 8.79 + net_send_data(fd, NETCODE_GAMEINFO, 8.80 + &(settings->gameinfo), sizeof(GameInfo)); 8.81 + } 8.82 addstr("\rClient connected - awaiting challenge acceptance..."); 8.83 refresh(); 8.84 int code = net_recieve_code(fd); 8.85 if (code == NETCODE_ACCEPT) { 8.86 addstr("\rClient connected - challenge accepted."); 8.87 clrtoeol(); 8.88 - 8.89 - game_start(settings, fd); 8.90 + if (settings->continuepgn) { 8.91 + game_continue(settings, fd, &continuegame); 8.92 + } else { 8.93 + game_start(settings, fd); 8.94 + } 8.95 } else if (code == NETCODE_DECLINE) { 8.96 addstr("\rClient connected - challenge declined."); 8.97 clrtoeol();
9.1 --- a/src/terminal-chess.h Mon Jun 16 13:45:31 2014 +0200 9.2 +++ b/src/terminal-chess.h Mon Jun 16 15:41:06 2014 +0200 9.3 @@ -53,6 +53,7 @@ 9.4 #define is_server(settings) !((settings)->serverhost) 9.5 9.6 void dump_gameinfo(GameInfo *gameinfo); 9.7 +void dump_moveinfo(GameState *gamestate); 9.8 9.9 int server_run(Settings* settings); 9.10 int client_run(Settings* settings);