# HG changeset patch # User Mike Becker # Date 1784381408 -7200 # Node ID 21b76056a178ce8a49cf04c4516aa710b8112b3e # Parent c3ae96ed44d1f94a0567500e99584e5e30c8ba1a add sanity check for result in PGN move text diff -r c3ae96ed44d1 -r 21b76056a178 src/chess/pgn.c --- a/src/chess/pgn.c Sat Jul 18 15:01:04 2026 +0200 +++ b/src/chess/pgn.c Sat Jul 18 15:30:08 2026 +0200 @@ -36,13 +36,15 @@ #include enum { - pgn_error_missing_quote = 1, + pgn_no_error = 0, + pgn_error_missing_quote, pgn_error_missing_bracket, pgn_error_missing_brace, pgn_error_missing_dot, pgn_error_move_syntax, pgn_error_move_semantics, pgn_error_result_syntax, + pgn_error_result_mismatch, pgn_error_unexpected_eof, }; @@ -55,6 +57,7 @@ "Move is syntactically incorrect.", "Move is not valid according to chess rules.", "Result syntax is incorrect. Expected 1-0, 0-1, 1/2-1/2, or *.", + "Result at end of move text does not match result in tag roster.", "Unexpected end of data.", }; @@ -62,11 +65,29 @@ return pgn_error_strings[code]; } +static const char *pgn_result(GameState *gamestate) { + if (gamestate->stalemate || gamestate->remis) { + return "1/2-1/2"; + } else if (gamestate->wresign) { + return "0-1"; + } else if (gamestate->bresign) { + return "1-0"; + } else if (gamestate->checkmate) { + if (current_color(gamestate) == BLACK) { + return "1-0"; + } else { + return "0-1"; + } + } else { + return "*"; + } +} + int parse_pgn(const char *pgndata, GameState *gamestate) { int i; char c; - char result[8]; + char result[8] = {0}; char tagkey[32]; char tagvalue[128]; @@ -103,8 +124,8 @@ } // TODO: read clock info + if (strcmp("Result", tagkey) == 0) { - // TODO: why are we parsing this? it is never used! memcpy(result, tagvalue, 8); } } @@ -117,6 +138,7 @@ char movestr[10]; Move move; uint8_t curcol = WHITE; + bool movetext_ends_with_result = false; while (true) { /* move */ @@ -132,7 +154,7 @@ if (eval_move(gamestate, movestr, &move, curcol) != VALID_MOVE_SYNTAX) { return pgn_error_move_syntax; - } + } if (validate_move(gamestate, &move) != VALID_MOVE_SEMANTICS) { return pgn_error_move_semantics; } @@ -174,9 +196,11 @@ return pgn_error_result_syntax; } } + movetext_ends_with_result = true; break; } else if (c == '/') { gamestate->remis = !gamestate->stalemate; + movetext_ends_with_result = true; break; } else { /* oops, it was a move number, go back! */ @@ -196,8 +220,16 @@ } curcol = opponent_color(curcol); } + + /* sanity check result - if it was specified */ + if (movetext_ends_with_result && result[0]) { + if (strncmp(result, pgn_result(gamestate), 8) != 0) { + fprintf(stderr, "Fuck it: %s\n", result); + return pgn_error_result_mismatch; + } + } - return 0; + return pgn_no_error; } int read_pgn(FILE *stream, GameState *gamestate) { @@ -283,23 +315,7 @@ fprintf(stream, "[Round \"%s\"]\n", "-"); fprintf(stream, "[White \"%s\"]\n", pgn_player_name(gamestate, WHITE)); fprintf(stream, "[Black \"%s\"]\n", pgn_player_name(gamestate, BLACK)); - - char *result; - if (gamestate->stalemate || gamestate->remis) { - result = "1/2-1/2"; - } else if (gamestate->wresign) { - result = "0-1"; - } else if (gamestate->bresign) { - result = "1-0"; - } else if (gamestate->checkmate) { - if (current_color(gamestate) == BLACK) { - result = "1-0"; - } else { - result = "0-1"; - } - } else { - result = "*"; - } + const char *result = pgn_result(gamestate); fprintf(stream, "[Result \"%s\"]\n\n", result); // TODO: add optional clock info