| 183 static void save_pgn(GameState *gamestate, GameInfo *gameinfo) { |
183 static void save_pgn(GameState *gamestate, GameInfo *gameinfo) { |
| 184 bool export_comments = prompt_yesno("Export with comments"); |
184 bool export_comments = prompt_yesno("Export with comments"); |
| 185 |
185 |
| 186 printw("\rFilename: "); |
186 printw("\rFilename: "); |
| 187 clrtoeol(); |
187 clrtoeol(); |
| 188 refresh(); |
|
| 189 |
188 |
| 190 char filename[64]; |
189 char filename[64]; |
| 191 int y = getcury(stdscr); |
190 int y = getcury(stdscr); |
| 192 if (getnstr(filename, 64) == OK && filename[0] != '\0') { |
191 if (getnstr(filename, 64) == OK && filename[0] != '\0') { |
| 193 move(y, 0); |
192 move(y, 0); |
| 224 "%s to move: ", curcolorstr); |
223 "%s to move: ", curcolorstr); |
| 225 clrtoeol(); |
224 clrtoeol(); |
| 226 |
225 |
| 227 if (asyncgetnstr(movestr, &bufpos, MOVESTR_BUFLEN)) { |
226 if (asyncgetnstr(movestr, &bufpos, MOVESTR_BUFLEN)) { |
| 228 if (strncmp(movestr, "resign", MOVESTR_BUFLEN) == 0) { |
227 if (strncmp(movestr, "resign", MOVESTR_BUFLEN) == 0) { |
| 229 gamestate->resign = 1; |
228 gamestate->resign = true; |
| 230 printw("%s resigned!", curcolorstr); |
|
| 231 clrtobot(); |
|
| 232 refresh(); |
|
| 233 return 1; |
229 return 1; |
| 234 } else if (strncmp(movestr, "remis", MOVESTR_BUFLEN) == 0) { |
230 } else if (strncmp(movestr, "remis", MOVESTR_BUFLEN) == 0) { |
| 235 gamestate->remis = 1; |
231 gamestate->remis = true; |
| 236 printw("Game ends remis."); |
|
| 237 clrtobot(); |
|
| 238 refresh(); |
|
| 239 return 1; |
232 return 1; |
| 240 } else if (strncmp(movestr, "savepgn", MOVESTR_BUFLEN) == 0) { |
233 } else if (strncmp(movestr, "savepgn", MOVESTR_BUFLEN) == 0) { |
| 241 save_pgn(gamestate, gameinfo); |
234 save_pgn(gamestate, gameinfo); |
| 242 } else { |
235 } else { |
| 243 Move move; |
236 Move move; |
| 245 if (result == VALID_MOVE_SYNTAX) { |
238 if (result == VALID_MOVE_SYNTAX) { |
| 246 result = validate_move(gamestate, &move); |
239 result = validate_move(gamestate, &move); |
| 247 if (result == VALID_MOVE_SEMANTICS) { |
240 if (result == VALID_MOVE_SEMANTICS) { |
| 248 apply_move(gamestate, &move); |
241 apply_move(gamestate, &move); |
| 249 if (gamestate->checkmate) { |
242 if (gamestate->checkmate) { |
| 250 printw("Checkmate!"); |
|
| 251 clrtoeol(); |
|
| 252 return 1; |
243 return 1; |
| 253 } else if (gamestate->stalemate) { |
244 } else if (gamestate->stalemate) { |
| 254 printw("Stalemate!"); |
|
| 255 clrtoeol(); |
|
| 256 return 1; |
245 return 1; |
| 257 } else { |
246 } else { |
| 258 return 0; |
247 return 0; |
| 259 } |
248 } |
| 260 } else { |
249 } else { |
| 345 /* read move */ |
331 /* read move */ |
| 346 if (use_premove || asyncgetnstr(movestr, &bufpos, MOVESTR_BUFLEN)) { |
332 if (use_premove || asyncgetnstr(movestr, &bufpos, MOVESTR_BUFLEN)) { |
| 347 bool was_premove = use_premove; |
333 bool was_premove = use_premove; |
| 348 use_premove = false; |
334 use_premove = false; |
| 349 if (strncmp(movestr, "resign", MOVESTR_BUFLEN) == 0) { |
335 if (strncmp(movestr, "resign", MOVESTR_BUFLEN) == 0) { |
| 350 gamestate->resign = 1; |
336 gamestate->resign = true; |
| 351 printw("You resigned!"); |
|
| 352 clrtoeol(); |
|
| 353 refresh(); |
|
| 354 net_send_code(opponent, NETCODE_RESIGN); |
337 net_send_code(opponent, NETCODE_RESIGN); |
| 355 return 1; |
338 return 1; |
| 356 } else if (strncmp(movestr, "savepgn", MOVESTR_BUFLEN) == 0) { |
339 } else if (strncmp(movestr, "savepgn", MOVESTR_BUFLEN) == 0) { |
| 357 save_pgn(gamestate, gameinfo); |
340 save_pgn(gamestate, gameinfo); |
| 358 } else if (strncmp(movestr, "remis", MOVESTR_BUFLEN) == 0) { |
341 } else if (strncmp(movestr, "remis", MOVESTR_BUFLEN) == 0) { |
| 359 if (remis_suggested) { |
342 if (remis_suggested) { |
| 360 net_send_code(opponent, NETCODE_REMIS); |
343 net_send_code(opponent, NETCODE_REMIS); |
| 361 gamestate->remis = 1; |
344 gamestate->remis = true; |
| 362 printw("\rRemis accepted!"); |
|
| 363 clrtoeol(); |
|
| 364 refresh(); |
|
| 365 return 1; |
345 return 1; |
| 366 } if (!remis_rejected) { |
346 } if (!remis_rejected) { |
| 367 net_send_code(opponent, NETCODE_REMIS); |
347 net_send_code(opponent, NETCODE_REMIS); |
| 368 printw("Remis offer sent - waiting for acceptance..."); |
348 printw("Remis offer sent - waiting for acceptance..."); |
| 369 refresh(); |
349 refresh(); |
| 370 code = net_recieve_code(opponent); |
350 code = net_recieve_code(opponent); |
| 371 if (code == NETCODE_ACCEPT) { |
351 if (code == NETCODE_ACCEPT) { |
| 372 gamestate->remis = 1; |
352 gamestate->remis = true; |
| 373 printw("\rRemis accepted!"); |
|
| 374 clrtoeol(); |
|
| 375 refresh(); |
|
| 376 return 1; |
353 return 1; |
| 377 } else if (code == NETCODE_CONNLOST) { |
354 } else if (code == NETCODE_CONNLOST) { |
| 378 printw("\rYour opponent left the game."); |
355 gamestate->ragequit = true; |
| 379 clrtoeol(); |
|
| 380 refresh(); |
|
| 381 return 1; |
356 return 1; |
| 382 } else { |
357 } else { |
| 383 remis_rejected = true; |
358 remis_rejected = true; |
| 384 } |
359 } |
| 385 } |
360 } |
| 402 } else if (code == NETCODE_ACCEPT |
377 } else if (code == NETCODE_ACCEPT |
| 403 || code == NETCODE_CHECK |
378 || code == NETCODE_CHECK |
| 404 || code == NETCODE_CHECKMATE |
379 || code == NETCODE_CHECKMATE |
| 405 || code == NETCODE_STALEMATE) { |
380 || code == NETCODE_STALEMATE) { |
| 406 apply_move(gamestate, &move); |
381 apply_move(gamestate, &move); |
| 407 if (gamestate->checkmate) { |
382 if (gamestate->checkmate || gamestate->stalemate) { |
| 408 printw("Checkmate!"); |
|
| 409 clrtoeol(); |
|
| 410 return 1; |
|
| 411 } else if (gamestate->stalemate) { |
|
| 412 printw("Stalemate!"); |
|
| 413 clrtoeol(); |
|
| 414 return 1; |
383 return 1; |
| 415 } else { |
384 } else { |
| 416 return 0; |
385 return 0; |
| 417 } |
386 } |
| 418 } else if (code == NETCODE_CONNLOST) { |
387 } else if (code == NETCODE_CONNLOST) { |
| 466 refresh(); |
435 refresh(); |
| 467 |
436 |
| 468 /* allow the player to prepare a move */ |
437 /* allow the player to prepare a move */ |
| 469 if (asyncgetnstr(movestr, &bufpos, MOVESTR_BUFLEN)) { |
438 if (asyncgetnstr(movestr, &bufpos, MOVESTR_BUFLEN)) { |
| 470 if (strncmp(movestr, "resign", MOVESTR_BUFLEN) == 0) { |
439 if (strncmp(movestr, "resign", MOVESTR_BUFLEN) == 0) { |
| 471 gamestate->resign = 1; |
440 gamestate->resign = true; |
| 472 printw("You resigned!"); |
|
| 473 clrtoeol(); |
|
| 474 refresh(); |
|
| 475 net_send_code(opponent, NETCODE_RESIGN); |
441 net_send_code(opponent, NETCODE_RESIGN); |
| 476 return 1; |
442 return 1; |
| 477 } else if (strncmp(movestr, "taunt", MOVESTR_BUFLEN) == 0) { |
443 } else if (strncmp(movestr, "taunt", MOVESTR_BUFLEN) == 0) { |
| 478 resign_suggested = true; |
444 resign_suggested = true; |
| 479 net_send_code(opponent, NETCODE_TAUNT); |
445 net_send_code(opponent, NETCODE_TAUNT); |
| 503 case NETCODE_TIMEOVER: |
469 case NETCODE_TIMEOVER: |
| 504 /* redraw the time control */ |
470 /* redraw the time control */ |
| 505 timecontrol(gamestate, gameinfo); |
471 timecontrol(gamestate, gameinfo); |
| 506 return 1; |
472 return 1; |
| 507 case NETCODE_RESIGN: |
473 case NETCODE_RESIGN: |
| 508 gamestate->resign = 1; |
474 gamestate->resign = true; |
| 509 printw("\rYour opponent resigned!"); |
|
| 510 clrtoeol(); |
|
| 511 return 1; |
475 return 1; |
| 512 case NETCODE_CONNLOST: |
476 case NETCODE_CONNLOST: |
| 513 printw("\rYour opponent has left the game."); |
477 gamestate->ragequit = true; |
| 514 clrtoeol(); |
|
| 515 return 1; |
478 return 1; |
| 516 case NETCODE_REMIS: |
479 case NETCODE_REMIS: |
| 517 if (remis_suggested) { |
480 if (remis_suggested) { |
| 518 gamestate->remis = 1; |
481 gamestate->remis = true; |
| 519 printw("\rRemis accepted!"); |
|
| 520 clrtoeol(); |
|
| 521 return 1; |
482 return 1; |
| 522 } else { |
483 } else { |
| 523 if (prompt_yesno( |
484 if (prompt_yesno( |
| 524 "\rYour opponent offers remis - do you accept")) { |
485 "\rYour opponent offers remis - do you accept")) { |
| 525 gamestate->remis = 1; |
486 gamestate->remis = true; |
| 526 printw("\rRemis accepted!"); |
|
| 527 clrtoeol(); |
|
| 528 net_send_code(opponent, NETCODE_ACCEPT); |
487 net_send_code(opponent, NETCODE_ACCEPT); |
| 529 return 1; |
488 return 1; |
| 530 } else { |
489 } else { |
| 531 net_send_code(opponent, NETCODE_DECLINE); |
490 net_send_code(opponent, NETCODE_DECLINE); |
| 532 } |
491 } |
| 538 code = validate_move(gamestate, &move); |
497 code = validate_move(gamestate, &move); |
| 539 if (code == VALID_MOVE_SEMANTICS) { |
498 if (code == VALID_MOVE_SEMANTICS) { |
| 540 apply_move(gamestate, &move); |
499 apply_move(gamestate, &move); |
| 541 if (gamestate->checkmate) { |
500 if (gamestate->checkmate) { |
| 542 net_send_code(opponent, NETCODE_CHECKMATE); |
501 net_send_code(opponent, NETCODE_CHECKMATE); |
| 543 printw("\rCheckmate!"); |
|
| 544 clrtoeol(); |
|
| 545 return 1; |
502 return 1; |
| 546 } else if (gamestate->stalemate) { |
503 } else if (gamestate->stalemate) { |
| 547 net_send_code(opponent, NETCODE_STALEMATE); |
504 net_send_code(opponent, NETCODE_STALEMATE); |
| 548 printw("\rStalemate!"); |
|
| 549 clrtoeol(); |
|
| 550 return 1; |
505 return 1; |
| 551 } else if (move.check) { |
506 } else if (move.check) { |
| 552 net_send_code(opponent, NETCODE_CHECK); |
507 net_send_code(opponent, NETCODE_CHECK); |
| 553 } else { |
508 } else { |
| 554 net_send_code(opponent, NETCODE_ACCEPT); |
509 net_send_code(opponent, NETCODE_ACCEPT); |
| 573 } |
528 } |
| 574 } |
529 } |
| 575 } |
530 } |
| 576 |
531 |
| 577 void game_review(Settings* settings, GameState *gamestate) { |
532 void game_review(Settings* settings, GameState *gamestate) { |
| |
533 const unsigned page_moves = 10; |
| 578 GameInfo *gameinfo = &(settings->gameinfo); |
534 GameInfo *gameinfo = &(settings->gameinfo); |
| 579 |
535 GameState viewedstate = {0}; |
| 580 move(0,0); |
536 unsigned viewedmove = gamestate->movecount; |
| 581 draw_board(gamestate, WHITE, settings->unicode); |
537 bool redraw = true; |
| 582 |
538 |
| 583 mvaddstr(getmaxy(stdscr)-1, 0, |
|
| 584 "Press 'q' to quit or 's' to save a PGN file..."); |
|
| 585 refresh(); |
|
| 586 flushinp(); |
|
| 587 |
|
| 588 noecho(); |
539 noecho(); |
| 589 int c; |
540 int c; |
| 590 do { |
541 do { |
| |
542 if (redraw) { |
| |
543 gamestate_cleanup(&viewedstate); |
| |
544 gamestate_at_move(gamestate, viewedmove, &viewedstate); |
| |
545 |
| |
546 erase(); /* don't use clear() to avoid flickering */ |
| |
547 draw_board(&viewedstate, WHITE, settings->unicode); |
| |
548 timecontrol(&viewedstate, gameinfo); |
| |
549 |
| |
550 move(getmaxy(stdscr)-5, 0); |
| |
551 const char *curcolorstr = |
| |
552 gamestate->movecount % 2 == 0 ? "White" : "Black"; |
| |
553 if (gamestate->resign) { |
| |
554 printw("%s resigned.\n", curcolorstr); |
| |
555 } else if (gamestate->remis) { |
| |
556 addstr("The game ended remis.\n"); |
| |
557 } else if (gamestate->stalemate) { |
| |
558 addstr("The game ended in a stalemate.\n"); |
| |
559 } else if (gamestate->checkmate) { |
| |
560 printw("%s has lost the game.\n", curcolorstr); |
| |
561 } else if (gamestate->ragequit) { |
| |
562 printw("Your opponent disconnected.\n"); |
| |
563 } |
| |
564 addstr("\nPress 'q' to quit, 's' to save the position as PGN, or\n" |
| |
565 "arrow keys, home/end, page up/down to review the game.\n"); |
| |
566 flushinp(); |
| |
567 redraw = false; |
| |
568 } |
| 591 c = getch(); |
569 c = getch(); |
| 592 if (c == 's') { |
570 if (c == 's') { |
| 593 addch('\r'); |
571 addch('\r'); |
| 594 echo(); |
572 echo(); |
| 595 save_pgn(gamestate, gameinfo); |
573 save_pgn(&viewedstate, gameinfo); |
| 596 addstr(" Press 'q' to quit..."); |
|
| 597 noecho(); |
574 noecho(); |
| |
575 redraw = true; |
| |
576 } else if (c == KEY_UP || c == KEY_LEFT) { |
| |
577 if (viewedmove > 0) { |
| |
578 viewedmove--; |
| |
579 redraw = true; |
| |
580 } |
| |
581 } else if (c == KEY_DOWN || c == KEY_RIGHT) { |
| |
582 if (viewedmove < gamestate->movecount) { |
| |
583 viewedmove++; |
| |
584 redraw = true; |
| |
585 } |
| |
586 } else if (c == KEY_HOME) { |
| |
587 viewedmove = 0; |
| |
588 redraw = true; |
| |
589 } else if (c == KEY_END) { |
| |
590 viewedmove = gamestate->movecount; |
| |
591 redraw = true; |
| |
592 } else if (c == KEY_PPAGE) { |
| |
593 if (viewedmove > page_moves) { |
| |
594 viewedmove -= page_moves; |
| |
595 } else { |
| |
596 viewedmove = 0; |
| |
597 } |
| |
598 redraw = true; |
| |
599 } else if (c == KEY_NPAGE) { |
| |
600 viewedmove += page_moves; |
| |
601 if (viewedmove > gamestate->movecount) { |
| |
602 viewedmove = gamestate->movecount; |
| |
603 } |
| |
604 redraw = true; |
| 598 } |
605 } |
| 599 } while (c != 'q'); |
606 } while (c != 'q'); |
| 600 echo(); |
607 echo(); |
| 601 |
608 gamestate_cleanup(&viewedstate); |
| 602 gamestate_cleanup(gamestate); |
|
| 603 } |
609 } |
| 604 |
610 |
| 605 void game_play_singlemachine(Settings *settings) { |
611 void game_play_singlemachine(Settings *settings) { |
| 606 inputy = getmaxy(stdscr) - 6; |
612 inputy = getmaxy(stdscr) - 6; |
| 607 |
613 |
| 640 &(settings->gameinfo), curcol); |
646 &(settings->gameinfo), curcol); |
| 641 curcol = opponent_color(curcol); |
647 curcol = opponent_color(curcol); |
| 642 } while (running); |
648 } while (running); |
| 643 |
649 |
| 644 game_review(settings, &gamestate); |
650 game_review(settings, &gamestate); |
| |
651 gamestate_cleanup(&gamestate); |
| 645 } |
652 } |
| 646 |
653 |
| 647 void game_play(Settings *settings, GameState *gamestate, int opponent) { |
654 void game_play(Settings *settings, GameState *gamestate, int opponent) { |
| 648 inputy = getmaxy(stdscr) - 6; |
655 inputy = getmaxy(stdscr) - 6; |
| 649 |
656 |