summaryrefslogtreecommitdiff
path: root/apps/plugins/chessbox/chessbox_pgn.c
diff options
context:
space:
mode:
Diffstat (limited to 'apps/plugins/chessbox/chessbox_pgn.c')
-rw-r--r--apps/plugins/chessbox/chessbox_pgn.c381
1 files changed, 371 insertions, 10 deletions
diff --git a/apps/plugins/chessbox/chessbox_pgn.c b/apps/plugins/chessbox/chessbox_pgn.c
index db95210a8d..fb04f6ac38 100644
--- a/apps/plugins/chessbox/chessbox_pgn.c
+++ b/apps/plugins/chessbox/chessbox_pgn.c
@@ -188,7 +188,8 @@
188 #error CHESSBOX: Unsupported keypad 188 #error CHESSBOX: Unsupported keypad
189#endif 189#endif
190 190
191#define LOG_FILE PLUGIN_DIR "/chessbox.log" 191#define PGN_FILE PLUGIN_GAMES_DIR "/chessbox.pgn"
192#define LOG_FILE PLUGIN_GAMES_DIR "/chessbox.log"
192int loghandler; 193int loghandler;
193 194
194struct plugin_api* rb; 195struct plugin_api* rb;
@@ -520,6 +521,180 @@ void pgn_to_coords(struct pgn_ply_node* ply){
520 color[locn[ply->row_from][ply->column_from]] = neutral; 521 color[locn[ply->row_from][ply->column_from]] = neutral;
521} 522}
522 523
524void coords_to_pgn(struct pgn_ply_node* ply){
525 int pos = 0,i,j;
526 unsigned short moving_piece = board[locn[ply->row_from][ply->column_from]];
527 char unambiguous_position;
528 bool found = false;
529 char alg_move[5];
530 char move_buffer[10];
531 short move;
532 if (moving_piece == king){
533 /* check castling */
534 if (ply->column_from == 4 && ply->column_to == 6){
535 /* castling kingside */
536 rb->strcpy(ply->pgn_text,"O-O");
537 ply->castle = true;
538 } else if (ply->column_from == 4 && ply->column_to == 2){
539 /* castling queenside */
540 rb->strcpy(ply->pgn_text,"O-O-O");
541 } else {
542 if (board[locn[ply->row_to][ply->column_to]] != no_piece){
543 rb->snprintf(ply->pgn_text,10,"Kx%c%c",'a'+ply->column_to,
544 '1'+ply->row_to);
545 } else {
546 rb->snprintf(ply->pgn_text,10,"K%c%c",'a'+ply->column_to,
547 '1'+ply->row_to);
548 }
549 }
550 } else if (moving_piece == pawn){
551 if (ply->column_from != ply->column_to){
552 /* check enpassant */
553 if (board[locn[ply->row_to][ply->column_to]] == no_piece){
554 ply->enpassant = true;
555 }
556 /* check promotions when taking a piece */
557 if (ply->row_to == 0 || ply->row_to == 7) {
558 ply->promotion = true;
559 ply->promotion_piece = queen;
560 rb->snprintf(ply->pgn_text,10,"%cx%c%c=D", 'a'+ply->column_from,
561 'a'+ply->column_to,'1'+ply->row_to);
562 } else {
563 rb->snprintf(ply->pgn_text,10,"%cx%c%c", 'a'+ply->column_from,
564 'a'+ply->column_to,'1'+ply->row_to);
565 }
566 } else {
567 /* check promotions when not taking a piece */
568 if (ply->row_to == 0 || ply->row_to == 7) {
569 ply->promotion = true;
570 ply->promotion_piece = queen;
571 rb->snprintf(ply->pgn_text,10,"%c%c=D", 'a'+ply->column_to,
572 '1'+ply->row_to);
573 } else {
574 rb->snprintf(ply->pgn_text,10,"%c%c", 'a'+ply->column_to,
575 '1'+ply->row_to);
576 }
577 }
578 } else {
579 /* verify ambiguous moves for the different kinds of pieces */
580 unambiguous_position = '\0';
581 if (moving_piece == knight){
582 for (i=0;i<8;i++){
583 if (ply->row_to + kn_offs[i][0] >= 0 && ply->row_to + kn_offs[i][0] <= 7
584 && ply->column_to + kn_offs[i][1] >= 0 && ply->column_to + kn_offs[i][1] <= 7
585 && board[locn[ply->row_to + kn_offs[i][0]][ply->column_to + kn_offs[i][1]]] == knight
586 && color[locn[ply->row_to + kn_offs[i][0]][ply->column_to + kn_offs[i][1]]] == ply->player
587 && (ply->row_to + kn_offs[i][0] != ply->row_from
588 || ply->column_to + kn_offs[i][1] != ply->column_from)){
589 if (ply->row_to + kn_offs[i][0] != ply->row_from){
590 unambiguous_position = '1' + ply->row_from;
591 } else {
592 unambiguous_position = 'a' + ply->column_from;
593 }
594 break;
595 }
596 }
597 }
598 if (moving_piece == rook || moving_piece == queen){
599 found = false;
600 for (i=0;i<4;i++){
601 j=1;
602 while (ply->row_to+(j*rk_offs[i][0]) >= 0 && ply->row_to+(j*rk_offs[i][0]) <= 7 &&
603 ply->column_to+(j*rk_offs[i][1]) >= 0 && ply->column_to+(j*rk_offs[i][1]) <= 7){
604 if (board[locn[ply->row_to+(j*rk_offs[i][0])][ply->column_to+(j*rk_offs[i][1])]] != no_piece) {
605 if (board[locn[ply->row_to+(j*rk_offs[i][0])][ply->column_to+(j*rk_offs[i][1])]] == moving_piece &&
606 color[locn[ply->row_to+(j*rk_offs[i][0])][ply->column_to+(j*rk_offs[i][1])]] == ply->player &&
607 (ply->row_to+(j*rk_offs[i][0]) != ply->row_from
608 || ply->column_to+(j*rk_offs[i][1]) != ply->column_from)) {
609 if (ply->row_to+(j*rk_offs[i][0]) != ply->row_from){
610 unambiguous_position = '1' + ply->row_from;
611 } else {
612 unambiguous_position = 'a' + ply->column_from;
613 }
614 found = true;
615 }
616 break;
617 }
618 j++;
619 }
620 if (found) {
621 break;
622 }
623 }
624 }
625 if (moving_piece == bishop || (moving_piece == queen && !found)){
626 for (i=0;i<4;i++){
627 j=1;
628 while (ply->row_to+(j*bp_offs[i][0]) >= 0 && ply->row_to+(j*bp_offs[i][0]) <= 7 &&
629 ply->column_to+(j*bp_offs[i][1]) >= 0 && ply->column_to+(j*bp_offs[i][1]) <= 7){
630 if (board[locn[ply->row_to+(j*bp_offs[i][0])][ply->column_to+(j*bp_offs[i][1])]] != no_piece) {
631 if (board[locn[ply->row_to+(j*bp_offs[i][0])][ply->column_to+(j*bp_offs[i][1])]] == moving_piece &&
632 color[locn[ply->row_to+(j*bp_offs[i][0])][ply->column_to+(j*bp_offs[i][1])]] == ply->player &&
633 (ply->row_to+(j*bp_offs[i][0]) != ply->row_from
634 || ply->column_to+(j*bp_offs[i][1]) != ply->column_from)) {
635 if (ply->row_to+(j*bp_offs[i][0]) != ply->row_from){
636 unambiguous_position = '1' + ply->row_from;
637 } else {
638 unambiguous_position = 'a' + ply->column_from;
639 }
640 found = true;
641 }
642 break;
643 }
644 j++;
645 }
646 if (found) {
647 break;
648 }
649 }
650 }
651 /* generate the first portion of the PGN text
652 * always as white so all uppercase, black/white considerations
653 * will be useful for FEN notation but not in this case
654 */
655 if (unambiguous_position == '\0'){
656 if (board[locn[ply->row_to][ply->column_to]] != no_piece){
657 rb->snprintf(ply->pgn_text,10,"%cx%c%c",
658 pgn_from_piece(moving_piece,white) ,
659 'a'+ply->column_to, '1'+ply->row_to);
660 } else {
661 rb->snprintf(ply->pgn_text,10,"%c%c%c",
662 pgn_from_piece(moving_piece,white) ,
663 'a'+ply->column_to, '1'+ply->row_to);
664 }
665 } else {
666 if (board[locn[ply->row_to][ply->column_to]] != no_piece){
667 rb->snprintf(ply->pgn_text,10,"%c%cx%c%c",
668 pgn_from_piece(moving_piece,white) ,
669 unambiguous_position, 'a'+ply->column_to,
670 '1'+ply->row_to);
671 } else {
672 rb->snprintf(ply->pgn_text,10,"%c%c%c%c",
673 pgn_from_piece(moving_piece,white) ,
674 unambiguous_position, 'a'+ply->column_to,
675 '1'+ply->row_to);
676 }
677 }
678 }
679 /* update the board */
680 rb->snprintf(alg_move,5,"%c%c%c%c",'a' + ply->column_from, '1' + ply->row_from,
681 'a' + ply->column_to, '1' + ply->row_to);
682 /* The function returns false if the move is invalid, but since we're
683 * replaying the game, that should not be posible
684 */
685 VerifyMove (ply->player, alg_move , 0 , &move, move_buffer );
686
687 /* add check/mate indicators */
688 for (pos=0;ply->pgn_text[pos] != '\0';pos++);
689 if (ply->checkmate) {
690 ply->pgn_text[pos] = '#'; pos++;
691 ply->pgn_text[pos] = '\0'; pos++;
692 } else if (move_buffer[4] == '+'){
693 ply->pgn_text[pos] = '+'; pos++;
694 ply->pgn_text[pos] = '\0'; pos++;
695 }
696}
697
523char * get_game_text(int selected_item, void *data, char *buffer){ 698char * get_game_text(int selected_item, void *data, char *buffer){
524 int i; 699 int i;
525 struct pgn_game_node *temp_node = (struct pgn_game_node *)data; 700 struct pgn_game_node *temp_node = (struct pgn_game_node *)data;
@@ -538,11 +713,21 @@ char * get_game_text(int selected_item, void *data, char *buffer){
538 return buffer; 713 return buffer;
539} 714}
540 715
716void write_pgn_token(int fhandler, char *buffer, size_t *line_length){
717 if (*line_length + rb->strlen(buffer) + 1 > 80){
718 rb->fdprintf(fhandler,"\n");
719 *line_length = 0;
720 }
721 rb->fdprintf(fhandler,"%s ",buffer);
722 *line_length += (rb->strlen(buffer) + 1);
723}
724
541/* ---- api functions ---- */ 725/* ---- api functions ---- */
542struct pgn_game_node* pgn_list_games(struct plugin_api* api,const char* filename){ 726struct pgn_game_node* pgn_list_games(struct plugin_api* api,const char* filename){
543 int fhandler; 727 int fhandler;
544 char line_buffer[128]; 728 char line_buffer[128];
545 struct pgn_game_node size_node, *first_game = NULL, *curr_node = NULL, *temp_node; 729 struct pgn_game_node size_node, *first_game = NULL;
730 struct pgn_game_node *curr_node = NULL, *temp_node;
546 unsigned short game_count = 1; 731 unsigned short game_count = 1;
547 int line_count = 0; 732 int line_count = 0;
548 bool header_start = true, game_start = false; 733 bool header_start = true, game_start = false;
@@ -577,7 +762,9 @@ struct pgn_game_node* pgn_list_games(struct plugin_api* api,const char* filename
577 } else { 762 } else {
578 if (line_buffer[0] == '['){ 763 if (line_buffer[0] == '['){
579 process_tag(curr_node, line_buffer); 764 process_tag(curr_node, line_buffer);
580 } else if (line_buffer[0] == '\r' || line_buffer[0] == '\n' || line_buffer[0] == '\0'){ 765 } else if (line_buffer[0] == '\r'
766 || line_buffer[0] == '\n'
767 || line_buffer[0] == '\0'){
581 if (game_start) { 768 if (game_start) {
582 game_start = false; 769 game_start = false;
583 } else { 770 } else {
@@ -594,7 +781,8 @@ struct pgn_game_node* pgn_list_games(struct plugin_api* api,const char* filename
594 return first_game; 781 return first_game;
595} 782}
596 783
597struct pgn_game_node* pgn_show_game_list(struct plugin_api* api, struct pgn_game_node* first_game){ 784struct pgn_game_node* pgn_show_game_list(struct plugin_api* api,
785 struct pgn_game_node* first_game){
598 int curr_selection = 0; 786 int curr_selection = 0;
599 int button; 787 int button;
600 struct gui_synclist games_list; 788 struct gui_synclist games_list;
@@ -638,8 +826,10 @@ struct pgn_game_node* pgn_show_game_list(struct plugin_api* api, struct pgn_game
638 } 826 }
639} 827}
640 828
641void pgn_parse_game(struct plugin_api* api, const char* filename, struct pgn_game_node* selected_game){ 829void pgn_parse_game(struct plugin_api* api, const char* filename,
642 struct pgn_ply_node size_ply, *first_ply = NULL, *temp_ply = NULL, *curr_node = NULL; 830 struct pgn_game_node* selected_game){
831 struct pgn_ply_node size_ply, *first_ply = NULL;
832 struct pgn_ply_node *temp_ply = NULL, *curr_node = NULL;
643 int fhandler, i; 833 int fhandler, i;
644 char line_buffer[128]; 834 char line_buffer[128];
645 char token_buffer[10]; 835 char token_buffer[10];
@@ -682,16 +872,20 @@ void pgn_parse_game(struct plugin_api* api, const char* filename, struct pgn_gam
682 temp_ply->prev_node = curr_node; 872 temp_ply->prev_node = curr_node;
683 curr_node = temp_ply; 873 curr_node = temp_ply;
684 } 874 }
685 rb->fdprintf(loghandler,"player: %u; pgn: %s; from: %u,%u; to: %u,%u; taken: %u.\n", 875 rb->fdprintf(loghandler,
686 temp_ply->player, temp_ply->pgn_text, temp_ply->row_from, temp_ply->column_from, 876 "player: %u; pgn: %s; from: %u,%u; to: %u,%u; taken: %u.\n",
687 temp_ply->row_to, temp_ply->column_to, temp_ply->taken_piece); 877 temp_ply->player, temp_ply->pgn_text, temp_ply->row_from,
878 temp_ply->column_from, temp_ply->row_to,
879 temp_ply->column_to, temp_ply->taken_piece);
688 } 880 }
689 } 881 }
690 } 882 }
691 883
692 rb->close(loghandler); 884 rb->close(loghandler);
693 885
694 /* additional dummy ply to represent end of file without loosing the previous node's pointer */ 886 /* additional dummy ply to represent end of file without
887 *loosing the previous node's pointer
888 */
695 if (first_ply != NULL){ 889 if (first_ply != NULL){
696 temp_ply = (struct pgn_ply_node *)pl_malloc(sizeof size_ply); 890 temp_ply = (struct pgn_ply_node *)pl_malloc(sizeof size_ply);
697 temp_ply->player = neutral; 891 temp_ply->player = neutral;
@@ -701,3 +895,170 @@ void pgn_parse_game(struct plugin_api* api, const char* filename, struct pgn_gam
701 selected_game->first_ply = first_ply; 895 selected_game->first_ply = first_ply;
702 rb->close(fhandler); 896 rb->close(fhandler);
703} 897}
898
899struct pgn_game_node* pgn_init_game(struct plugin_api* api){
900 struct pgn_game_node game_size, *game;
901 struct pgn_ply_node ply_size, *ply;
902 struct tm *current_time;
903
904 rb = api;
905
906 if (bufptr == NULL){
907 pl_malloc_init();
908 }
909
910 /* create an "end of game" dummy ply and assign defaults */
911 ply = (struct pgn_ply_node *)pl_malloc(sizeof ply_size);
912 ply->player = neutral;
913 ply->pgn_text[0] = '\0';
914 ply->prev_node = NULL;
915 ply->next_node = NULL;
916
917 /* create the game and assign defaults */
918 game = (struct pgn_game_node *)pl_malloc(sizeof game_size);
919 game->game_number = 0;
920 rb->strcpy(game->white_player,"Player");
921 rb->strcpy(game->black_player,"GnuChess");
922 current_time = rb->get_time();
923 if (current_time->tm_year < 100){
924 rb->snprintf(game->game_date,11,"????.??.??");
925 } else {
926 rb->snprintf(game->game_date,11,"%4u.%2u.%2u",current_time->tm_year + 1900,
927 current_time->tm_mon + 1, current_time->tm_mday);
928 }
929 rb->strcpy(game->result,"*");
930 game->pgn_line = 0;
931 game->first_ply = ply;
932 game->next_node = NULL;
933
934 return game;
935}
936
937void pgn_append_ply(struct plugin_api* api, struct pgn_game_node* game,
938 unsigned short ply_player, char *move_buffer, bool is_mate){
939 struct pgn_ply_node ply_size, *ply, *temp;
940
941 rb = api;
942
943 ply = (struct pgn_ply_node *)pl_malloc(sizeof ply_size);
944 ply->player = ply_player;
945 ply->column_from = move_buffer[0] - 'a';
946 ply->row_from = move_buffer[1] - '1';
947 ply->column_to = move_buffer[2] - 'a';
948 ply->row_to = move_buffer[3] - '1';
949 ply->castle = false;
950 ply->promotion = false;
951 ply->enpassant = false;
952 ply->promotion_piece = no_piece;
953 ply->taken_piece = no_piece;
954 ply->draw = false;
955 ply->checkmate = is_mate;
956
957 /* move the pointer to the "end of game" marker ply */
958 for (temp=game->first_ply;temp->next_node!=NULL;temp=temp->next_node);
959
960 /* arrange the pointers to insert the ply before the marker */
961 ply->next_node = temp;
962 ply->prev_node = temp->prev_node;
963 if (temp->prev_node == NULL){
964 game->first_ply = ply;
965 } else {
966 temp->prev_node->next_node = ply;
967 }
968 temp->prev_node = ply;
969}
970
971void pgn_set_result(struct plugin_api* api, struct pgn_game_node* game,
972 bool is_mate){
973
974 rb = api;
975
976 struct pgn_ply_node *ply;
977 for(ply=game->first_ply;ply->next_node != NULL;ply=ply->next_node);
978 if (is_mate){
979 ply->prev_node->checkmate = true;
980 } else {
981 ply->prev_node->draw = true;
982 }
983}
984
985void pgn_store_game(struct plugin_api* api, struct pgn_game_node* game){
986 int fhandler;
987 struct pgn_ply_node *ply;
988 unsigned ply_count;
989 size_t line_length=0;
990 char buffer[10];
991
992 rb = api;
993
994 GNUChess_Initialize();
995
996 ply_count=0;
997 ply=game->first_ply;
998 while (ply->next_node!=NULL){
999 coords_to_pgn(ply);
1000 if (ply->checkmate){
1001 if (ply->player == white){
1002 rb->strcpy(game->result,"1-0");
1003 } else {
1004 rb->strcpy(game->result,"0-1");
1005 }
1006 }
1007 if (ply->draw){
1008 rb->strcpy(game->result,"1/2-1/2");
1009 }
1010 ply=ply->next_node;
1011 ply_count++;
1012 }
1013
1014 fhandler = rb->open(PGN_FILE, O_WRONLY|O_CREAT|O_APPEND);
1015
1016
1017 /* the first 7 tags are mandatory according to the PGN specification so we
1018 * have to include them even if the values don't make much sense
1019 */
1020 rb->fdprintf(fhandler,"[Event \"Casual Game\"]\n");
1021 rb->fdprintf(fhandler,"[Site \"?\"]\n");
1022 rb->fdprintf(fhandler,"[Date \"%s\"]\n",game->game_date);
1023 rb->fdprintf(fhandler,"[Round \"?\"]\n");
1024 rb->fdprintf(fhandler,"[White \"%s\"]\n",game->white_player);
1025 rb->fdprintf(fhandler,"[Black \"%s\"]\n",game->black_player);
1026 rb->fdprintf(fhandler,"[Result \"%s\"]\n",game->result);
1027 rb->fdprintf(fhandler,"[PlyCount \"%u\"]\n",ply_count);
1028
1029 /* leave a blank line between the tag section and the game section */
1030 rb->fdprintf(fhandler,"\n");
1031
1032 /* write the plies in several lines of up to 80 characters */
1033 for (ply_count=0, ply=game->first_ply;ply->next_node!=NULL;
1034 ply=ply->next_node,ply_count++){
1035 /* write the move number */
1036 if (ply->player == white){
1037 rb->snprintf(buffer,10,"%u.",(ply_count/2)+1);
1038 write_pgn_token(fhandler, buffer, &line_length);
1039 }
1040 /* write the actual move */
1041 write_pgn_token(fhandler,ply->pgn_text,&line_length);
1042 /* write the result of the game at the end */
1043 if (ply->checkmate){
1044 if (ply->player == white){
1045 write_pgn_token(fhandler,"1-0",&line_length);
1046 } else {
1047 write_pgn_token(fhandler,"0-1",&line_length);
1048 }
1049 break;
1050 } else if (ply->draw){
1051 write_pgn_token(fhandler,"1/2-1/2",&line_length);
1052 break;
1053 } else if (ply->next_node->player == neutral) {
1054 /* unknown end of the game */
1055 write_pgn_token(fhandler,"*",&line_length);
1056 break;
1057 }
1058 }
1059
1060 /* leave a blank line between the tag section and the game section */
1061 rb->fdprintf(fhandler,"\n\n");
1062
1063 rb->close(fhandler);
1064}