/* Tests `make_move` and `undo_move` by making and undoing every single move for a set of fen positions, then making sure the resulting board is the same as the original board. */ int test_make_undo_move() { static const char* fen_positions[] = { "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1", "1B4n1/5k2/5P2/4p2N/P2QR3/p2pb2K/3Pb2R/8 w - - 0 1", "3b4/N2P1p2/1kP4p/p4n1K/2p5/1PPp2P1/1p6/8 w - - 0 1", "8/4p3/3R4/2P4R/P3Pp2/6kp/8/K7 w - - 0 1", "8/8/p3K3/k1N2B2/3P4/6Pp/5P1p/4R3 w - - 0 1", "K7/8/1P3r2/1P2b1p1/8/5p2/1Q2q2n/7k b - - 0 1", "1q1rr3/P1RN1P1p/Kp3p2/2P2p2/b4pP1/n4kb1/8/3Rn3 b - - 0 1", "8/2P5/Bp4Kn/q7/5P2/k7/8/B7 b - - 0 1", "8/8/1B6/8/P1b4K/8/8/1k6 b - - 0 1", "8/P2kp3/7B/1R3r2/1bK4p/Q4pp1/P6N/8 b - - 0 1" }; board_t board[ELEMENTS_IN(fen_positions)], board_original[ELEMENTS_IN(fen_positions)]; move_t move_list[256]; for (int i = 0; i < ELEMENTS_IN(fen_positions); i++) { set_board_from_fen_string(&board[i], fen_positions[i]); memcpy(&board_original[i], &board[i], sizeof(board_t)); } for (int i = 0; i < ELEMENTS_IN(fen_positions); i++) { move_t* end_of_list = generate_pseudolegal_moves(&board[i], move_list); for (int j = 0; move_list[j] != *end_of_list; j++) { make_move(&board[i], move_list[j]); undo_move(&board[i], move_list[j]); if (!compare_boards(&board[i], &board_original[i])) { fprintf(stderr, "test_make_undo_move -- Test \"%s\" failed: move %d.\n", fen_positions[i], j); return TEST_ERROR; } } } return TEST_SUCCESS; }
int add_solution(char board[8][8], solution **head) { if (*head) { solution *n; solution *tmp = *head; while (tmp) { if (compare_boards(board, tmp->solution)) return 0; tmp = tmp->next; } if (!(n = malloc(sizeof(*n)))) { return 0; } tmp = *head; while (tmp->next) tmp = tmp->next; tmp-> next = n; n->next = 0; copy_solution(n, board); return 1; } if (!(*head = malloc(sizeof(solution)))) return 0; (*head)->next = 0; copy_solution(*head, board); return 1; }
TEST_F(board_test, scroll_down) { //given std::set<position> positions{ position(1, 1), position(1, 2), position(1, 3) }; board b(4, 4, 3, 5, irandom_mock_, { blue , yellow , green , purple , yellow , green , blue , black , purple , green , yellow , purple , blue , blue , green , black } ); board expected(4, 4, 3, 5, irandom_mock_, { blue , none , green , purple , yellow , none , blue , black , purple , none , yellow , purple , blue , yellow , blue , black } ); b.select(position(1, 3)); b.select(position(2, 3)); b.swap(); std::cout << b; EXPECT_EQ(3u, b.matches().size()); //when auto results = b.scroll_down(); //then EXPECT_EQ(positions, results); compare_boards(b, expected); }
int main(int argc, char *argv[]) { const char *const Help = "fens2pgn - converts multiple FENs into single PGN file\n" "Syntax: fens2pgn [arguments] [output file] [input file]\n" "Arguments:\n" " -f force validity of every chess move\n" " -o take next argument as output file\n" " -q quiet - doesn't print informations to stderr\n" " -v verbose - notify of every skipped FEN\n" " -h, --help print this help text\n" " -u, --usage short usage information\n" " -V, --version display program version"; if (argc < 2) { puts(Help); return 0; } struct { bool validate; bool quiet; bool verbose; char *read_from_file; char *write_to_file; } parameters = {0, 0, 0, NULL, NULL}; const struct option long_options[] = { {"help", 0, NULL, 'h'}, {"usage", 0, NULL, 'u'}, {"version", 0, NULL, 'V'}, {NULL, 0, NULL, 0} }; for (int option, long_option_index; (option = getopt_long(argc, argv, "fho:quvV", long_options, &long_option_index)) != -1;) switch (option) { case 'f': parameters.validate = 1; break; case 'h': puts(Help); return 0; case 'o': parameters.write_to_file = optarg; break; case 'q': parameters.quiet = 1; parameters.verbose = 0; break; case 'u': puts("Syntax: fens2pgn [-fqv] [-o OUTPUT_FILE] [INPUT_FILE]\n" " [-h|--help] [-u|--usage] [-V|--version]"); return 0; case 'v': parameters.quiet = 0; parameters.verbose = 1; break; case 'V': puts("fens2pgn " VERSION "\n" "Copyright (C) 2015 Paweł Zacharek"); return 0; default: return EINVAL; } if (optind == argc - 1) // 1 unknown parameter (input file name) parameters.read_from_file = argv[optind]; else if (optind != argc) { // more unknown parameters fputs("Invalid argument(s) found.\n", stderr); return EINVAL; } FILE *input = (parameters.read_from_file == NULL ? stdin : fopen(parameters.read_from_file, "r")); FILE *output = (parameters.write_to_file == NULL ? stdout : fopen(parameters.write_to_file, "w")); if (input == NULL) { fprintf(stderr, "Can't open file \"%.255s\" for reading.\n", parameters.read_from_file); return ENOENT; } if (output == NULL) { fprintf(stderr, "Can't open file \"%.255s\" for writing.\n", parameters.read_from_file); return EACCES; } char fen_buffer[MAX_FEN_LENGHT + 1], fen_placement_buffer[MAX_PLACEMENT_LENGHT + 1]; char store_space[STORE_SPACE_SIZE], store_move[STORE_MOVE_SIZE]; char board_buffer_1[8][8], board_buffer_2[8][8]; char (*board_1)[8] = board_buffer_1, (*board_2)[8] = board_buffer_2; int fen_number = 0, move_number = 1, number_of_characters_in_line = 0; struct field distinctions[4], *field, *previous_field; struct piece king_placement; signed char number_of_differences; char control_character, whose_move = 'w', castling_prospects[4 + 1] = "KQkq", en_passant_field[2 + 1] = "-"; char control_string[8 + 1], result[7 + 1]; // to store respectively "Result \"" and "1/2-1/2" result[0] = '\0'; bool metadata_included = 0, fens_are_incomplete = 0, first_move_number_already_written = 0, move_is_invalid = 0; // beginning of METADATA section fscanf(input, " %c", &control_character); while (control_character == '[') { metadata_included = 1; putc('[', output); if (fscanf(input, "%8[^\n]", control_string) == 1) { if (strncmp(control_string, "Result \"", 8) == 0 && fscanf(input, "%7[-01/2*]", result) == 1) fprintf(output, "%s%s", control_string, result); else fputs(control_string, output); } for (int character; (character = getc(input)) != EOF;) { putc(character, output); if (character == '\n') break; } fscanf(input, " %c", &control_character); } ungetc(control_character, input); // beginning of GAME section if (fscanf(input, "%" STR(MAX_FEN_LENGHT) "[-0-9a-h/rnqkpRNBQKP w]", fen_buffer) != 1) { // reading the first FEN fputs("No valid FEN found.\n", stderr); return EILSEQ; } ++fen_number; sscanf(fen_buffer, "%" STR(MAX_PLACEMENT_LENGHT) "[1-8/rnbqkpRNBQKP]", fen_placement_buffer); if (strncmp(fen_buffer, "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1", 56) != 0) { // it isn't classical complete FEN if (sscanf(fen_buffer, "%*[1-8/rnbqkpRNBQKP] %c %4s %2s %*d %d", &whose_move, castling_prospects, en_passant_field, &move_number) != 4) { // FENs are incomplete fens_are_incomplete = 1; if (parameters.quiet != 1) fputs("FENs considered incomplete.\n", stderr); // not corrupts redirected output whose_move = 'w'; strcpy(castling_prospects, "KQkq"); strcpy(en_passant_field, "-"); move_number = 1; } if (fens_are_incomplete == 0 || strncmp(fen_buffer, "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR", 43) != 0) // starting position isn't classical fprintf(output, "[SetUp \"1\"]\n[FEN \"%s\"]\n", fen_buffer); } if (metadata_included == 1) putc('\n', output); if (fen2board(fen_placement_buffer, board_1, parameters.validate) == 0) { fputs("There are more than 2 kings or invalid pieces on the first board. Terminating.\n", stderr); return 0; } for (;;) { if (fscanf(input, " %" STR(MAX_FEN_LENGHT) "[-0-9a-h/rnqkpRNBQKP w]", fen_buffer) != 1) break; ++fen_number; sscanf(fen_buffer, "%" STR(MAX_PLACEMENT_LENGHT) "[1-8/rnbqkpRNBQKP]", fen_placement_buffer); if (fen2board(fen_placement_buffer, board_2, parameters.validate) == 0) { fprintf(stderr, "There are more than 2 kings or invalid pieces on board nr %d. Terminating.\n", fen_number); return 0; } number_of_differences = compare_boards((const char (*)[8])board_1, (const char (*)[8])board_2, distinctions); if (number_of_differences < 2) { if (parameters.verbose == 1) fprintf(stderr, "Skipped \"%s\" (%d%s FEN).\n", fen_buffer, fen_number, Ordinal_Number_Suffix(fen_number)); continue; } if (number_of_differences > 4) { fprintf(stderr, "Can't compare \"%s\" (%d%s FEN) with the previous one.\n", fen_buffer, fen_number, Ordinal_Number_Suffix(fen_number)); return EILSEQ; } switch (number_of_differences) { case 2: // ordinary move or capture if (distinctions[0].piece_after != ' ') { field = &distinctions[0]; previous_field = &distinctions[1]; } else { // it means that (distinctions[1].piece_after != ' ') field = &distinctions[1]; previous_field = &distinctions[0]; } switch (what_to_write((const char (*)[8])board_2, (const struct field *)field, (const struct field *)previous_field, en_passant_field, parameters.validate, whose_move, castling_prospects)) { case PAWN_MOVE: snprintf(store_move, STORE_MOVE_SIZE, "%c%hhd", field->alphabetical, field->numerical); break; case PAWN_CAPTURE: snprintf(store_move, STORE_MOVE_SIZE, "%cx%c%hhd", previous_field->alphabetical, field->alphabetical, field->numerical); break; case PAWN_PROMOTION: snprintf(store_move, STORE_MOVE_SIZE, "%c%hhd=%c", field->alphabetical, field->numerical, toupper(field->piece_after)); break; case PAWN_CAPTURE_PROMOTION: snprintf(store_move, STORE_MOVE_SIZE, "%cx%c%hhd=%c", previous_field->alphabetical, field->alphabetical, field->numerical, toupper(field->piece_after)); break; case PIECE_MOVE: snprintf(store_move, STORE_MOVE_SIZE, field->piece_before != ' ' ? "%cx%c%hhd" : "%c%c%hhd", toupper(field->piece_after), field->alphabetical, field->numerical); break; case DIS_LINE: case DIS_WHICHEVER: snprintf(store_move, STORE_MOVE_SIZE, field->piece_before != ' ' ? "%c%cx%c%hhd" : "%c%c%c%hhd", toupper(field->piece_after), previous_field->alphabetical, field->alphabetical, field->numerical); break; case DIS_COLUMN: snprintf(store_move, STORE_MOVE_SIZE, field->piece_before != ' ' ? "%c%hhdx%c%hhd" : "%c%hhd%c%hhd", toupper(field->piece_after), previous_field->numerical, field->alphabetical, field->numerical); break; case DIS_BOTH: snprintf(store_move, STORE_MOVE_SIZE, field->piece_before != ' ' ? "%c%c%hhdx%c%hhd" : "%c%c%hhd%c%hhd", toupper(field->piece_after), previous_field->alphabetical, previous_field->numerical, field->alphabetical, field->numerical); break; case INVALID_MOVE: move_is_invalid = 1; break; } break; case 3: // en passant capture if (parameters.validate != 1) { if (whose_move == 'w') snprintf(store_move, STORE_MOVE_SIZE, "%cx%c%hhd", distinctions[0].alphabetical == distinctions[2].alphabetical ? distinctions[1].alphabetical : distinctions[2].alphabetical, distinctions[0].alphabetical, distinctions[0].numerical); else snprintf(store_move, STORE_MOVE_SIZE, "%cx%c%hhd", distinctions[0].alphabetical == distinctions[2].alphabetical ? distinctions[1].alphabetical : distinctions[0].alphabetical, distinctions[2].alphabetical, distinctions[2].numerical); } else { char all_fields[6 + 1]; snprintf(all_fields, sizeof all_fields, "%c%c%c%c%c%c", distinctions[0].piece_before, distinctions[0].piece_after, distinctions[1].piece_before, distinctions[1].piece_after, distinctions[2].piece_before, distinctions[2].piece_after); if (whose_move == 'w' && en_passant_field[0] == distinctions[0].alphabetical && en_passant_field[1] - '0' == distinctions[0].numerical && distinctions[0].numerical == distinctions[2].numerical + 1 && distinctions[1].alphabetical == distinctions[2].alphabetical - 1 && ( distinctions[0].alphabetical == distinctions[2].alphabetical && memcmp(all_fields, " PP p ", 6) == 0 || distinctions[0].alphabetical != distinctions[2].alphabetical && memcmp(all_fields, " Pp P ", 6) == 0 )) snprintf(store_move, STORE_MOVE_SIZE, "%cx%c%hhd", distinctions[0].alphabetical == distinctions[2].alphabetical ? distinctions[1].alphabetical : distinctions[2].alphabetical, distinctions[0].alphabetical, distinctions[0].numerical); else if (whose_move == 'b' && en_passant_field[0] == distinctions[2].alphabetical && en_passant_field[1] - '0' == distinctions[2].numerical && distinctions[0].numerical == distinctions[2].numerical + 1 && distinctions[0].alphabetical == distinctions[1].alphabetical - 1 && ( distinctions[0].alphabetical == distinctions[2].alphabetical && memcmp(all_fields, "P p p", 6) == 0 || distinctions[0].alphabetical != distinctions[2].alphabetical && memcmp(all_fields, "p P p", 6) == 0 )) snprintf(store_move, STORE_MOVE_SIZE, "%cx%c%hhd", distinctions[0].alphabetical == distinctions[2].alphabetical ? distinctions[1].alphabetical : distinctions[0].alphabetical, distinctions[2].alphabetical, distinctions[2].numerical); else move_is_invalid = 1; } break; case 4: // castling if (parameters.validate != 1) snprintf(store_move, STORE_MOVE_SIZE, distinctions[0].alphabetical == 'a' ? "O-O-O" : "O-O"); else { if (distinctions[0].alphabetical == 'a' && is_king_checked_while_castling(distinctions[3].piece_before, distinctions[3].alphabetical, distinctions[3].numerical, (const char (*)[8])board_1, (const char (*)[8])board_2, -1) == 0 && ( whose_move == 'w' && strchr(castling_prospects, 'Q') != NULL && memcmp(&board_1[8 - 1][0], "R K", 5) == 0 && memcmp(&board_2[8 - 1][0], " KR ", 5) == 0 || whose_move == 'b' && strchr(castling_prospects, 'q') != NULL && memcmp(&board_1[8 - 8][0], "r k", 5) == 0 && memcmp(&board_2[8 - 8][0], " kr ", 5) == 0 )) snprintf(store_move, STORE_MOVE_SIZE, "O-O-O"); else if (distinctions[3].alphabetical == 'h' && is_king_checked_while_castling(distinctions[0].piece_before, distinctions[0].alphabetical, distinctions[0].numerical, (const char (*)[8])board_1, (const char (*)[8])board_2, 1) == 0 && ( whose_move == 'w' && strchr(castling_prospects, 'K') != NULL && memcmp(&board_1[8 - 1][4], "K R", 4) == 0 && memcmp(&board_2[8 - 1][0], " RK ", 4) == 0 || whose_move == 'b' && strchr(castling_prospects, 'k') != NULL && memcmp(&board_1[8 - 8][4], "k r", 4) == 0 && memcmp(&board_2[8 - 8][0], " rk ", 4) == 0 )) snprintf(store_move, STORE_MOVE_SIZE, "O-O"); else move_is_invalid = 1; } break; } if (parameters.validate == 1 && move_is_invalid == 1) { move_is_invalid = 0; if (parameters.quiet != 1) fprintf(stderr, "Skipped \"%s\" (%d%s FEN) because the calculated move is invalid.\n", fen_buffer, fen_number, Ordinal_Number_Suffix(fen_number)); continue; } if (parameters.validate == 1 && find_piece(whose_move == 'w' ? 'K' : 'k', &king_placement, (const char (*)[8])board_2)) if (is_piece_attacked(king_placement.type, king_placement.alphabetical, king_placement.numerical, (const char (*)[8])board_2, 1)) { if (parameters.quiet != 1) fprintf(stderr, "Skipped \"%s\" (%d%s FEN) because we're still in check.\n", fen_buffer, fen_number, Ordinal_Number_Suffix(fen_number)); continue; } if (find_piece(whose_move == 'w' ? 'k' : 'K', &king_placement, (const char (*)[8])board_2)) if (is_piece_attacked(king_placement.type, king_placement.alphabetical, king_placement.numerical, (const char (*)[8])board_2, 1)) { // king is checked if (does_king_cannot_move((const struct piece *)&king_placement, board_2) && ( is_piece_attacked(king_placement.type, king_placement.alphabetical, king_placement.numerical, (const char (*)[8])board_2, 2) // double check || is_it_checkmate(king_placement.alphabetical, king_placement.numerical, (const char (*)[8])board_2, (const char *)en_passant_field) )) snprintf(store_move + strlen(store_move), STORE_MOVE_SIZE - strlen(store_move), "#"); else snprintf(store_move + strlen(store_move), STORE_MOVE_SIZE - strlen(store_move), "+"); } if (first_move_number_already_written == 1) strcpy(store_space, " "); if (whose_move == 'w' || first_move_number_already_written == 0) snprintf(store_space + (first_move_number_already_written ? 1 : 0), STORE_SPACE_SIZE - (first_move_number_already_written ? 1 : 0), "%d. ", move_number); number_of_characters_in_line += strlen(store_space) - 1; // we can omit trailing space if (number_of_characters_in_line >= 80) { store_space[0] = '\n'; number_of_characters_in_line = strlen(store_space) - 2; // the first space is also not included } number_of_characters_in_line += strlen(store_move) + 1; // add previously disregarded trailing space if (number_of_characters_in_line >= 80) { store_space[strlen(store_space) - 1] = '\n'; number_of_characters_in_line = strlen(store_move); // doesn't count trailing space of 'store_space' array } fprintf(output, "%s%s", store_space, store_move); first_move_number_already_written = 1; if (whose_move == 'b') ++move_number; whose_move = (whose_move == 'b' ? 'w' : 'b'); swap_pointers((void **)&board_1, (void **)&board_2); } if (result[0] != '\0') fprintf(output, "%c%s\n", number_of_characters_in_line + 1 + strlen(result) >= 80 ? '\n' : ' ', result); else fprintf(output, "%c*\n", number_of_characters_in_line + 2 >= 80 ? '\n' : ' '); fclose(input); fclose(output); return 0; }