void chess_generate_init(void) { static int initialized = 0; ChessSquare sq; int dirs, d; int slide, jump; int file, rank; int to_file, to_rank; if (initialized) return; initialized = 1; for (sq = CHESS_SQUARE_A1; sq <= CHESS_SQUARE_H8; sq++) { dirs = 0; file = chess_square_file(sq); rank = chess_square_rank(sq); for (d = 0; d < 8; d++) { slide = slides_array[d]; to_file = file + chess_square_file(slide + 36) - 4; to_rank = rank + chess_square_rank(slide + 36) - 4; if (to_file >= CHESS_FILE_A && to_file <= CHESS_FILE_H && to_rank >= CHESS_RANK_1 && to_rank <= CHESS_RANK_8) dirs |= dirs_array[d]; } slide_dirs[sq] = dirs; } for (sq = CHESS_SQUARE_A1; sq <= CHESS_SQUARE_H8; sq++) { dirs = 0; file = chess_square_file(sq); rank = chess_square_rank(sq); for (d = 0; d < 8; d++) { jump = jumps_array[d]; to_file = file + chess_square_file(jump + 36) - 4; to_rank = rank + chess_square_rank(jump + 36) - 4; if (to_file >= CHESS_FILE_A && to_file <= CHESS_FILE_H && to_rank >= CHESS_RANK_1 && to_rank <= CHESS_RANK_8) dirs |= dirs_array[d]; } jump_dirs[sq] = dirs; } }
ChessBoolean chess_position_validate(ChessPosition* position) { ChessSquare sq, other_king; ChessRank rank; ChessPiece pc; ChessPosition temp_position; chess_position_copy(position, &temp_position); temp_position.wking = CHESS_SQUARE_INVALID; temp_position.bking = CHESS_SQUARE_INVALID; for (sq = CHESS_SQUARE_A1; sq <= CHESS_SQUARE_H8; ++sq) { pc = position->piece[sq]; if (pc == CHESS_PIECE_WHITE_KING) { if (temp_position.wking != CHESS_SQUARE_INVALID) return CHESS_FALSE; /* Too many white kings */ temp_position.wking = sq; } else if (pc == CHESS_PIECE_BLACK_KING) { if (temp_position.bking != CHESS_SQUARE_INVALID) return CHESS_FALSE; /* Too many black kings */ temp_position.bking = sq; } else if (pc == CHESS_PIECE_WHITE_PAWN || pc == CHESS_PIECE_BLACK_PAWN) { rank = chess_square_rank(sq); if (rank == CHESS_RANK_1 || rank == CHESS_RANK_8) { /* Pawn on first or last rank */ return CHESS_FALSE; } } } if (temp_position.wking == CHESS_SQUARE_INVALID || temp_position.bking == CHESS_SQUARE_INVALID) { /* No white king or black king */ return CHESS_FALSE; } /* Clear any impossible castling states */ if (temp_position.piece[CHESS_SQUARE_E1] != CHESS_PIECE_WHITE_KING) { temp_position.castle &= ~CHESS_CASTLE_STATE_WKQ; } if (temp_position.piece[CHESS_SQUARE_H1] != CHESS_PIECE_WHITE_ROOK) { temp_position.castle &= ~CHESS_CASTLE_STATE_WK; } if (temp_position.piece[CHESS_SQUARE_A1] != CHESS_PIECE_WHITE_ROOK) { temp_position.castle &= ~CHESS_CASTLE_STATE_WQ; } if (temp_position.piece[CHESS_SQUARE_E8] != CHESS_PIECE_BLACK_KING) { temp_position.castle &= ~CHESS_CASTLE_STATE_BKQ; } if (temp_position.piece[CHESS_SQUARE_H8] != CHESS_PIECE_BLACK_ROOK) { temp_position.castle &= ~CHESS_CASTLE_STATE_BK; } if (temp_position.piece[CHESS_SQUARE_A8] != CHESS_PIECE_BLACK_ROOK) { temp_position.castle &= ~CHESS_CASTLE_STATE_BQ; } /* Clear en passant state if it's not valid */ if (temp_position.ep != CHESS_FILE_INVALID) { /* Valid would mean that a pawn (possibly) just moved to its 4th rank */ if ((temp_position.to_move == CHESS_COLOR_WHITE && temp_position.piece[chess_square_from_fr(temp_position.ep, CHESS_RANK_5)] != CHESS_PIECE_BLACK_PAWN) || (temp_position.to_move == CHESS_COLOR_BLACK && temp_position.piece[chess_square_from_fr(temp_position.ep, CHESS_RANK_4)] != CHESS_PIECE_WHITE_PAWN)) { temp_position.ep = CHESS_FILE_INVALID; } } other_king = (temp_position.to_move == CHESS_COLOR_WHITE) ? temp_position.bking : temp_position.wking; if (chess_generate_is_square_attacked(&temp_position, other_king, temp_position.to_move)) { /* Opponent's king is en prise */ return CHESS_FALSE; } /* All checks passed! */ chess_position_copy(&temp_position, position); return CHESS_TRUE; }
static ChessMove gen_next(ChessMoveGenerator* gen) { const ChessPosition* position = gen->position; ChessPiece piece; ChessColor color = chess_position_to_move(position); ChessPiece target; int dirs, dir; int piece_dirs; ChessRank start_rank, end_rank; int slide; int capture_dirs; ChessFile ep_file; ChessSquare ep; ChessMove move; if (gen->promote != CHESS_MOVE_PROMOTE_NONE) { gen_pawn_promotes: if (++gen->promote <= CHESS_MOVE_PROMOTE_QUEEN) return chess_move_make_promote(gen->sq, gen->to, gen->promote); gen->promote = CHESS_MOVE_PROMOTE_NONE; } for (; gen->sq <= CHESS_SQUARE_H8; gen->sq++) { piece = chess_position_piece(position, gen->sq); if (piece == CHESS_PIECE_NONE) continue; if (color != chess_piece_color(piece)) continue; switch (piece) { case CHESS_PIECE_WHITE_PAWN: case CHESS_PIECE_BLACK_PAWN: start_rank = (color == CHESS_COLOR_WHITE) ? CHESS_RANK_2 : CHESS_RANK_7; end_rank = (color == CHESS_COLOR_WHITE) ? CHESS_RANK_8 : CHESS_RANK_1; slide = (color == CHESS_COLOR_WHITE) ? SLIDE_N : SLIDE_S; capture_dirs = (color == CHESS_COLOR_WHITE) ? DIR_NE | DIR_NW : DIR_SE | DIR_SW; dirs = capture_dirs & slide_dirs[gen->sq]; ep_file = chess_position_ep(position); ep = (ep_file == CHESS_FILE_INVALID) ? CHESS_SQUARE_INVALID : chess_square_from_fr(ep_file, (color == CHESS_COLOR_WHITE) ? CHESS_RANK_6 : CHESS_RANK_3); if (gen->d == 0) { if (gen->to == CHESS_SQUARE_INVALID) { gen->to = gen->sq + slide; if (chess_position_piece(position, gen->to) == CHESS_PIECE_NONE) { if (chess_square_rank(gen->to) == end_rank) goto gen_pawn_promotes; else return chess_move_make(gen->sq, gen->to); } } else if (chess_square_rank(gen->sq) == start_rank && gen->to == gen->sq + slide) { gen->to += slide; if (chess_position_piece(position, gen->to) == CHESS_PIECE_NONE) return chess_move_make(gen->sq, gen->to); } gen->to = CHESS_SQUARE_INVALID; gen->d = 1; } while (gen->d < 8) { if (dirs_array[gen->d] & dirs) { gen->to = gen->sq + slides_array[gen->d]; piece = chess_position_piece(position, gen->to); gen->d += 2; if (piece != CHESS_PIECE_NONE && chess_piece_color(piece) != color) { if (chess_square_rank(gen->to) == end_rank) goto gen_pawn_promotes; else return chess_move_make(gen->sq, gen->to); } else if (gen->to == ep) { return chess_move_make(gen->sq, gen->to); } } else { gen->d += 2; } } gen->to = CHESS_SQUARE_INVALID; gen->d = 0; break; case CHESS_PIECE_WHITE_KNIGHT: case CHESS_PIECE_BLACK_KNIGHT: dirs = jump_dirs[gen->sq]; for (; gen->d < 8; gen->d++) { dir = dirs_array[gen->d]; if ((dir & dirs) == 0) continue; gen->to = gen->sq + jumps_array[gen->d]; target = chess_position_piece(position, gen->to); if (target == CHESS_PIECE_NONE || chess_piece_color(target) != color) { gen->d++; return chess_move_make(gen->sq, gen->to); } } gen->to = CHESS_SQUARE_INVALID; gen->d = 0; break; case CHESS_PIECE_WHITE_BISHOP: case CHESS_PIECE_BLACK_BISHOP: piece_dirs = bishop_dirs; goto gen_slide_moves; case CHESS_PIECE_WHITE_ROOK: case CHESS_PIECE_BLACK_ROOK: piece_dirs = rook_dirs; goto gen_slide_moves; case CHESS_PIECE_WHITE_QUEEN: case CHESS_PIECE_BLACK_QUEEN: piece_dirs = queen_dirs; gen_slide_moves: while (gen->d < 8) { dir = dirs_array[gen->d] & piece_dirs; if (gen->to == CHESS_SQUARE_INVALID) gen->to = gen->sq; do { dirs = slide_dirs[gen->to]; if ((dir & dirs) == 0) break; gen->to += slides_array[gen->d]; target = chess_position_piece(position, gen->to); if (target == CHESS_PIECE_NONE) return chess_move_make(gen->sq, gen->to); else if (chess_piece_color(target) != color) { move = chess_move_make(gen->sq, gen->to); gen->d++; gen->to = CHESS_SQUARE_INVALID; return move; } } while (target == CHESS_PIECE_NONE); gen->to = CHESS_SQUARE_INVALID; gen->d++; } gen->to = CHESS_SQUARE_INVALID; gen->d = 0; break; case CHESS_PIECE_WHITE_KING: case CHESS_PIECE_BLACK_KING: dirs = slide_dirs[gen->sq]; for (; gen->d < 8; gen->d++) { dir = dirs_array[gen->d]; if ((dir & dirs) == 0) continue; gen->to = gen->sq + slides_array[gen->d]; target = chess_position_piece(position, gen->to); if (target == CHESS_PIECE_NONE || chess_piece_color(target) != color) { gen->d++; return chess_move_make(gen->sq, gen->to); } } gen->to = CHESS_SQUARE_INVALID; gen->d = 0; break; default: assert(0); break; } } if (gen->castle == -1) { if (!chess_position_is_check(position)) gen->castle = chess_position_castle(position); else { gen->castle = CHESS_CASTLE_STATE_NONE; } } if (color == CHESS_COLOR_WHITE) { if ((gen->castle & CHESS_CASTLE_STATE_WK) && chess_position_piece(position, CHESS_SQUARE_F1) == CHESS_PIECE_NONE && chess_position_piece(position, CHESS_SQUARE_G1) == CHESS_PIECE_NONE && !chess_generate_is_square_attacked(position, CHESS_SQUARE_F1, CHESS_COLOR_BLACK) && !chess_generate_is_square_attacked(position, CHESS_SQUARE_G1, CHESS_COLOR_BLACK)) { gen->castle &= ~CHESS_CASTLE_STATE_WK; return chess_move_make(CHESS_SQUARE_E1, CHESS_SQUARE_G1); } if ((gen->castle & CHESS_CASTLE_STATE_WQ) && chess_position_piece(position, CHESS_SQUARE_B1) == CHESS_PIECE_NONE && chess_position_piece(position, CHESS_SQUARE_C1) == CHESS_PIECE_NONE && chess_position_piece(position, CHESS_SQUARE_D1) == CHESS_PIECE_NONE && !chess_generate_is_square_attacked(position, CHESS_SQUARE_D1, CHESS_COLOR_BLACK) && !chess_generate_is_square_attacked(position, CHESS_SQUARE_C1, CHESS_COLOR_BLACK)) { gen->castle &= ~CHESS_CASTLE_STATE_WQ; return chess_move_make(CHESS_SQUARE_E1, CHESS_SQUARE_C1); } } else { if ((gen->castle & CHESS_CASTLE_STATE_BK) && chess_position_piece(position, CHESS_SQUARE_F8) == CHESS_PIECE_NONE && chess_position_piece(position, CHESS_SQUARE_G8) == CHESS_PIECE_NONE && !chess_generate_is_square_attacked(position, CHESS_SQUARE_F8, CHESS_COLOR_WHITE) && !chess_generate_is_square_attacked(position, CHESS_SQUARE_G8, CHESS_COLOR_WHITE)) { gen->castle &= ~CHESS_CASTLE_STATE_BK; return chess_move_make(CHESS_SQUARE_E8, CHESS_SQUARE_G8); } if ((gen->castle & CHESS_CASTLE_STATE_BQ) && chess_position_piece(position, CHESS_SQUARE_B8) == CHESS_PIECE_NONE && chess_position_piece(position, CHESS_SQUARE_C8) == CHESS_PIECE_NONE && chess_position_piece(position, CHESS_SQUARE_D8) == CHESS_PIECE_NONE && !chess_generate_is_square_attacked(position, CHESS_SQUARE_D8, CHESS_COLOR_WHITE) && !chess_generate_is_square_attacked(position, CHESS_SQUARE_C8, CHESS_COLOR_WHITE)) { gen->castle &= ~CHESS_CASTLE_STATE_BQ; return chess_move_make(CHESS_SQUARE_E8, CHESS_SQUARE_C8); } } return 0; }