char char_from_piece(piece_t p) { static const char sPIECES[6] = { 'P', 'N', 'B', 'R', 'Q', 'K' }; if (piece_color(p) == BLACK) { return tolower(sPIECES[piece_type(p)]); } return sPIECES[piece_type(p)]; }
static sint piece_char(uint piece) { return type_char(piece_type(piece)); }
/* * Add all pseudo-legal captures that the given piece (N/B/Q/K) can make. */ static void generate_piece_captures(const position_t* pos, square_t from, piece_t piece, move_t** moves_head) { move_t* moves = *moves_head; square_t to; // Note: I unrolled all these loops to handle each direction explicitly. // The idea was to increase performance, but it's only about 1% faster // for much more code, so it's possible I'll change this back later. switch (piece_type(piece)) { case KING: to = from - 17; if (pos->board[to] != EMPTY && can_capture(piece, pos->board[to])) { moves = add_move(pos, create_move(from, to, piece, pos->board[to]), moves); } to = from - 16; if (pos->board[to] != EMPTY && can_capture(piece, pos->board[to])) { moves = add_move(pos, create_move(from, to, piece, pos->board[to]), moves); } to = from - 15; if (pos->board[to] != EMPTY && can_capture(piece, pos->board[to])) { moves = add_move(pos, create_move(from, to, piece, pos->board[to]), moves); } to = from - 1; if (pos->board[to] != EMPTY && can_capture(piece, pos->board[to])) { moves = add_move(pos, create_move(from, to, piece, pos->board[to]), moves); } to = from + 1; if (pos->board[to] != EMPTY && can_capture(piece, pos->board[to])) { moves = add_move(pos, create_move(from, to, piece, pos->board[to]), moves); } to = from + 15; if (pos->board[to] != EMPTY && can_capture(piece, pos->board[to])) { moves = add_move(pos, create_move(from, to, piece, pos->board[to]), moves); } to = from + 16; if (pos->board[to] != EMPTY && can_capture(piece, pos->board[to])) { moves = add_move(pos, create_move(from, to, piece, pos->board[to]), moves); } to = from + 17; if (pos->board[to] != EMPTY && can_capture(piece, pos->board[to])) { moves = add_move(pos, create_move(from, to, piece, pos->board[to]), moves); } break; case KNIGHT: to = from - 33; if (pos->board[to] != EMPTY && can_capture(piece, pos->board[to])) { moves = add_move(pos, create_move(from, to, piece, pos->board[to]), moves); } to = from - 31; if (pos->board[to] != EMPTY && can_capture(piece, pos->board[to])) { moves = add_move(pos, create_move(from, to, piece, pos->board[to]), moves); } to = from - 18; if (pos->board[to] != EMPTY && can_capture(piece, pos->board[to])) { moves = add_move(pos, create_move(from, to, piece, pos->board[to]), moves); } to = from - 14; if (pos->board[to] != EMPTY && can_capture(piece, pos->board[to])) { moves = add_move(pos, create_move(from, to, piece, pos->board[to]), moves); } to = from + 14; if (pos->board[to] != EMPTY && can_capture(piece, pos->board[to])) { moves = add_move(pos, create_move(from, to, piece, pos->board[to]), moves); } to = from + 18; if (pos->board[to] != EMPTY && can_capture(piece, pos->board[to])) { moves = add_move(pos, create_move(from, to, piece, pos->board[to]), moves); } to = from + 31; if (pos->board[to] != EMPTY && can_capture(piece, pos->board[to])) { moves = add_move(pos, create_move(from, to, piece, pos->board[to]), moves); } to = from + 33; if (pos->board[to] != EMPTY && can_capture(piece, pos->board[to])) { moves = add_move(pos, create_move(from, to, piece, pos->board[to]), moves); } break; case BISHOP: for (to=from-17; pos->board[to]==EMPTY; to-=17) {} if (can_capture(piece, pos->board[to])) { moves = add_move(pos, create_move(from, to, piece, pos->board[to]), moves); } for (to=from-15; pos->board[to]==EMPTY; to-=15) {} if (can_capture(piece, pos->board[to])) { moves = add_move(pos, create_move(from, to, piece, pos->board[to]), moves); } for (to=from+15; pos->board[to]==EMPTY; to+=15) {} if (can_capture(piece, pos->board[to])) { moves = add_move(pos, create_move(from, to, piece, pos->board[to]), moves); } for (to=from+17; pos->board[to]==EMPTY; to+=17) {} if (can_capture(piece, pos->board[to])) { moves = add_move(pos, create_move(from, to, piece, pos->board[to]), moves); } break; case ROOK: for (to=from-16; pos->board[to]==EMPTY; to-=16) {} if (can_capture(piece, pos->board[to])) { moves = add_move(pos, create_move(from, to, piece, pos->board[to]), moves); } for (to=from-1; pos->board[to]==EMPTY; to-=1) {} if (can_capture(piece, pos->board[to])) { moves = add_move(pos, create_move(from, to, piece, pos->board[to]), moves); } for (to=from+1; pos->board[to]==EMPTY; to+=1) {} if (pos->board[to] != EMPTY && can_capture(piece, pos->board[to])) { moves = add_move(pos, create_move(from, to, piece, pos->board[to]), moves); } for (to=from+16; pos->board[to]==EMPTY; to+=16) {} if (can_capture(piece, pos->board[to])) { moves = add_move(pos, create_move(from, to, piece, pos->board[to]), moves); } break; case QUEEN: for (to=from-17; pos->board[to]==EMPTY; to-=17) {} if (can_capture(piece, pos->board[to])) { moves = add_move(pos, create_move(from, to, piece, pos->board[to]), moves); } for (to=from-16; pos->board[to]==EMPTY; to-=16) {} if (can_capture(piece, pos->board[to])) { moves = add_move(pos, create_move(from, to, piece, pos->board[to]), moves); } for (to=from-15; pos->board[to]==EMPTY; to-=15) {} if (can_capture(piece, pos->board[to])) { moves = add_move(pos, create_move(from, to, piece, pos->board[to]), moves); } for (to=from-1; pos->board[to]==EMPTY; to-=1) {} if (can_capture(piece, pos->board[to])) { moves = add_move(pos, create_move(from, to, piece, pos->board[to]), moves); } for (to=from+1; pos->board[to]==EMPTY; to+=1) {} if (pos->board[to] != EMPTY && can_capture(piece, pos->board[to])) { moves = add_move(pos, create_move(from, to, piece, pos->board[to]), moves); } for (to=from+15; pos->board[to]==EMPTY; to+=15) {} if (can_capture(piece, pos->board[to])) { moves = add_move(pos, create_move(from, to, piece, pos->board[to]), moves); } for (to=from+16; pos->board[to]==EMPTY; to+=16) {} if (can_capture(piece, pos->board[to])) { moves = add_move(pos, create_move(from, to, piece, pos->board[to]), moves); } for (to=from+17; pos->board[to]==EMPTY; to+=17) {} if (can_capture(piece, pos->board[to])) { moves = add_move(pos, create_move(from, to, piece, pos->board[to]), moves); } break; default: assert(false); } *moves_head = moves; }
int alpha_beta(app_t *app, cnodeptr_t parent, int alpha, int beta, int depth) { int palpha = alpha; int i, score = -MATE, highest = -MATE; node_t node; move_t cutoff = 0; piece_t p; init_node(&node); assert(app); app->search.nodes++; node.depth = depth; /* max depth */ if (app->game.board->ply > (SEARCH_MAXDEPTH - 1)) { /* return evaluate(app->board); */ return quiescent(app, parent, alpha, beta); } /* recursive base */ if (depth == 0) { return evaluate(app->game.board); } /* draws */ if (repetitions(app) || (app->game.board->half >= 100)) { return 0; } /* if we are checked, set the nodes checked flag */ if (check(app->game.board, app->game.board->side)) { node.flags |= NODE_CHECK; /* extend our search by 1 depth if we are in check */ /* NOTES: we may want to NOT extend our search here if the parent is in check, because the means we already extended once */ depth++; } /* TODO: - NULL moves - Late-move reduction - Tactical extensions (pins & forks -> depth++) */ /* probe our table */ if (probe_hash(&app->hash, app->game.board, &cutoff, &score, depth, alpha, beta) == TRUE) { app->hash.cut++; return score; } /* generate moves */ generate_moves(app->game.board, &node.ml, &node.ml); /* reset score */ score = -MATE; /* try to match our hash hit move */ if (cutoff != 0) { for (i = 0; i < node.ml.count; i++) { if (node.ml.moves[i] == cutoff) { node.ml.scores[i] = 20000; break; } } } /* search negamax */ for (i = 0; i < node.ml.count; i++) { /* get the next move ordered */ next_move(i, &node.ml); if (!(do_move(app->game.board, &app->game.undo, node.ml.moves[i]))) continue; score = -alpha_beta(app, &node, -beta, -alpha, depth - 1); node.made++; undo_move(app->game.board, &app->game.undo); /* score whatever is best so far */ if (score > highest) { node.best = node.ml.moves[i]; highest = score; /* update alpha */ if (score > alpha) { if (score >= beta) { /* non-captures causing beta cutoffs (killers) */ if (!is_capture(node.ml.moves[i])) { app->game.board->killers[1][app->game.board->ply] = app->game.board->killers[0][app->game.board->ply]; app->game.board->killers[0][app->game.board->ply] = node.ml.moves[i]; } /* store this beta in our transposition table */ store_hash(&app->hash, app->game.board, node.best, beta, HASH_BETA, depth); return beta; } /* update alpha */ alpha = score; /* update our history */ if (!is_capture(node.best)) { p = app->game.board->pos.squares[move_from(node.best)]; app->game.board->history[piece_color(p)][piece_type(p)][move_to(node.best)] += depth; } } } } /* check for checkmate or stalemate */ if (!node.made) { if (node.flags & NODE_CHECK) { return -MATE + app->game.board->ply; } else { return 0; } } if (alpha != palpha) { /* store this as an exact, since we beat alpha */ store_hash(&app->hash, app->game.board, node.best, highest, HASH_EXACT, depth); } else { /* store the current alpha */ store_hash(&app->hash, app->game.board, node.best, alpha, HASH_ALPHA, depth); } return alpha; }
/* * Generate pseudo-legal moves which are neither captures nor promotions. */ int generate_pseudo_quiet_moves(const position_t* pos, move_t* moves) { move_t* moves_head = moves; color_t side = pos->side_to_move; piece_t piece; square_t from; // Castling. Castles are considered pseudo-legal if we have appropriate // castling rights, the squares between king and rook are unoccupied, // and the intermediate square is unattacked. Therefore checking for // legality just requires seeing if we're in check afterwards. // This is messy for Chess960, so it's separated into separate cases. square_t my_king_home = king_home + side*A8; if (!options.chess960) { if (has_oo_rights(pos, side) && pos->board[my_king_home+1] == EMPTY && pos->board[my_king_home+2] == EMPTY && !is_square_attacked((position_t*)pos,my_king_home+1,side^1)) { assert(pos->board[my_king_home] == create_piece(pos->side_to_move, KING)); moves = add_move(pos, create_move_castle(my_king_home, my_king_home+2, create_piece(side, KING)), moves); } if (has_ooo_rights(pos, side) && pos->board[my_king_home-1] == EMPTY && pos->board[my_king_home-2] == EMPTY && pos->board[my_king_home-3] == EMPTY && !is_square_attacked((position_t*)pos,my_king_home-1,side^1)) { assert(pos->board[my_king_home] == create_piece(pos->side_to_move, KING)); moves = add_move(pos, create_move_castle(my_king_home, my_king_home-2, create_piece(side, KING)), moves); } } else { if (has_oo_rights(pos, side)) { square_t my_f1 = F1 + side*A8; square_t my_g1 = G1 + side*A8; square_t my_kr = king_rook_home + side*A8; bool castle_ok = true; // Check that rook is unimpeded. for (square_t sq = MIN(my_kr, my_f1); sq <= MAX(my_kr, my_f1); ++sq) { if (sq != my_kr && sq != my_king_home && pos->board[sq] != EMPTY) { castle_ok = false; break; } } // Check that the king is unimpeded and unattacked if (castle_ok) { for (square_t sq = MIN(my_king_home, my_g1); sq <= my_g1; ++sq) { if (sq != my_king_home && sq != my_kr && pos->board[sq] != EMPTY) { castle_ok = false; break; } if (sq != my_g1 && is_square_attacked((position_t*)pos, sq, side^1)) { castle_ok = false; break; } } } if (castle_ok) moves = add_move(pos, create_move_castle(my_king_home, my_g1, create_piece(side, KING)), moves); } if (has_ooo_rights(pos, side)) { square_t my_d1 = D1 + side*A8; square_t my_c1 = C1 + side*A8; square_t my_qr = queen_rook_home + side*A8; bool castle_ok = true; // Check that rook is unimpeded. for (square_t sq = MIN(my_qr, my_d1); sq <= MAX(my_qr, my_d1); ++sq) { if (sq != my_qr && sq != my_king_home && pos->board[sq] != EMPTY) { castle_ok = false; break; } } // Check that the king is unimpeded and unattacked if (castle_ok) { for (square_t sq = MIN(my_king_home, my_c1); sq <= MAX(my_king_home, my_c1); ++sq) { if (sq != my_king_home && sq != my_qr && pos->board[sq] != EMPTY) { castle_ok = false; break; } if (sq != my_c1 && is_square_attacked((position_t*)pos, sq, side^1)) { castle_ok = false; break; } } } if (castle_ok) moves = add_move(pos, create_move_castle(my_king_home, my_c1, create_piece(side, KING)), moves); } } for (int i = 0; i < pos->num_pieces[side]; ++i) { from = pos->pieces[side][i]; piece = pos->board[from]; assert(piece_color(piece) == side && piece_type(piece) != PAWN); generate_piece_noncaptures(pos, from, piece, &moves); } piece = create_piece(side, PAWN); for (int i=0; i<pos->num_pawns[side]; ++i) { from = pos->pawns[side][i]; assert(pos->board[from] == piece); generate_pawn_quiet_moves(pos, from, piece, &moves); } *moves = 0; return (moves-moves_head); }