src/game.c

changeset 50
41017d0a72c5
parent 49
02c509a44e98
child 51
84f2e380a434
equal deleted inserted replaced
49:02c509a44e98 50:41017d0a72c5
33 #include "colors.h" 33 #include "colors.h"
34 #include <ncurses.h> 34 #include <ncurses.h>
35 #include <string.h> 35 #include <string.h>
36 #include <inttypes.h> 36 #include <inttypes.h>
37 #include <sys/select.h> 37 #include <sys/select.h>
38 #include <stdio.h>
39 #include <errno.h>
38 40
39 static const uint8_t boardx = 10, boardy = 10; 41 static const uint8_t boardx = 10, boardy = 10;
40 static int inputy = 21; /* should be overridden on game startup */ 42 static int inputy = 21; /* should be overridden on game startup */
41 43
42 static int timecontrol(GameState *gamestate, GameInfo *gameinfo) { 44 static int timecontrol(GameState *gamestate, GameInfo *gameinfo) {
67 } 69 }
68 70
69 return 0; 71 return 0;
70 } 72 }
71 73
72 static void draw_board(GameState *gamestate) { 74 static void draw_board(GameState *gamestate, uint8_t perspective) {
73 for (uint8_t y = 0 ; y < 8 ; y++) { 75 for (uint8_t y = 0 ; y < 8 ; y++) {
74 for (uint8_t x = 0 ; x < 8 ; x++) { 76 for (uint8_t x = 0 ; x < 8 ; x++) {
75 uint8_t col = gamestate->board[y][x] & COLOR_MASK; 77 uint8_t col = gamestate->board[y][x] & COLOR_MASK;
76 uint8_t piece = gamestate->board[y][x] & PIECE_MASK; 78 uint8_t piece = gamestate->board[y][x] & PIECE_MASK;
77 char piecec; 79 char piecec;
87 (boardblack ? COL_WB : COL_WW) : 89 (boardblack ? COL_WB : COL_WW) :
88 (boardblack ? COL_BB : COL_BW) 90 (boardblack ? COL_BB : COL_BW)
89 ) 91 )
90 ); 92 );
91 93
92 int cy = gamestate->mycolor == WHITE ? boardy-y : boardy-7+y; 94 int cy = perspective == WHITE ? boardy-y : boardy-7+y;
93 int cx = gamestate->mycolor == WHITE ? boardx+x*3 : boardx+21-x*3; 95 int cx = perspective == WHITE ? boardx+x*3 : boardx+21-x*3;
94 mvaddch(cy, cx, ' '); 96 mvaddch(cy, cx, ' ');
95 mvaddch(cy, cx+1, piecec); 97 mvaddch(cy, cx+1, piecec);
96 mvaddch(cy, cx+2, ' '); 98 mvaddch(cy, cx+2, ' ');
97 } 99 }
98 } 100 }
99 101
100 attrset(A_NORMAL); 102 attrset(A_NORMAL);
101 for (uint8_t i = 0 ; i < 8 ; i++) { 103 for (uint8_t i = 0 ; i < 8 ; i++) {
102 int x = gamestate->mycolor == WHITE ? boardx+i*3+1 : boardx+22-i*3; 104 int x = perspective == WHITE ? boardx+i*3+1 : boardx+22-i*3;
103 int y = gamestate->mycolor == WHITE ? boardy-i : boardy-7+i; 105 int y = perspective == WHITE ? boardy-i : boardy-7+i;
104 mvaddch(boardy+1, x, 'a'+i); 106 mvaddch(boardy+1, x, 'a'+i);
105 mvaddch(y, boardx-2, '1'+i); 107 mvaddch(y, boardx-2, '1'+i);
106 } 108 }
107 109
108 /* move log */ 110 /* move log */
120 move(logy, logx); 122 move(logy, logx);
121 } 123 }
122 printw("%d. ", logi / 2); 124 printw("%d. ", logi / 2);
123 } 125 }
124 126
125 if (logelem) { 127 addstr(logelem->move.string);
126 addstr(logelem->move.string); 128 if (!logelem->next) {
127 if (!logelem->next) { 129 if (gamestate->stalemate) {
128 if (gamestate->stalemate) { 130 addstr(" stalemate");
129 addstr(" stalemate"); 131 }
130 } 132 }
131 } 133 addch(' ');
132 addch(' '); 134
133 135 logelem = logelem->next;
134 logelem = logelem->next;
135 }
136 } 136 }
137 } 137 }
138 138
139 static void eval_move_failed_msg(int code) { 139 static void eval_move_failed_msg(int code) {
140 switch (code) { 140 switch (code) {
165 default: 165 default:
166 printw("Unknown move parser error."); 166 printw("Unknown move parser error.");
167 } 167 }
168 } 168 }
169 169
170 #define MOVESTR_BUFLEN 8 170 static void save_pgn(GameState *gamestate, GameInfo *gameinfo) {
171 static int domove_singlemachine(GameState *gamestate, GameInfo *gameinfo) { 171 printw("Filename: ");
172 clrtoeol();
173 refresh();
174
175 char filename[64];
176 int y = getcury(stdscr);
177 if (getnstr(filename, 64) == OK) {
178 move(y, 0);
179 FILE *file = fopen(filename, "w");
180 if (file) {
181 write_pgn(file, gamestate, gameinfo);
182 fclose(file);
183 printw("File saved.");
184 } else {
185 printw("Can't write to file (%s).", strerror(errno));
186 }
187 clrtoeol();
188 }
189 }
190
191 #define MOVESTR_BUFLEN 10
192 static int domove_singlemachine(GameState *gamestate,
193 GameInfo *gameinfo, uint8_t curcolor) {
172 194
173 195
174 size_t bufpos = 0; 196 size_t bufpos = 0;
175 char movestr[MOVESTR_BUFLEN]; 197 char movestr[MOVESTR_BUFLEN];
176 198
181 } 203 }
182 204
183 move(inputy, 0); 205 move(inputy, 0);
184 printw( 206 printw(
185 "Use chess notation to enter your move.\n" 207 "Use chess notation to enter your move.\n"
186 "Or type 'resign' to resign or 'remis' to end with remis.\n\n" 208 "Or use a command: remis, resign, savepgn\n\n"
187 "Type your move: "); 209 "Type your move: ");
188 clrtoeol(); 210 clrtoeol();
189 211
190 if (asyncgetnstr(movestr, &bufpos, MOVESTR_BUFLEN)) { 212 if (asyncgetnstr(movestr, &bufpos, MOVESTR_BUFLEN)) {
191 if (strncmp(movestr, "resign", MOVESTR_BUFLEN) == 0) { 213 if (strncmp(movestr, "resign", MOVESTR_BUFLEN) == 0) {
214 gamestate->resign = 1;
192 printw("%s resigned!", 215 printw("%s resigned!",
193 gamestate->mycolor==WHITE?"White":"Black"); 216 curcolor==WHITE?"White":"Black");
194 clrtoeol(); 217 clrtobot();
195 refresh(); 218 refresh();
196 return 1; 219 return 1;
197 } else if (strncmp(movestr, "remis", MOVESTR_BUFLEN) == 0) { 220 } else if (strncmp(movestr, "remis", MOVESTR_BUFLEN) == 0) {
221 gamestate->remis = 1;
198 printw("Game ends remis."); 222 printw("Game ends remis.");
199 clrtoeol(); 223 clrtobot();
200 refresh(); 224 refresh();
201 return 1; 225 return 1;
226 } else if (strncmp(movestr, "savepgn", MOVESTR_BUFLEN) == 0) {
227 save_pgn(gamestate, gameinfo);
202 } else { 228 } else {
203 Move move; 229 Move move;
204 int eval_result = eval_move(gamestate, movestr, &move); 230 int result = eval_move(gamestate, movestr, &move, curcolor);
205 switch (eval_result) { 231 switch (result) {
206 case VALID_MOVE_SYNTAX: 232 case VALID_MOVE_SYNTAX:
207 eval_result = validate_move(gamestate, &move); 233 result = validate_move(gamestate, &move);
208 if (eval_result == VALID_MOVE_SEMANTICS) { 234 if (result == VALID_MOVE_SEMANTICS) {
209 apply_move(gamestate, &move); 235 apply_move(gamestate, &move);
210 if (gamestate->checkmate) { 236 if (gamestate->checkmate) {
211 printw("Checkmate!"); 237 printw("Checkmate!");
212 clrtoeol(); 238 clrtoeol();
213 return 1; 239 return 1;
217 return 1; 243 return 1;
218 } else { 244 } else {
219 return 0; 245 return 0;
220 } 246 }
221 } else { 247 } else {
222 eval_move_failed_msg(eval_result); 248 eval_move_failed_msg(result);
223 } 249 }
224 break; 250 break;
225 default: 251 default:
226 eval_move_failed_msg(eval_result); 252 eval_move_failed_msg(result);
227 } 253 }
228 clrtoeol(); 254 clrtoeol();
229 } 255 }
230 } 256 }
231 } 257 }
232 } 258 }
233 259
234 static int sendmove(GameState *gamestate, GameInfo *gameinfo, int opponent) { 260 static int sendmove(GameState *gamestate, GameInfo *gameinfo,
261 int opponent, uint8_t mycolor) {
235 262
236 size_t bufpos = 0; 263 size_t bufpos = 0;
237 char movestr[MOVESTR_BUFLEN]; 264 char movestr[MOVESTR_BUFLEN];
238 _Bool remisrejected = FALSE; 265 _Bool remisrejected = FALSE;
239 uint8_t code; 266 uint8_t code;
247 274
248 move(inputy, 0); 275 move(inputy, 0);
249 if (remisrejected) { 276 if (remisrejected) {
250 printw( 277 printw(
251 "Use chess notation to enter your move.\n" 278 "Use chess notation to enter your move.\n"
252 "Remis offer rejected - type 'resign' to resign. \n\n" 279 "Remis offer rejected \n\n"
253 "Type your move: "); 280 "Type your move: ");
254 } else { 281 } else {
255 printw( 282 printw(
256 "Use chess notation to enter your move.\n" 283 "Use chess notation to enter your move.\n"
257 "Or type 'resign' to resign or 'remis' to offer remis.\n\n" 284 "Or use a command: remis, resign, savepgn\n\n"
258 "Type your move: "); 285 "Type your move: ");
259 } 286 }
260 clrtoeol(); 287 clrtoeol();
261 288
262 if (asyncgetnstr(movestr, &bufpos, MOVESTR_BUFLEN)) { 289 if (asyncgetnstr(movestr, &bufpos, MOVESTR_BUFLEN)) {
263 if (strncmp(movestr, "resign", MOVESTR_BUFLEN) == 0) { 290 if (strncmp(movestr, "resign", MOVESTR_BUFLEN) == 0) {
291 gamestate->resign = 1;
264 printw("You resigned!"); 292 printw("You resigned!");
265 clrtoeol(); 293 clrtoeol();
266 refresh(); 294 refresh();
267 net_send_code(opponent, NETCODE_RESIGN); 295 net_send_code(opponent, NETCODE_RESIGN);
268 return 1; 296 return 1;
297 } else if (strncmp(movestr, "savepgn", MOVESTR_BUFLEN) == 0) {
298 save_pgn(gamestate, gameinfo);
269 } else if (strncmp(movestr, "remis", MOVESTR_BUFLEN) == 0) { 299 } else if (strncmp(movestr, "remis", MOVESTR_BUFLEN) == 0) {
270 if (!remisrejected) { 300 if (!remisrejected) {
271 net_send_code(opponent, NETCODE_REMIS); 301 net_send_code(opponent, NETCODE_REMIS);
272 printw("Remis offer sent - waiting for acceptance..."); 302 printw("Remis offer sent - waiting for acceptance...");
273 refresh(); 303 refresh();
274 code = net_recieve_code(opponent); 304 code = net_recieve_code(opponent);
275 if (code == NETCODE_ACCEPT) { 305 if (code == NETCODE_ACCEPT) {
306 gamestate->remis = 1;
276 printw("\rRemis accepted!"); 307 printw("\rRemis accepted!");
277 clrtoeol(); 308 clrtoeol();
278 refresh(); 309 refresh();
279 return 1; 310 return 1;
280 } else if (code == NETCODE_CONNLOST) { 311 } else if (code == NETCODE_CONNLOST) {
286 remisrejected = TRUE; 317 remisrejected = TRUE;
287 } 318 }
288 } 319 }
289 } else { 320 } else {
290 Move move; 321 Move move;
291 int eval_result = eval_move(gamestate, movestr, &move); 322 int eval_result = eval_move(gamestate, movestr, &move, mycolor);
292 switch (eval_result) { 323 switch (eval_result) {
293 case VALID_MOVE_SYNTAX: 324 case VALID_MOVE_SYNTAX:
294 net_send_data(opponent, NETCODE_MOVE, &move, 325 net_send_data(opponent, NETCODE_MOVE, &move,
295 sizeof(Move)-8); 326 sizeof(Move)-8);
296 code = net_recieve_code(opponent); 327 code = net_recieve_code(opponent);
351 FD_ZERO(&readfds); 382 FD_ZERO(&readfds);
352 FD_SET(opponent, &readfds); 383 FD_SET(opponent, &readfds);
353 timeout.tv_sec = 0; 384 timeout.tv_sec = 0;
354 timeout.tv_usec = 1e5; 385 timeout.tv_usec = 1e5;
355 386
387 // TODO: allow commands
388
356 int result = select(opponent+1, &readfds, NULL, NULL, &timeout); 389 int result = select(opponent+1, &readfds, NULL, NULL, &timeout);
357 if (result == -1) { 390 if (result == -1) {
358 printw("\rCannot perform asynchronous network IO"); 391 printw("\rCannot perform asynchronous network IO");
359 cbreak(); getch(); 392 cbreak(); getch();
360 exit(EXIT_FAILURE); 393 exit(EXIT_FAILURE);
367 case NETCODE_TIMEOVER: 400 case NETCODE_TIMEOVER:
368 printw("\rYour opponent's time ran out - you win!"); 401 printw("\rYour opponent's time ran out - you win!");
369 clrtoeol(); 402 clrtoeol();
370 return 1; 403 return 1;
371 case NETCODE_RESIGN: 404 case NETCODE_RESIGN:
405 gamestate->resign = 1;
372 printw("\rYour opponent resigned!"); 406 printw("\rYour opponent resigned!");
373 clrtoeol(); 407 clrtoeol();
374 return 1; 408 return 1;
375 case NETCODE_CONNLOST: 409 case NETCODE_CONNLOST:
376 printw("\rYour opponent has left the game."); 410 printw("\rYour opponent has left the game.");
377 clrtoeol(); 411 clrtoeol();
378 return 1; 412 return 1;
379 case NETCODE_REMIS: 413 case NETCODE_REMIS:
380 if (prompt_yesno( 414 if (prompt_yesno(
381 "\rYour opponent offers remis - do you accept")) { 415 "\rYour opponent offers remis - do you accept")) {
416 gamestate->remis = 1;
382 printw("\rRemis accepted!"); 417 printw("\rRemis accepted!");
383 clrtoeol(); 418 clrtoeol();
384 net_send_code(opponent, NETCODE_ACCEPT); 419 net_send_code(opponent, NETCODE_ACCEPT);
385 return 1; 420 return 1;
386 } else { 421 } else {
419 } 454 }
420 } 455 }
421 } 456 }
422 } 457 }
423 458
424 static void init_board(GameState *gamestate) { 459 static void post_game(GameState *gamestate, GameInfo *gameinfo) {
425 Board initboard = { 460 move(0,0);
426 {WROOK, WKNIGHT, WBISHOP, WQUEEN, WKING, WBISHOP, WKNIGHT, WROOK}, 461 draw_board(gamestate, WHITE);
427 {WPAWN, WPAWN, WPAWN, WPAWN, WPAWN, WPAWN, WPAWN, WPAWN}, 462
428 {0, 0, 0, 0, 0, 0, 0, 0}, 463 // TODO: network connection is still open here - think about it!
429 {0, 0, 0, 0, 0, 0, 0, 0}, 464
430 {0, 0, 0, 0, 0, 0, 0, 0}, 465 mvaddstr(getmaxy(stdscr)-1, 0,
431 {0, 0, 0, 0, 0, 0, 0, 0}, 466 "Press 'q' to quit or 's' to save a PGN file...");
432 {BPAWN, BPAWN, BPAWN, BPAWN, BPAWN, BPAWN, BPAWN, BPAWN}, 467 refresh();
433 {BROOK, BKNIGHT, BBISHOP, BQUEEN, BKING, BBISHOP, BKNIGHT, BROOK} 468 flushinp();
434 }; 469
435 memcpy(gamestate->board, initboard, sizeof(Board)); 470 noecho();
471 int c;
472 do {
473 c = getch();
474 if (c == 's') {
475 addch('\r');
476 echo();
477 save_pgn(gamestate, gameinfo);
478 addstr(" Press 'q' to quit...");
479 noecho();
480 }
481 } while (c != 'q');
482 echo();
483
484 gamestate_cleanup(gamestate);
436 } 485 }
437 486
438 void game_start_singlemachine(Settings *settings) { 487 void game_start_singlemachine(Settings *settings) {
439 inputy = getmaxy(stdscr) - 6; 488 inputy = getmaxy(stdscr) - 6;
440 489
441 GameState gamestate; 490 GameState gamestate;
442 memset(&gamestate, 0, sizeof(GameState)); 491 gamestate_init(&gamestate);
443 init_board(&gamestate); 492 uint8_t curcol = WHITE;
444 gamestate.mycolor = WHITE; 493
494 if (settings->continuepgn) {
495 FILE *pgnfile = fopen(settings->continuepgn, "r");
496 if (pgnfile) {
497 int result = read_pgn(pgnfile, &gamestate, &(settings->gameinfo));
498 fclose(pgnfile);
499 if (result != EXIT_SUCCESS) {
500 addstr("Invalid PGN file content.\n");
501 return;
502 }
503 if (!is_game_running(&gamestate)) {
504 addstr("Game has ended. Use -S to analyze it.\n");
505 return;
506 }
507 curcol = opponent_color(gamestate.lastmove->move.piece&COLOR_MASK);
508 } else {
509 printw("Can't read PGN file (%s)\n", strerror(errno));
510 return;
511 }
512 }
445 513
446 _Bool running; 514 _Bool running;
447 do { 515 do {
448 clear(); 516 clear();
449 draw_board(&gamestate); 517 draw_board(&gamestate, curcol);
450 running = !domove_singlemachine(&gamestate, &(settings->gameinfo)); 518 running = !domove_singlemachine(&gamestate,
451 gamestate.mycolor = opponent_color(gamestate.mycolor); 519 &(settings->gameinfo), curcol);
520 curcol = opponent_color(curcol);
452 } while (running); 521 } while (running);
453 move(0,0); 522
454 draw_board(&gamestate); 523 post_game(&gamestate, &(settings->gameinfo));
455
456 gamestate_cleanup(&gamestate);
457 } 524 }
458 525
459 void game_start(Settings *settings, int opponent) { 526 void game_start(Settings *settings, int opponent) {
460 inputy = getmaxy(stdscr) - 6; 527 inputy = getmaxy(stdscr) - 6;
461 528
462 _Bool myturn = is_server(settings) == 529 _Bool myturn = is_server(settings) ==
463 (settings->gameinfo.servercolor == WHITE); 530 (settings->gameinfo.servercolor == WHITE);
531 uint8_t mycolor = myturn ? WHITE : BLACK;
464 532
465 GameState gamestate; 533 GameState gamestate;
466 memset(&gamestate, 0, sizeof(GameState)); 534 gamestate_init(&gamestate);
467 init_board(&gamestate);
468 gamestate.mycolor = myturn ? WHITE:BLACK;
469 535
470 _Bool running; 536 _Bool running;
471 do { 537 do {
472 clear(); 538 clear();
473 draw_board(&gamestate); 539 draw_board(&gamestate, mycolor);
474 if (myturn) { 540 if (myturn) {
475 running = !sendmove(&gamestate, &(settings->gameinfo), opponent); 541 running = !sendmove(&gamestate, &(settings->gameinfo),
542 opponent, mycolor);
476 } else { 543 } else {
477 running = !recvmove(&gamestate, &(settings->gameinfo), opponent); 544 running = !recvmove(&gamestate, &(settings->gameinfo), opponent);
478 } 545 }
479 myturn ^= TRUE; 546 myturn ^= TRUE;
480 } while (running); 547 } while (running);
481 548
482 move(0,0); 549 post_game(&gamestate, &(settings->gameinfo));
483 draw_board(&gamestate); 550 }
484
485 gamestate_cleanup(&gamestate);
486 }

mercurial