]> git.sesse.net Git - pgn-extract/blobdiff - output.c
Push through a computer/human flag to the binary output.
[pgn-extract] / output.c
index 2878b00a3b195165c2ef6c38f51246d5f6e4a55a..a403488375b878a2035259267c2df9dd1dbfd41e 100644 (file)
--- a/output.c
+++ b/output.c
@@ -24,6 +24,7 @@
 #include <string.h>
 #include <stdlib.h>
 #include <ctype.h>
+#include <time.h>
 #include "bool.h"
 #include "defs.h"
 #include "typedef.h"
@@ -34,6 +35,8 @@
 #include "apply.h"
 #include "output.h"
 #include "mymalloc.h"
+#include "eco.h"
+#include "farmhash-c.h"
 
 
             /* Functions for outputting games in the required format. */
@@ -62,6 +65,9 @@ static char promoted_piece_letter(Piece piece);
 static void print_algebraic_game(Game current_game,FILE *outputfile,
                            unsigned move_number,Boolean white_to_move,
                            Board *final_board);
+static void output_sesse_bin_game(Game current_game,FILE *outputfile,
+                                  unsigned move_number,Boolean white_to_move,
+                                  Board *final_board);
 static void print_epd_game(Game current_game,FILE *outputfile,
                            unsigned move_number,Boolean white_to_move,
                            Board *final_board);
@@ -149,6 +155,7 @@ which_output_format(const char *arg)
         { "elalg", ELALG },
         { "uci", UCI },
         { "cm", CM },
+        { "sessebin", SESSE_BIN },
         { "", SOURCE },
         /* Add others before the terminating NULL. */
         { (const char *) NULL, SAN }
@@ -188,6 +195,7 @@ output_file_suffix(OutputFormat format)
     static const char PGN_suffix[] = ".pgn";
     static const char EPD_suffix[] = ".epd";
     static const char CM_suffix[] = ".cm";
+    static const char BIN_suffix[] = ".bin";
 
     switch(format){
         case SOURCE:
@@ -200,6 +208,8 @@ output_file_suffix(OutputFormat format)
             return EPD_suffix;
         case CM:
             return CM_suffix;
+        case SESSE_BIN:
+            return BIN_suffix;
         default:
             return PGN_suffix;
     }
@@ -996,6 +1006,10 @@ output_game(Game current_game,FILE *outputfile)
            case CM:
                output_cm_game(outputfile,move_number,white_to_move,current_game);
                break;
+           case SESSE_BIN:
+               output_sesse_bin_game(current_game,outputfile,move_number,white_to_move,
+                                      final_board);
+               break;
            default:
                fprintf(GlobalState.logfile,
                            "Internal error: unknown output type %d in output_game().\n",
@@ -1156,6 +1170,126 @@ print_algebraic_game(Game current_game,FILE *outputfile,
     putc('\n',outputfile);
 }
 
+static void
+output_sesse_bin_game(Game current_game,FILE *outputfile,
+                      unsigned move_number,Boolean white_to_move,
+                      Board* final_board)
+{
+    const char *result = NULL;
+    Move *move;
+
+    // Find the result. Skip games with no result.
+    for (move = current_game.moves; move != NULL; move = move->next) {
+        if (move->class == NULL_MOVE) {
+            // Skip games with null moves.
+            return;
+        }
+        if (move->terminating_result) {
+            result = move->terminating_result;
+        }
+    }
+    if (result == NULL || strcmp(result, "*") == 0) {
+        return;
+    }
+
+    int result_int = -1;
+    if (strcmp(result, "1-0") == 0) {
+        result_int = 0;
+    } else if (strcmp(result, "1/2-1/2") == 0) {
+        result_int = 1;
+    } else if (strcmp(result, "0-1") == 0) {
+        result_int = 2;
+    } else {
+        fprintf(stderr, "Unknown result '%s'\n", result);
+        return;
+    }
+
+    // Find Black and White Elos.
+    const char *white_elo_tag = current_game.tags[WHITE_ELO_TAG];
+    const char *black_elo_tag = current_game.tags[BLACK_ELO_TAG];
+    int white_elo = 0, black_elo = 0;
+    if (white_elo_tag) {
+        white_elo = atoi(white_elo_tag);
+    }
+    if (black_elo_tag) {
+        black_elo = atoi(black_elo_tag);
+    }
+    int file_num = current_game.file_number;
+    long start_position = current_game.start_position;
+    int computer_flag = GlobalState.computer_flag;
+
+    // Parse date and time, if it exists. Set invalid dates to year 3000.
+    const char *date_tag = current_game.tags[DATE_TAG];
+    const char *time_tag = current_game.tags[TIME_TAG];
+    struct tm tm = {0};
+    time_t timestamp;
+    int year, month, day;
+    if (date_tag && sscanf(date_tag, "%u.%u.%u", &year, &month, &day) == 3) {
+        int hour, minute, second;
+        tm.tm_year = year - 1900;
+        tm.tm_mon = month - 1;
+        tm.tm_mday = day;
+
+        if (time_tag && sscanf(time_tag, "%u:%u:%u", &hour, &minute, &second) == 3) {
+            tm.tm_hour = hour;
+            tm.tm_min = minute;
+            tm.tm_sec = second;
+        }
+        timestamp = mktime(&tm);
+    } else {
+        timestamp = 32503680000;
+    }
+
+    uint16_t prev_board_hash = 0;
+    unsigned int opening;
+
+    for (move = current_game.moves; move != NULL; move = move->next) {
+        opening = move->eco ? move->eco->cumulative_hash_value : 0;  // Truncate to 32 bits.
+
+        // key
+        putc(move->bpfen_len + sizeof(prev_board_hash), outputfile);
+        fwrite(move->bpfen, move->bpfen_len, 1, outputfile);
+        fwrite(&prev_board_hash, sizeof(prev_board_hash), 1, outputfile);
+
+        // value
+        putc(result_int, outputfile);
+        fwrite(&white_elo, sizeof(white_elo), 1, outputfile);
+        fwrite(&black_elo, sizeof(black_elo), 1, outputfile);
+        fwrite(&opening, sizeof(opening), 1, outputfile);
+        fwrite(&timestamp, sizeof(timestamp), 1, outputfile);
+        fwrite(&file_num, sizeof(file_num), 1, outputfile);
+        fwrite(&start_position, sizeof(start_position), 1, outputfile);
+        putc(computer_flag, outputfile);
+        putc(strlen((char *)move->move), outputfile);
+        fwrite(move->move, strlen((char *)move->move), 1, outputfile);
+
+        prev_board_hash = farmhash_32(move->bpfen, move->bpfen_len);
+    }
+
+    // Final position.
+    char *bpfen;
+    int bpfen_len;
+    build_BPFEN_string(final_board, &bpfen, &bpfen_len);
+
+    // key
+    putc(bpfen_len + sizeof(prev_board_hash), outputfile);
+    fwrite(bpfen, bpfen_len, 1, outputfile);
+    fwrite(&prev_board_hash, sizeof(prev_board_hash), 1, outputfile);
+
+    // value
+    putc(result_int, outputfile);
+    fwrite(&white_elo, sizeof(white_elo), 1, outputfile);
+    fwrite(&black_elo, sizeof(black_elo), 1, outputfile);
+    fwrite(&opening, sizeof(opening), 1, outputfile);  // Not perfect, but should be OK.
+    fwrite(&timestamp, sizeof(timestamp), 1, outputfile);
+    fwrite(&file_num, sizeof(file_num), 1, outputfile);
+    fwrite(&start_position, sizeof(start_position), 1, outputfile);
+    putc(computer_flag, outputfile);
+    putc(0, outputfile);  // No move.
+
+    free(bpfen);
+}
+
 static void
 print_epd_move_list(Game current_game,FILE *outputfile,
                     unsigned move_number, Boolean white_to_move,