1.1 --- a/src/game.c Wed Jun 11 16:54:20 2014 +0200 1.2 +++ b/src/game.c Mon Jun 16 13:45:31 2014 +0200 1.3 @@ -35,6 +35,8 @@ 1.4 #include <string.h> 1.5 #include <inttypes.h> 1.6 #include <sys/select.h> 1.7 +#include <stdio.h> 1.8 +#include <errno.h> 1.9 1.10 static const uint8_t boardx = 10, boardy = 10; 1.11 static int inputy = 21; /* should be overridden on game startup */ 1.12 @@ -69,7 +71,7 @@ 1.13 return 0; 1.14 } 1.15 1.16 -static void draw_board(GameState *gamestate) { 1.17 +static void draw_board(GameState *gamestate, uint8_t perspective) { 1.18 for (uint8_t y = 0 ; y < 8 ; y++) { 1.19 for (uint8_t x = 0 ; x < 8 ; x++) { 1.20 uint8_t col = gamestate->board[y][x] & COLOR_MASK; 1.21 @@ -89,8 +91,8 @@ 1.22 ) 1.23 ); 1.24 1.25 - int cy = gamestate->mycolor == WHITE ? boardy-y : boardy-7+y; 1.26 - int cx = gamestate->mycolor == WHITE ? boardx+x*3 : boardx+21-x*3; 1.27 + int cy = perspective == WHITE ? boardy-y : boardy-7+y; 1.28 + int cx = perspective == WHITE ? boardx+x*3 : boardx+21-x*3; 1.29 mvaddch(cy, cx, ' '); 1.30 mvaddch(cy, cx+1, piecec); 1.31 mvaddch(cy, cx+2, ' '); 1.32 @@ -99,8 +101,8 @@ 1.33 1.34 attrset(A_NORMAL); 1.35 for (uint8_t i = 0 ; i < 8 ; i++) { 1.36 - int x = gamestate->mycolor == WHITE ? boardx+i*3+1 : boardx+22-i*3; 1.37 - int y = gamestate->mycolor == WHITE ? boardy-i : boardy-7+i; 1.38 + int x = perspective == WHITE ? boardx+i*3+1 : boardx+22-i*3; 1.39 + int y = perspective == WHITE ? boardy-i : boardy-7+i; 1.40 mvaddch(boardy+1, x, 'a'+i); 1.41 mvaddch(y, boardx-2, '1'+i); 1.42 } 1.43 @@ -122,17 +124,15 @@ 1.44 printw("%d. ", logi / 2); 1.45 } 1.46 1.47 - if (logelem) { 1.48 - addstr(logelem->move.string); 1.49 - if (!logelem->next) { 1.50 - if (gamestate->stalemate) { 1.51 - addstr(" stalemate"); 1.52 - } 1.53 + addstr(logelem->move.string); 1.54 + if (!logelem->next) { 1.55 + if (gamestate->stalemate) { 1.56 + addstr(" stalemate"); 1.57 } 1.58 - addch(' '); 1.59 - 1.60 - logelem = logelem->next; 1.61 } 1.62 + addch(' '); 1.63 + 1.64 + logelem = logelem->next; 1.65 } 1.66 } 1.67 1.68 @@ -167,8 +167,30 @@ 1.69 } 1.70 } 1.71 1.72 -#define MOVESTR_BUFLEN 8 1.73 -static int domove_singlemachine(GameState *gamestate, GameInfo *gameinfo) { 1.74 +static void save_pgn(GameState *gamestate, GameInfo *gameinfo) { 1.75 + printw("Filename: "); 1.76 + clrtoeol(); 1.77 + refresh(); 1.78 + 1.79 + char filename[64]; 1.80 + int y = getcury(stdscr); 1.81 + if (getnstr(filename, 64) == OK) { 1.82 + move(y, 0); 1.83 + FILE *file = fopen(filename, "w"); 1.84 + if (file) { 1.85 + write_pgn(file, gamestate, gameinfo); 1.86 + fclose(file); 1.87 + printw("File saved."); 1.88 + } else { 1.89 + printw("Can't write to file (%s).", strerror(errno)); 1.90 + } 1.91 + clrtoeol(); 1.92 + } 1.93 +} 1.94 + 1.95 +#define MOVESTR_BUFLEN 10 1.96 +static int domove_singlemachine(GameState *gamestate, 1.97 + GameInfo *gameinfo, uint8_t curcolor) { 1.98 1.99 1.100 size_t bufpos = 0; 1.101 @@ -183,29 +205,33 @@ 1.102 move(inputy, 0); 1.103 printw( 1.104 "Use chess notation to enter your move.\n" 1.105 - "Or type 'resign' to resign or 'remis' to end with remis.\n\n" 1.106 + "Or use a command: remis, resign, savepgn\n\n" 1.107 "Type your move: "); 1.108 clrtoeol(); 1.109 1.110 if (asyncgetnstr(movestr, &bufpos, MOVESTR_BUFLEN)) { 1.111 if (strncmp(movestr, "resign", MOVESTR_BUFLEN) == 0) { 1.112 + gamestate->resign = 1; 1.113 printw("%s resigned!", 1.114 - gamestate->mycolor==WHITE?"White":"Black"); 1.115 - clrtoeol(); 1.116 + curcolor==WHITE?"White":"Black"); 1.117 + clrtobot(); 1.118 refresh(); 1.119 return 1; 1.120 } else if (strncmp(movestr, "remis", MOVESTR_BUFLEN) == 0) { 1.121 + gamestate->remis = 1; 1.122 printw("Game ends remis."); 1.123 - clrtoeol(); 1.124 + clrtobot(); 1.125 refresh(); 1.126 return 1; 1.127 + } else if (strncmp(movestr, "savepgn", MOVESTR_BUFLEN) == 0) { 1.128 + save_pgn(gamestate, gameinfo); 1.129 } else { 1.130 Move move; 1.131 - int eval_result = eval_move(gamestate, movestr, &move); 1.132 - switch (eval_result) { 1.133 + int result = eval_move(gamestate, movestr, &move, curcolor); 1.134 + switch (result) { 1.135 case VALID_MOVE_SYNTAX: 1.136 - eval_result = validate_move(gamestate, &move); 1.137 - if (eval_result == VALID_MOVE_SEMANTICS) { 1.138 + result = validate_move(gamestate, &move); 1.139 + if (result == VALID_MOVE_SEMANTICS) { 1.140 apply_move(gamestate, &move); 1.141 if (gamestate->checkmate) { 1.142 printw("Checkmate!"); 1.143 @@ -219,11 +245,11 @@ 1.144 return 0; 1.145 } 1.146 } else { 1.147 - eval_move_failed_msg(eval_result); 1.148 + eval_move_failed_msg(result); 1.149 } 1.150 break; 1.151 default: 1.152 - eval_move_failed_msg(eval_result); 1.153 + eval_move_failed_msg(result); 1.154 } 1.155 clrtoeol(); 1.156 } 1.157 @@ -231,7 +257,8 @@ 1.158 } 1.159 } 1.160 1.161 -static int sendmove(GameState *gamestate, GameInfo *gameinfo, int opponent) { 1.162 +static int sendmove(GameState *gamestate, GameInfo *gameinfo, 1.163 + int opponent, uint8_t mycolor) { 1.164 1.165 size_t bufpos = 0; 1.166 char movestr[MOVESTR_BUFLEN]; 1.167 @@ -249,23 +276,26 @@ 1.168 if (remisrejected) { 1.169 printw( 1.170 "Use chess notation to enter your move.\n" 1.171 - "Remis offer rejected - type 'resign' to resign. \n\n" 1.172 + "Remis offer rejected \n\n" 1.173 "Type your move: "); 1.174 } else { 1.175 printw( 1.176 "Use chess notation to enter your move.\n" 1.177 - "Or type 'resign' to resign or 'remis' to offer remis.\n\n" 1.178 + "Or use a command: remis, resign, savepgn\n\n" 1.179 "Type your move: "); 1.180 } 1.181 clrtoeol(); 1.182 1.183 if (asyncgetnstr(movestr, &bufpos, MOVESTR_BUFLEN)) { 1.184 if (strncmp(movestr, "resign", MOVESTR_BUFLEN) == 0) { 1.185 + gamestate->resign = 1; 1.186 printw("You resigned!"); 1.187 clrtoeol(); 1.188 refresh(); 1.189 net_send_code(opponent, NETCODE_RESIGN); 1.190 return 1; 1.191 + } else if (strncmp(movestr, "savepgn", MOVESTR_BUFLEN) == 0) { 1.192 + save_pgn(gamestate, gameinfo); 1.193 } else if (strncmp(movestr, "remis", MOVESTR_BUFLEN) == 0) { 1.194 if (!remisrejected) { 1.195 net_send_code(opponent, NETCODE_REMIS); 1.196 @@ -273,6 +303,7 @@ 1.197 refresh(); 1.198 code = net_recieve_code(opponent); 1.199 if (code == NETCODE_ACCEPT) { 1.200 + gamestate->remis = 1; 1.201 printw("\rRemis accepted!"); 1.202 clrtoeol(); 1.203 refresh(); 1.204 @@ -288,7 +319,7 @@ 1.205 } 1.206 } else { 1.207 Move move; 1.208 - int eval_result = eval_move(gamestate, movestr, &move); 1.209 + int eval_result = eval_move(gamestate, movestr, &move, mycolor); 1.210 switch (eval_result) { 1.211 case VALID_MOVE_SYNTAX: 1.212 net_send_data(opponent, NETCODE_MOVE, &move, 1.213 @@ -353,6 +384,8 @@ 1.214 timeout.tv_sec = 0; 1.215 timeout.tv_usec = 1e5; 1.216 1.217 + // TODO: allow commands 1.218 + 1.219 int result = select(opponent+1, &readfds, NULL, NULL, &timeout); 1.220 if (result == -1) { 1.221 printw("\rCannot perform asynchronous network IO"); 1.222 @@ -369,6 +402,7 @@ 1.223 clrtoeol(); 1.224 return 1; 1.225 case NETCODE_RESIGN: 1.226 + gamestate->resign = 1; 1.227 printw("\rYour opponent resigned!"); 1.228 clrtoeol(); 1.229 return 1; 1.230 @@ -379,6 +413,7 @@ 1.231 case NETCODE_REMIS: 1.232 if (prompt_yesno( 1.233 "\rYour opponent offers remis - do you accept")) { 1.234 + gamestate->remis = 1; 1.235 printw("\rRemis accepted!"); 1.236 clrtoeol(); 1.237 net_send_code(opponent, NETCODE_ACCEPT); 1.238 @@ -421,39 +456,71 @@ 1.239 } 1.240 } 1.241 1.242 -static void init_board(GameState *gamestate) { 1.243 - Board initboard = { 1.244 - {WROOK, WKNIGHT, WBISHOP, WQUEEN, WKING, WBISHOP, WKNIGHT, WROOK}, 1.245 - {WPAWN, WPAWN, WPAWN, WPAWN, WPAWN, WPAWN, WPAWN, WPAWN}, 1.246 - {0, 0, 0, 0, 0, 0, 0, 0}, 1.247 - {0, 0, 0, 0, 0, 0, 0, 0}, 1.248 - {0, 0, 0, 0, 0, 0, 0, 0}, 1.249 - {0, 0, 0, 0, 0, 0, 0, 0}, 1.250 - {BPAWN, BPAWN, BPAWN, BPAWN, BPAWN, BPAWN, BPAWN, BPAWN}, 1.251 - {BROOK, BKNIGHT, BBISHOP, BQUEEN, BKING, BBISHOP, BKNIGHT, BROOK} 1.252 - }; 1.253 - memcpy(gamestate->board, initboard, sizeof(Board)); 1.254 +static void post_game(GameState *gamestate, GameInfo *gameinfo) { 1.255 + move(0,0); 1.256 + draw_board(gamestate, WHITE); 1.257 + 1.258 + // TODO: network connection is still open here - think about it! 1.259 + 1.260 + mvaddstr(getmaxy(stdscr)-1, 0, 1.261 + "Press 'q' to quit or 's' to save a PGN file..."); 1.262 + refresh(); 1.263 + flushinp(); 1.264 + 1.265 + noecho(); 1.266 + int c; 1.267 + do { 1.268 + c = getch(); 1.269 + if (c == 's') { 1.270 + addch('\r'); 1.271 + echo(); 1.272 + save_pgn(gamestate, gameinfo); 1.273 + addstr(" Press 'q' to quit..."); 1.274 + noecho(); 1.275 + } 1.276 + } while (c != 'q'); 1.277 + echo(); 1.278 + 1.279 + gamestate_cleanup(gamestate); 1.280 } 1.281 1.282 void game_start_singlemachine(Settings *settings) { 1.283 inputy = getmaxy(stdscr) - 6; 1.284 1.285 GameState gamestate; 1.286 - memset(&gamestate, 0, sizeof(GameState)); 1.287 - init_board(&gamestate); 1.288 - gamestate.mycolor = WHITE; 1.289 + gamestate_init(&gamestate); 1.290 + uint8_t curcol = WHITE; 1.291 + 1.292 + if (settings->continuepgn) { 1.293 + FILE *pgnfile = fopen(settings->continuepgn, "r"); 1.294 + if (pgnfile) { 1.295 + int result = read_pgn(pgnfile, &gamestate, &(settings->gameinfo)); 1.296 + fclose(pgnfile); 1.297 + if (result != EXIT_SUCCESS) { 1.298 + addstr("Invalid PGN file content.\n"); 1.299 + return; 1.300 + } 1.301 + if (!is_game_running(&gamestate)) { 1.302 + addstr("Game has ended. Use -S to analyze it.\n"); 1.303 + return; 1.304 + } 1.305 + curcol = opponent_color(gamestate.lastmove->move.piece&COLOR_MASK); 1.306 + } else { 1.307 + printw("Can't read PGN file (%s)\n", strerror(errno)); 1.308 + return; 1.309 + } 1.310 + } 1.311 1.312 _Bool running; 1.313 do { 1.314 clear(); 1.315 - draw_board(&gamestate); 1.316 - running = !domove_singlemachine(&gamestate, &(settings->gameinfo)); 1.317 - gamestate.mycolor = opponent_color(gamestate.mycolor); 1.318 + draw_board(&gamestate, curcol); 1.319 + running = !domove_singlemachine(&gamestate, 1.320 + &(settings->gameinfo), curcol); 1.321 + curcol = opponent_color(curcol); 1.322 } while (running); 1.323 - move(0,0); 1.324 - draw_board(&gamestate); 1.325 1.326 - gamestate_cleanup(&gamestate); 1.327 + post_game(&gamestate, &(settings->gameinfo)); 1.328 } 1.329 1.330 void game_start(Settings *settings, int opponent) { 1.331 @@ -461,26 +528,23 @@ 1.332 1.333 _Bool myturn = is_server(settings) == 1.334 (settings->gameinfo.servercolor == WHITE); 1.335 + uint8_t mycolor = myturn ? WHITE : BLACK; 1.336 1.337 GameState gamestate; 1.338 - memset(&gamestate, 0, sizeof(GameState)); 1.339 - init_board(&gamestate); 1.340 - gamestate.mycolor = myturn ? WHITE:BLACK; 1.341 + gamestate_init(&gamestate); 1.342 1.343 _Bool running; 1.344 do { 1.345 clear(); 1.346 - draw_board(&gamestate); 1.347 + draw_board(&gamestate, mycolor); 1.348 if (myturn) { 1.349 - running = !sendmove(&gamestate, &(settings->gameinfo), opponent); 1.350 + running = !sendmove(&gamestate, &(settings->gameinfo), 1.351 + opponent, mycolor); 1.352 } else { 1.353 running = !recvmove(&gamestate, &(settings->gameinfo), opponent); 1.354 } 1.355 myturn ^= TRUE; 1.356 } while (running); 1.357 1.358 - move(0,0); 1.359 - draw_board(&gamestate); 1.360 - 1.361 - gamestate_cleanup(&gamestate); 1.362 + post_game(&gamestate, &(settings->gameinfo)); 1.363 }