Exemple #1
0
/*
    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;
}
Exemple #2
0
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;
}
Exemple #3
0
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);
}
Exemple #4
0
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;
}