| 258 uint16_t white = remaining_movetime(gamestate, WHITE); |
258 uint16_t white = remaining_movetime(gamestate, WHITE); |
| 259 uint16_t black = remaining_movetime(gamestate, BLACK); |
259 uint16_t black = remaining_movetime(gamestate, BLACK); |
| 260 char clkstr[16]; |
260 char clkstr[16]; |
| 261 bool always_hours = gamestate->info.time >= 3600; |
261 bool always_hours = gamestate->info.time >= 3600; |
| 262 print_clk(white, clkstr, always_hours); |
262 print_clk(white, clkstr, always_hours); |
| 263 mvprintw(boardy+4, boardx-1, "White time: %s", clkstr); |
263 mvaddstr(boardy+4, boardx-1, "White time: "); |
| |
264 addstr(clkstr); |
| 264 print_clk(black, clkstr, always_hours); |
265 print_clk(black, clkstr, always_hours); |
| 265 mvprintw(boardy+5, boardx-1, "Black time: %s", clkstr); |
266 mvaddstr(boardy+5, boardx-1, "Black time: "); |
| |
267 addstr(clkstr); |
| 266 |
268 |
| 267 if (white == 0) { |
269 if (white == 0) { |
| 268 move(inputy, 0); |
270 move(inputy, 0); |
| 269 printw("Time is over - Black wins!"); |
271 addstr("Time is over - Black wins!"); |
| 270 clrtobot(); |
272 clrtobot(); |
| 271 refresh(); |
273 refresh(); |
| 272 return 1; |
274 return 1; |
| 273 } |
275 } |
| 274 if (black == 0) { |
276 if (black == 0) { |
| 275 move(inputy, 0); |
277 move(inputy, 0); |
| 276 printw("Time is over - White wins!"); |
278 addstr("Time is over - White wins!"); |
| 277 clrtobot(); |
279 clrtobot(); |
| 278 refresh(); |
280 refresh(); |
| 279 return 1; |
281 return 1; |
| 280 } |
282 } |
| 281 } |
283 } |
| 318 ) |
320 ) |
| 319 ); |
321 ); |
| 320 |
322 |
| 321 int cy = perspective == WHITE ? boardy-y : boardy-7+y; |
323 int cy = perspective == WHITE ? boardy-y : boardy-7+y; |
| 322 int cx = perspective == WHITE ? boardx+x*3 : boardx+21-x*3; |
324 int cx = perspective == WHITE ? boardx+x*3 : boardx+21-x*3; |
| 323 mvprintw(cy, cx, " %s ", piecestr); |
325 mvaddch(cy, cx, ' '); |
| |
326 addstr(piecestr); |
| |
327 addch(' '); |
| 324 } |
328 } |
| 325 } |
329 } |
| 326 |
330 |
| 327 attrset(A_NORMAL); |
331 attrset(A_NORMAL); |
| 328 for (uint8_t i = 0 ; i < 8 ; i++) { |
332 for (uint8_t i = 0 ; i < 8 ; i++) { |
| 362 } |
366 } |
| 363 |
367 |
| 364 static void eval_move_failed_msg(int code) { |
368 static void eval_move_failed_msg(int code) { |
| 365 switch (code) { |
369 switch (code) { |
| 366 case AMBIGUOUS_MOVE: |
370 case AMBIGUOUS_MOVE: |
| 367 printw("Ambiguous move - please specify the piece to move."); |
371 addstr("Ambiguous move - please specify the piece to move."); |
| 368 break; |
372 break; |
| 369 case PIECE_NOT_FOUND: |
373 case PIECE_NOT_FOUND: |
| 370 printw("No piece can be moved this way."); |
374 addstr("No piece can be moved this way."); |
| 371 break; |
375 break; |
| 372 case NEED_PROMOTION: |
376 case NEED_PROMOTION: |
| 373 printw("You need to promote the pawn (append \"=Q\" e.g.)!"); |
377 addstr("You need to promote the pawn (append \"=Q\" e.g.)!"); |
| 374 break; |
378 break; |
| 375 case KING_IN_CHECK: |
379 case KING_IN_CHECK: |
| 376 printw("Your king is in check!"); |
380 addstr("Your king is in check!"); |
| 377 break; |
381 break; |
| 378 case PIECE_PINNED: |
382 case PIECE_PINNED: |
| 379 printw("This piece is pinned!"); |
383 addstr("This piece is pinned!"); |
| 380 break; |
384 break; |
| 381 case INVALID_MOVE_SYNTAX: |
385 case INVALID_MOVE_SYNTAX: |
| 382 printw("Can't interpret move - please use algebraic notation."); |
386 addstr("Can't interpret move - please use algebraic notation."); |
| 383 break; |
387 break; |
| 384 case RULES_VIOLATED: |
388 case RULES_VIOLATED: |
| 385 printw("Move does not comply with chess rules."); |
389 addstr("Move does not comply with chess rules."); |
| 386 break; |
390 break; |
| 387 case KING_MOVES_INTO_CHECK: |
391 case KING_MOVES_INTO_CHECK: |
| 388 printw("Can't move the king into a check position."); |
392 addstr("Can't move the king into a check position."); |
| 389 break; |
393 break; |
| 390 case MISSING_CHECK: |
394 case MISSING_CHECK: |
| 391 printw("Move does not indicate check."); |
395 addstr("Move does not indicate check."); |
| 392 break; |
396 break; |
| 393 case MISSING_CHECKMATE: |
397 case MISSING_CHECKMATE: |
| 394 printw("Move does not indicate checkmate."); |
398 addstr("Move does not indicate checkmate."); |
| 395 break; |
399 break; |
| 396 case INVALID_CHECK: |
400 case INVALID_CHECK: |
| 397 printw("Move incorrectly indicates check."); |
401 addstr("Move incorrectly indicates check."); |
| 398 break; |
402 break; |
| 399 case INVALID_CHECKMATE: |
403 case INVALID_CHECKMATE: |
| 400 printw("Move incorrectly indicates checkmate."); |
404 addstr("Move incorrectly indicates checkmate."); |
| 401 break; |
405 break; |
| 402 default: |
406 default: |
| 403 printw("Unknown move parser error."); |
407 addstr("Unknown move parser error."); |
| 404 } |
408 } |
| 405 } |
409 } |
| 406 |
410 |
| 407 static void save_pgn(GameState *gamestate) { |
411 static void save_pgn(GameState *gamestate) { |
| 408 int y; |
412 int y; |
| 429 move(y, 0); |
433 move(y, 0); |
| 430 } |
434 } |
| 431 |
435 |
| 432 bool export_comments = prompt_yesno("Export with comments"); |
436 bool export_comments = prompt_yesno("Export with comments"); |
| 433 |
437 |
| 434 printw("\rFilename: "); |
438 addstr("\rFilename: "); |
| 435 clrtoeol(); |
439 clrtoeol(); |
| 436 |
440 |
| 437 char filename[64]; |
441 char filename[64]; |
| 438 if (getnstr(filename, 64) == OK && filename[0] != '\0') { |
442 if (getnstr(filename, 64) == OK && filename[0] != '\0') { |
| 439 move(y, 0); |
443 move(y, 0); |
| 440 FILE *file = fopen(filename, "w"); |
444 FILE *file = fopen(filename, "w"); |
| 441 if (file) { |
445 if (file) { |
| 442 write_pgn(file, gamestate, export_comments); |
446 write_pgn(file, gamestate, export_comments); |
| 443 fclose(file); |
447 fclose(file); |
| 444 printw("File saved."); |
448 addstr("File saved."); |
| 445 } else { |
449 } else { |
| 446 printw("Can't write to file (%s).", strerror(errno)); |
450 printw("Can't write to file (%s).", strerror(errno)); |
| 447 } |
451 } |
| 448 clrtoeol(); |
452 clrtoeol(); |
| 449 } |
453 } |
| 585 net_send_code(opponent, NETCODE_TIMEOVER); |
589 net_send_code(opponent, NETCODE_TIMEOVER); |
| 586 return 1; |
590 return 1; |
| 587 } |
591 } |
| 588 |
592 |
| 589 move(inputy, 0); |
593 move(inputy, 0); |
| 590 printw("Use chess notation to enter your move.\n"); |
594 addstr("Use chess notation to enter your move.\n"); |
| 591 if (resign_suggested) { |
595 if (resign_suggested) { |
| 592 if (remis_suggested) { |
596 if (remis_suggested) { |
| 593 printw("The opponent asks you to resign or accept remis. \n\n"); |
597 addstr("The opponent asks you to resign or accept remis. \n\n"); |
| 594 } else { |
598 } else { |
| 595 printw("The opponent asks you to resign. \n\n"); |
599 addstr("The opponent asks you to resign. \n\n"); |
| 596 } |
600 } |
| 597 } else if (remis_suggested) { |
601 } else if (remis_suggested) { |
| 598 printw("The opponent offers remis. Type remis to accept. \n\n"); |
602 addstr("The opponent offers remis. Type remis to accept. \n\n"); |
| 599 } else if (remis_rejected) { |
603 } else if (remis_rejected) { |
| 600 printw("Remis offer rejected. \n\n"); |
604 addstr("Remis offer rejected. \n\n"); |
| 601 } else if (remis_offered) { |
605 } else if (remis_offered) { |
| 602 printw("Remis offer sent. \n\n"); |
606 addstr("Remis offer sent. \n\n"); |
| 603 } else { |
607 } else { |
| 604 printw("Or use a command: remis, resign, savepgn \n\n"); |
608 addstr("Or use a command: remis, resign, savepgn \n\n"); |
| 605 } |
609 } |
| 606 printw("Type your move: "); |
610 addstr("Type your move: "); |
| 607 clrtoeol(); |
611 clrtoeol(); |
| 608 |
612 |
| 609 /* check if the opponent sent us something */ |
613 /* check if the opponent sent us something */ |
| 610 code = net_recieve_code_async(opponent); |
614 code = net_recieve_code_async(opponent); |
| 611 switch (code) { |
615 switch (code) { |
| 628 return 1; |
632 return 1; |
| 629 case NETCODE_CONNLOST: |
633 case NETCODE_CONNLOST: |
| 630 gamestate->ragequit = true; |
634 gamestate->ragequit = true; |
| 631 return 1; |
635 return 1; |
| 632 case NETCODE_ERROR: |
636 case NETCODE_ERROR: |
| 633 printw("\rCannot perform asynchronous network IO"); |
637 addstr("\rCannot perform asynchronous network IO"); |
| 634 cbreak(); getch(); |
638 cbreak(); getch(); |
| 635 exit(EXIT_FAILURE); |
639 exit(EXIT_FAILURE); |
| 636 case NETCODE_AGAIN: |
640 case NETCODE_AGAIN: |
| 637 /* try again */ |
641 /* try again */ |
| 638 break; |
642 break; |
| 639 default: |
643 default: |
| 640 printw("\nThe opponent sent an invalid network pacakge."); |
644 addstr("\nThe opponent sent an invalid network pacakge."); |
| 641 } |
645 } |
| 642 |
646 |
| 643 /* read move */ |
647 /* read move */ |
| 644 if (use_premove || asyncgetnstr(movestr, &bufpos, MOVESTR_BUFLEN)) { |
648 if (use_premove || asyncgetnstr(movestr, &bufpos, MOVESTR_BUFLEN)) { |
| 645 bool was_premove = use_premove; |
649 bool was_premove = use_premove; |
| 685 * we want to receive the "game end" code from the |
689 * we want to receive the "game end" code from the |
| 686 * other party. |
690 * other party. |
| 687 */ |
691 */ |
| 688 return 0; |
692 return 0; |
| 689 } else if (code == NETCODE_CONNLOST) { |
693 } else if (code == NETCODE_CONNLOST) { |
| 690 printw("Your opponent left the game."); |
694 addstr("Your opponent left the game."); |
| 691 return 1; |
695 return 1; |
| 692 } else { |
696 } else { |
| 693 printw("Invalid network response."); |
697 addstr("Invalid network response."); |
| 694 } |
698 } |
| 695 } else { |
699 } else { |
| 696 if (was_premove) { |
700 if (was_premove) { |
| 697 printw("\nThe prepared move could not be executed."); |
701 addstr("\nThe prepared move could not be executed."); |
| 698 } else { |
702 } else { |
| 699 eval_move_failed_msg(eval_result); |
703 eval_move_failed_msg(eval_result); |
| 700 } |
704 } |
| 701 } |
705 } |
| 702 clrtoeol(); |
706 clrtoeol(); |
| 714 bool resign_suggested = false; |
718 bool resign_suggested = false; |
| 715 while (1) { |
719 while (1) { |
| 716 timecontrol(gamestate); |
720 timecontrol(gamestate); |
| 717 |
721 |
| 718 move(inputy, 0); |
722 move(inputy, 0); |
| 719 printw("Waiting for opponent. Use chess notation to prepare a move.\n"); |
723 addstr("Waiting for opponent. Use chess notation to prepare a move.\n"); |
| 720 if (*gamestate->premove) { |
724 if (*gamestate->premove) { |
| 721 printw("Current pre-move: %s \n", |
725 printw("Current pre-move: %s \n", |
| 722 gamestate->premove); |
726 gamestate->premove); |
| 723 } else if (remis_suggested && !resign_suggested) { |
727 } else if (remis_suggested && !resign_suggested) { |
| 724 printw("Suggested remis. \n"); |
728 addstr("Suggested remis. \n"); |
| 725 } else if (resign_suggested) { |
729 } else if (resign_suggested) { |
| 726 if (remis_suggested) { |
730 if (remis_suggested) { |
| 727 printw("Suggested to resign or at least to accept remis. \n"); |
731 addstr("Suggested to resign or at least to accept remis. \n"); |
| 728 } else { |
732 } else { |
| 729 printw("Suggested to resign. \n"); |
733 addstr("Suggested to resign. \n"); |
| 730 } |
734 } |
| 731 } else { |
735 } else { |
| 732 printw("Or use a command: remis, resign, taunt, savepgn \n"); |
736 addstr("Or use a command: remis, resign, taunt, savepgn \n"); |
| 733 } |
737 } |
| 734 if (remis_offered) { |
738 if (remis_offered) { |
| 735 printw("Opponent offered remis. Type 'remis' to accept.\n"); |
739 addstr("Opponent offered remis. Type 'remis' to accept.\n"); |
| 736 } else { |
740 } else { |
| 737 printw("\n"); |
741 addch('\n'); |
| 738 } |
742 } |
| 739 printw("Prepare your next move: "); |
743 addstr("Prepare your next move: "); |
| 740 clrtoeol(); |
744 clrtoeol(); |
| 741 refresh(); |
745 refresh(); |
| 742 |
746 |
| 743 /* allow the player to prepare a move */ |
747 /* allow the player to prepare a move */ |
| 744 if (asyncgetnstr(movestr, &bufpos, MOVESTR_BUFLEN)) { |
748 if (asyncgetnstr(movestr, &bufpos, MOVESTR_BUFLEN)) { |
| 870 net_send_data(opponent, NETCODE_DECLINE_MOVE, &code, 1); |
874 net_send_data(opponent, NETCODE_DECLINE_MOVE, &code, 1); |
| 871 } |
875 } |
| 872 break; |
876 break; |
| 873 } |
877 } |
| 874 case NETCODE_ERROR: |
878 case NETCODE_ERROR: |
| 875 printw("\rCannot perform asynchronous network IO"); |
879 addstr("\rCannot perform asynchronous network IO"); |
| 876 cbreak(); getch(); |
880 cbreak(); getch(); |
| 877 exit(EXIT_FAILURE); |
881 exit(EXIT_FAILURE); |
| 878 case NETCODE_AGAIN: |
882 case NETCODE_AGAIN: |
| 879 /* try again */ |
883 /* try again */ |
| 880 break; |
884 break; |
| 881 default: |
885 default: |
| 882 printw("\nInvalid network request."); |
886 addstr("\nInvalid network request."); |
| 883 } |
887 } |
| 884 } |
888 } |
| 885 } |
889 } |
| 886 |
890 |
| 887 static void game_review(GameState *gamestate) { |
891 static void game_review(GameState *gamestate) { |
| 913 } else if (gamestate->threefold) { |
917 } else if (gamestate->threefold) { |
| 914 addstr("The game was drawn after threefold repetition.\n"); |
918 addstr("The game was drawn after threefold repetition.\n"); |
| 915 } else if (gamestate->nomaterial) { |
919 } else if (gamestate->nomaterial) { |
| 916 addstr("The game is drawn due to insufficient material.\n"); |
920 addstr("The game is drawn due to insufficient material.\n"); |
| 917 } else if (gamestate->checkmate) { |
921 } else if (gamestate->checkmate) { |
| 918 printw("%s was checkmated.\n", |
922 addstr(gamestate->movecount % 2 == 0 ? "White" : "Black"); |
| 919 gamestate->movecount % 2 == 0 ? "White" : "Black"); |
923 addstr(" was checkmated.\n"); |
| 920 } else if (gamestate->ragequit) { |
924 } else if (gamestate->ragequit) { |
| 921 printw("Your opponent disconnected.\n"); |
925 addstr("Your opponent disconnected.\n"); |
| 922 } |
926 } |
| 923 addstr("\nPress 'q' to quit, 's' to save the position as PGN, or\n" |
927 addstr("\nPress 'q' to quit, 's' to save the position as PGN, or\n" |
| 924 "arrow keys, home/end, page up/down to review the game.\n"); |
928 "arrow keys, home/end, page up/down to review the game.\n"); |
| 925 flushinp(); |
929 flushinp(); |
| 926 redraw = false; |
930 redraw = false; |
| 1007 |
1011 |
| 1008 static void dump_gameinfo(GameState *gamestate) { |
1012 static void dump_gameinfo(GameState *gamestate) { |
| 1009 GameInfo *gameinfo = &gamestate->info; |
1013 GameInfo *gameinfo = &gamestate->info; |
| 1010 int serverwhite = gameinfo->servercolor == WHITE; |
1014 int serverwhite = gameinfo->servercolor == WHITE; |
| 1011 attron(A_UNDERLINE); |
1015 attron(A_UNDERLINE); |
| 1012 printw("Game details\n"); |
1016 addstr("Game details\n"); |
| 1013 attroff(A_UNDERLINE); |
1017 attroff(A_UNDERLINE); |
| 1014 printw(" Server: %s\n Client: %s\n", |
1018 printw(" Server: %s\n Client: %s\n", |
| 1015 serverwhite?"White":"Black", serverwhite?"Black":"White" |
1019 serverwhite?"White":"Black", serverwhite?"Black":"White" |
| 1016 ); |
1020 ); |
| 1017 if (gameinfo->timecontrol) { |
1021 if (gameinfo->timecontrol) { |
| 1025 if (gameinfo->delay) { |
1029 if (gameinfo->delay) { |
| 1026 printw(" (with %us delay)", gameinfo->delay); |
1030 printw(" (with %us delay)", gameinfo->delay); |
| 1027 } |
1031 } |
| 1028 addch('\n'); |
1032 addch('\n'); |
| 1029 } else { |
1033 } else { |
| 1030 printw(" No time limit\n"); |
1034 addstr(" No time limit\n"); |
| 1031 } |
1035 } |
| 1032 addch('\n'); |
1036 addch('\n'); |
| 1033 for (unsigned i = 0 ; i < gamestate->movecount ; i++) { |
1037 for (unsigned i = 0 ; i < gamestate->movecount ; i++) { |
| 1034 if (i % 2 == 0) { |
1038 if (i % 2 == 0) { |
| 1035 printw("%d. %s", 1+i/2, gamestate->moves[i].string); |
1039 printw("%d. %s", 1+i/2, gamestate->moves[i].string); |
| 1054 close(server_fd); |
1058 close(server_fd); |
| 1055 } |
1059 } |
| 1056 } |
1060 } |
| 1057 |
1061 |
| 1058 static int server_open(Server *server) { |
1062 static int server_open(Server *server) { |
| 1059 printw("\nListening for client...\n"); |
1063 addstr("\nListening for client...\n"); |
| 1060 refresh(); |
1064 refresh(); |
| 1061 if (settings.usedomainsocket |
1065 if (settings.usedomainsocket |
| 1062 ? net_create_sock(server, settings.serverhost) |
1066 ? net_create_sock(server, settings.serverhost) |
| 1063 : net_create_tcp(server, settings.port)) { |
1067 : net_create_tcp(server, settings.port)) { |
| 1064 addstr("Server creation failed"); |
1068 addstr("Server creation failed"); |