void move_eval(const board_t * board, int eval) { int index; int best_value, value; int move; ASSERT(board!=NULL); ASSERT(value_is_ok(eval)); move = board->move; ASSERT(move_is_ok(move)); // exceptions if ((move & (1 << 15)) != 0) return; if (MOVE_TO(move) == board->cap_sq) return; if (MOVE_IS_CASTLE(move)) return; ASSERT(!MOVE_IS_PROMOTE(move)); ASSERT(!MOVE_IS_EN_PASSANT(move)); ASSERT(board->cap_sq==Empty); ASSERT(!board_is_check(board)); index = PIECE_TO_12(board->square[MOVE_TO(move)]) * 64 + SQUARE_TO_64(MOVE_TO(move)); ASSERT(index>=0&&index<HistorySize); if (eval > ValueQueen) { value = ValueQueen; } else if (eval < -ValueQueen) { value = -ValueQueen; } else { value = eval; } best_value = HistEval[index]; ASSERT(best_value<=HistoryEvalMax); ASSERT(best_value>=-HistoryEvalMax); if (value >= best_value) { if (value == HistoryEvalValue) value++; HistEval[index] = value; } else { if (HistEval[index] > -ValueQueen) { if (best_value == HistoryEvalValue+1) { HistEval[index] -= 2; } else { HistEval[index]--; } } } ASSERT(HistEval[index]!=HistoryEvalValue); ASSERT(HistEval[index]<=HistoryEvalMax); ASSERT(HistEval[index]>=-HistoryEvalMax); }
static int mvv_lva(int move, const board_t * board) { int piece, capture, promote; int value; ASSERT(move_is_ok(move)); ASSERT(board!=NULL); ASSERT(move_is_tactical(move,board)); if (MOVE_IS_EN_PASSANT(move)) { // en-passant capture value = 5; // PxP } else if ((capture = board->square[MOVE_TO(move)]) != Empty) { // normal capture piece = board->square[MOVE_FROM(move)]; value = PIECE_ORDER(capture) * 6 - PIECE_ORDER(piece) + 5; ASSERT(value>=0&&value<30); } else { // promote ASSERT(MOVE_IS_PROMOTE(move)); promote = move_promote(move); value = PIECE_ORDER(promote) - 5; ASSERT(value>=-4&&value<0); } ASSERT(value>=-4&&value<+30); return value; }
static bool capture_is_good(int move, const board_t * board) { int piece, capture; ASSERT(move_is_ok(move)); ASSERT(board!=NULL); ASSERT(MOVE_IS_TACTICAL(move,board)); // special cases if (MOVE_IS_EN_PASSANT(move)) return true; if (MOVE_IS_UNDER_PROMOTE(move)) return false; // REMOVE ME? // captures and queen promotes capture = board->square[MOVE_TO(move)]; if (capture != Empty) { // capture ASSERT(move_is_capture(move,board)); if (MOVE_IS_PROMOTE(move)) return true; // promote-capture piece = board->square[MOVE_FROM(move)]; if (VALUE_PIECE(capture) >= VALUE_PIECE(piece)) return true; } return see_move(move,board,0) >= 0; }
bool pseudo_is_legal(int move, board_t * board) { int me, opp; int from, to; int piece; bool legal; int king; undo_t undo[1]; ASSERT(move_is_ok(move)); ASSERT(board!=NULL); // init me = board->turn; opp = COLOUR_OPP(me); from = MOVE_FROM(move); to = MOVE_TO(move); piece = board->square[from]; ASSERT(COLOUR_IS(piece,me)); // slow test for en-passant captures if (MOVE_IS_EN_PASSANT(move)) { move_do(board,move,undo); legal = !IS_IN_CHECK(board,me); move_undo(board,move,undo); return legal; } // king moves (including castle) if (PIECE_IS_KING(piece)) { legal = !is_attacked(board,to,opp); if (DEBUG) { ASSERT(board->square[from]==piece); board->square[from] = Empty; ASSERT(legal==!is_attacked(board,to,opp)); board->square[from] = piece; } return legal; } // pins if (is_pinned(board,from,me)) { king = KING_POS(board,me); return DELTA_INC_LINE(king-to) == DELTA_INC_LINE(king-from); // does not discover the line } return true; }
int see(Move_t move, Board_t *board) { Board_t temp_board = *global_board(); Board_t *ptr = &temp_board; int attacking_piece = MOVE_PIECE(move); int captured_piece; int gain[32]; int depth = 0; int from = MOVE_FROM(move); int to = MOVE_TO(move); if (ENEMY_PAWNS(ptr) & SQ_MASK(to)) captured_piece = PAWN; else if (ENEMY_KNIGHTS(ptr) & SQ_MASK(to)) captured_piece = KNIGHT; else if (ENEMY_BISHOPS(ptr) & SQ_MASK(to)) captured_piece = BISHOP; else if (ENEMY_ROOKS(ptr) & SQ_MASK(to)) captured_piece = ROOK; else if (ENEMY_QUEENS(ptr) & SQ_MASK(to)) captured_piece = QUEEN; else captured_piece = 0; gain[0] = Piece_Values[captured_piece]; if (MOVE_IS_EN_PASSANT(move)) gain[0] = Piece_Values[PAWN]; do { make_quick_move_on_board(ptr, from, to, captured_piece, attacking_piece); captured_piece = attacking_piece; depth++; gain[depth] = Piece_Values[captured_piece] - gain[depth-1]; if (MAX(-gain[depth-1], gain[depth]) < 0) break; attacking_piece = get_least_valuable_attacker(ptr, to, &from); } while (attacking_piece); while (--depth) gain[depth-1] = -MAX(-gain[depth-1], gain[depth]); return gain[0]; }
int see_move(int move, const board_t * board) { int att, def; int from, to; alists_t alists[1]; int value, piece_value; int piece, capture; alist_t * alist; int pos; ASSERT(move_is_ok(move)); ASSERT(board!=NULL); // init from = MOVE_FROM(move); to = MOVE_TO(move); // move the piece piece_value = 0; piece = board->square[from]; ASSERT(piece_is_ok(piece)); att = PIECE_COLOUR(piece); def = COLOUR_OPP(att); // promote if (MOVE_IS_PROMOTE(move)) { ASSERT(PIECE_IS_PAWN(piece)); piece = move_promote(move); ASSERT(piece_is_ok(piece)); ASSERT(COLOUR_IS(piece,att)); } piece_value += VALUE_PIECE(piece); // clear attacker lists ALIST_CLEAR(alists->alist[Black]); ALIST_CLEAR(alists->alist[White]); // find hidden attackers alists_hidden(alists,board,from,to); // capture the piece value = 0; capture = board->square[to]; if (capture != Empty) { ASSERT(piece_is_ok(capture)); ASSERT(COLOUR_IS(capture,def)); value += VALUE_PIECE(capture); } // promote if (MOVE_IS_PROMOTE(move)) { value += VALUE_PIECE(piece) - ValuePawn; } // en-passant if (MOVE_IS_EN_PASSANT(move)) { ASSERT(value==0); ASSERT(PIECE_IS_PAWN(board->square[SQUARE_EP_DUAL(to)])); value += ValuePawn; alists_hidden(alists,board,SQUARE_EP_DUAL(to),to); } // build defender list alist = alists->alist[def]; alist_build(alist,board,to,def); if (alist->size == 0) return value; // no defender => stop SEE // build attacker list alist = alists->alist[att]; alist_build(alist,board,to,att); // remove the moved piece (if it's an attacker) for (pos = 0; pos < alist->size && alist->square[pos] != from; pos++) ; if (pos < alist->size) alist_remove(alist,pos); // SEE search value -= see_rec(alists,board,def,to,piece_value); return value; }
bool capture_is_good(int move, const board_t * board, bool in_pv) { #else static bool capture_is_good(int move, const board_t * board, bool in_pv) { #endif int piece, capture; int see_value; // WHM 11/22/08 ASSERT(move_is_ok(move)); ASSERT(board!=NULL); ASSERT(move_is_tactical(move,board)); // special cases if (MOVE_IS_EN_PASSANT(move)) return true; if (move_is_under_promote(move)) return false; // REMOVE ME? Keep, looks good to me. WHM; // if (MOVE_IS_PROMOTE(move)) return true; // WHM; promote-to-queen, measures a little weaker // too many garbage lines going nuts. // captures and queen promotes capture = board->square[MOVE_TO(move)]; piece = board->square[MOVE_FROM(move)]; if (capture != Empty) { // capture ASSERT(move_is_capture(move,board)); if (MOVE_IS_PROMOTE(move)) return true; // capture a piece on Rank8 and promote to queen if (VALUE_PIECE(capture) >= VALUE_PIECE(piece)) return true; } // return see_move(move,board) >= 0; WHM 11/22/08 // WHM 11/22/08 START see_value = see_move(move,board); if (see_value >= 0) return true; if (TryNodePVQueenPromotes) { if (in_pv && MOVE_IS_PROMOTE(move)) { ASSERT(!move_is_under_promote(move)); return true; // WHM: } } if (TryKingAttackSacs || TryKingBoxSacs || TryPasserSacs) { if (in_pv && see_value > -ValueBishop && capture != Empty) { ASSERT(COLOUR_IS(capture,COLOUR_OPP(board->turn))); // king attack sacs. if (TryKingAttackSacs) { if (narrow_piece_attack_king(board, piece, MOVE_TO(move), KING_POS(board,COLOUR_OPP(board->turn)))) { return true; } } // sacrifice attacks around the narrow/close king box can be examined more fully. Rybka lessons. if (TryKingBoxSacs) { if (DISTANCE(MOVE_TO(move),KING_POS(board,COLOUR_OPP(board->turn))) <= 1) { return true; } } // passer sacrifices... if (TryPasserSacs) { if (PIECE_IS_PAWN(capture) && PAWN_RANK(MOVE_TO(move),COLOUR_OPP(board->turn)) >= Rank6) { return true; } } } } // WHM 11/22/08 END return false; }
void move_do(board_t * board, int move, undo_t * undo) { int me, opp; int from, to; int piece, pos, capture; int old_flags, new_flags; int delta; int sq; int pawn, rook; ASSERT(board!=NULL); ASSERT(move_is_ok(move)); ASSERT(undo!=NULL); ASSERT(board_is_legal(board)); // initialise undo undo->capture = false; undo->turn = board->turn; undo->flags = board->flags; undo->ep_square = board->ep_square; undo->ply_nb = board->ply_nb; undo->cap_sq = board->cap_sq; undo->opening = board->opening; undo->endgame = board->endgame; undo->key = board->key; undo->pawn_key = board->pawn_key; undo->material_key = board->material_key; // init me = board->turn; opp = COLOUR_OPP(me); from = MOVE_FROM(move); to = MOVE_TO(move); piece = board->square[from]; ASSERT(COLOUR_IS(piece,me)); // update key stack ASSERT(board->sp<StackSize); board->stack[board->sp++] = board->key; // update turn board->turn = opp; board->key ^= RANDOM_64(RandomTurn); // update castling rights old_flags = board->flags; new_flags = old_flags & CastleMask[from] & CastleMask[to]; board->flags = new_flags; board->key ^= Castle64[new_flags^old_flags]; // HACK // update en-passant square if ((sq=board->ep_square) != SquareNone) { board->key ^= RANDOM_64(RandomEnPassant+SQUARE_FILE(sq)-FileA); board->ep_square = SquareNone; } if (PIECE_IS_PAWN(piece)) { delta = to - from; if (delta == +32 || delta == -32) { pawn = PAWN_MAKE(opp); if (board->square[to-1] == pawn || board->square[to+1] == pawn) { board->ep_square = (from + to) / 2; board->key ^= RANDOM_64(RandomEnPassant+SQUARE_FILE(to)-FileA); } } } // update move number (captures are handled later) board->ply_nb++; if (PIECE_IS_PAWN(piece)) board->ply_nb = 0; // conversion // update last square board->cap_sq = SquareNone; // remove the captured piece sq = to; if (MOVE_IS_EN_PASSANT(move)) sq = SQUARE_EP_DUAL(sq); if ((capture=board->square[sq]) != Empty) { ASSERT(COLOUR_IS(capture,opp)); ASSERT(!PIECE_IS_KING(capture)); undo->capture = true; undo->capture_square = sq; undo->capture_piece = capture; undo->capture_pos = board->pos[sq]; square_clear(board,sq,capture,true); board->ply_nb = 0; // conversion board->cap_sq = to; } // move the piece if (MOVE_IS_PROMOTE(move)) { // promote undo->pawn_pos = board->pos[from]; square_clear(board,from,piece,true); piece = move_promote(move); // insert the promote piece in MV order for (pos = board->piece_size[me]; pos > 0 && piece > board->square[board->piece[me][pos-1]]; pos--) // HACK ; square_set(board,to,piece,pos,true); board->cap_sq = to; } else { // normal move square_move(board,from,to,piece,true); } // move the rook in case of castling if (MOVE_IS_CASTLE(move)) { rook = Rook64 | COLOUR_FLAG(me); // HACK if (to == G1) { square_move(board,H1,F1,rook,true); } else if (to == C1) { square_move(board,A1,D1,rook,true); } else if (to == G8) { square_move(board,H8,F8,rook,true); } else if (to == C8) { square_move(board,A8,D8,rook,true); } else { ASSERT(false); } } // debug ASSERT(board_is_ok(board)); }
static int full_quiescence(board_t * board, int alpha, int beta, int depth, int height, mv_t pv[]) { bool in_check; int old_alpha; int value, best_value; int best_move; int move; int opt_value; attack_t attack[1]; sort_t sort[1]; undo_t undo[1]; mv_t new_pv[HeightMax]; int cd; ASSERT(board!=NULL); ASSERT(range_is_ok(alpha,beta)); ASSERT(depth_is_ok(depth)); ASSERT(height_is_ok(height)); ASSERT(pv!=NULL); ASSERT(board_is_legal(board)); ASSERT(depth<=0); // init SearchStack[height].best_move = MoveNone; SearchStack[height].move = MoveNone; SearchStack[height].threat_move = MoveNone; SearchStack[height].reduced = false; SearchCurrent->node_nb++; SearchInfo->check_nb--; PV_CLEAR(pv); if (height > SearchCurrent->max_depth) SearchCurrent->max_depth = height; if (SearchInfo->check_nb <= 0) { SearchInfo->check_nb += SearchInfo->check_inc; search_check(); } // draw? if (board_is_repetition(board) || recog_draw(board)) return ValueDraw; // mate-distance pruning if (UseDistancePruning) { // lower bound value = VALUE_MATE(height+2); // does not work if the current position is mate if (value > alpha && board_is_mate(board)) value = VALUE_MATE(height); if (value > alpha) { alpha = value; if (value >= beta) return value; } // upper bound value = -VALUE_MATE(height+1); if (value < beta) { beta = value; if (value <= alpha) return value; } } // more init attack_set(attack,board); in_check = ATTACK_IN_CHECK(attack); if (in_check) { ASSERT(depth<0); depth++; // in-check extension } // height limit if (height >= HeightMax-1) return eval(board, alpha, beta); // more init old_alpha = alpha; best_value = ValueNone; best_move = MoveNone; /* if (UseDelta) */ opt_value = +ValueInf; if (!in_check) { // lone-king stalemate? if (simple_stalemate(board)) return ValueDraw; // stand pat value = eval(board, alpha, beta); ASSERT(value>best_value); best_value = value; if (value > alpha) { alpha = value; if (value >= beta) goto cut; } if (UseDelta) { opt_value = value + DeltaMargin; ASSERT(opt_value<+ValueInf); } } // move loop /* cd = CheckDepth; if(cd < 0 && board->piece_size[board->turn] <= 5) cd++; */ sort_init_qs(sort,board,attack, depth>=CheckDepth /* depth>=cd */); while ((move=sort_next_qs(sort)) != MoveNone) { SearchStack[height].move = move; // delta pruning if (UseDelta && beta == old_alpha+1) { if (!in_check && !move_is_check(move,board) && !capture_is_dangerous(move,board)) { ASSERT(move_is_tactical(move,board)); // optimistic evaluation value = opt_value; int to = MOVE_TO(move); int capture = board->square[to]; if (capture != Empty) { value += VALUE_PIECE(capture); } else if (MOVE_IS_EN_PASSANT(move)) { value += ValuePawn; } if (MOVE_IS_PROMOTE(move)) value += ValueQueen - ValuePawn; // pruning if (value <= alpha) { if (value > best_value) { best_value = value; PV_CLEAR(pv); } continue; } } } move_do(board,move,undo); value = -full_quiescence(board,-beta,-alpha,depth-1,height+1,new_pv); move_undo(board,move,undo); if (value > best_value) { best_value = value; pv_cat(pv,new_pv,move); if (value > alpha) { alpha = value; best_move = move; SearchStack[height].best_move = move; if (value >= beta) goto cut; } } } // ALL node if (best_value == ValueNone) { // no legal move ASSERT(board_is_mate(board)); return VALUE_MATE(height); } cut: ASSERT(value_is_ok(best_value)); return best_value; }
static int full_quiescence(board_t * board, int alpha, int beta, int depth, int height, mv_t pv[], int ThreadId) { bool in_check; int old_alpha; int value, best_value; int best_move; int move; int opt_value; attack_t attack[1]; sort_t sort[1]; undo_t undo[1]; mv_t new_pv[HeightMax]; int probe_score, probe_depth; int trans_move, trans_depth, trans_flags, trans_value; entry_t * found_entry; ASSERT(board!=NULL); ASSERT(range_is_ok(alpha,beta)); ASSERT(depth_is_ok(depth)); ASSERT(height_is_ok(height)); ASSERT(pv!=NULL); ASSERT(board_is_legal(board)); ASSERT(depth<=0); // init SearchCurrent[ThreadId]->node_nb++; SearchInfo[ThreadId]->check_nb--; PV_CLEAR(pv); if (height > SearchCurrent[ThreadId]->max_depth) SearchCurrent[ThreadId]->max_depth = height; if (SearchInfo[ThreadId]->check_nb <= 0) { SearchInfo[ThreadId]->check_nb += SearchInfo[ThreadId]->check_inc; search_check(ThreadId); } // draw? if (board_is_repetition(board)) return ValueDraw; /* Interior node recognizer from scorpio by Daniel Shawul -> dont probe at the leaves as this will slow down search For 4/3 pieces probe there also. -> After captures and pawn moves assume exact score and cutoff tree, because we are making progress. Note this is not done only for speed. -> if we are far from root (depth / 2), assume exact score and cutoff tree */ /* if (egbb_is_loaded && board->piece_nb <= 4){ if (probe_bitbases(board, probe_score)){ probe_score = value_from_trans(probe_score,height); return probe_score; } }*/ if (recog_draw(board,ThreadId)) return ValueDraw; // mate-distance pruning if (UseDistancePruning) { // lower bound value = VALUE_MATE(height+2); // does not work if the current position is mate if (value > alpha && board_is_mate(board)) value = VALUE_MATE(height); if (value > alpha) { alpha = value; if (value >= beta) return value; } // upper bound value = -VALUE_MATE(height+1); if (value < beta) { beta = value; if (value <= alpha) return value; } } // transposition table: added by Jerry Donald, about +20 elo self-play // new in Toga II 4.0: accept hash hits in PV ~ +3 elo trans_move = MoveNone; if (UseTrans) { if (trans_retrieve(Trans,&found_entry,board->key,&trans_move,&trans_depth,&trans_flags,&trans_value)) { trans_value = value_from_trans(trans_value,height); if ((UseExact && trans_value != ValueNone && TRANS_IS_EXACT(trans_flags)) || (TRANS_IS_LOWER(trans_flags) && trans_value >= beta) || (TRANS_IS_UPPER(trans_flags) && trans_value <= alpha)) { return trans_value; } } } // more init attack_set(attack,board); in_check = ATTACK_IN_CHECK(attack); if (in_check) { ASSERT(depth<0); depth++; // in-check extension } // height limit if (height >= HeightMax-1) return eval(board, alpha, beta, ThreadId); // more init old_alpha = alpha; best_value = ValueNone; best_move = MoveNone; /* if (UseDelta) */ opt_value = +ValueInf; if (!in_check) { // lone-king stalemate? if (simple_stalemate(board)) return ValueDraw; // stand pat value = eval(board, alpha, beta, ThreadId); ASSERT(value>best_value); best_value = value; if (value > alpha) { alpha = value; if (value >= beta) goto cut; } if (UseDelta) { opt_value = value + DeltaMargin; ASSERT(opt_value<+ValueInf); } } // move loop /* cd = CheckDepth; if(cd < 0 && board->piece_size[board->turn] <= 5) cd++; */ sort_init_qs(sort,board,attack, depth>=SearchCurrent[ThreadId]->CheckDepth /* depth>=cd */); while ((move=sort_next_qs(sort)) != MoveNone) { // delta pruning if (UseDelta && beta == old_alpha+1) { // i.e. non-PV if (!in_check && !move_is_check(move,board) && !capture_is_dangerous(move,board)) { ASSERT(move_is_tactical(move,board)); // optimistic evaluation value = opt_value; int to = MOVE_TO(move); int capture = board->square[to]; if (capture != Empty) { value += VALUE_PIECE(capture); } else if (MOVE_IS_EN_PASSANT(move)) { value += ValuePawn; } if (MOVE_IS_PROMOTE(move)) value += ValueQueen - ValuePawn; // pruning if (value <= alpha) { if (value > best_value) { best_value = value; PV_CLEAR(pv); } continue; } } } move_do(board,move,undo); value = -full_quiescence(board,-beta,-alpha,depth-1,height+1,new_pv,ThreadId); move_undo(board,move,undo); if (value > best_value) { best_value = value; pv_cat(pv,new_pv,move); if (value > alpha) { alpha = value; best_move = move; if (value >= beta) goto cut; } } } // ALL node if (best_value == ValueNone) { // no legal move ASSERT(board_is_mate(board)); return VALUE_MATE(height); } cut: // store result in hash table (JD) if (UseTrans) { trans_move = best_move; trans_depth = 0; trans_flags = TransUnknown; if (best_value > old_alpha) trans_flags |= TransLower; if (best_value < beta) trans_flags |= TransUpper; trans_value = value_to_trans(best_value,height); trans_store(Trans,board->key,trans_move,trans_depth,trans_flags,trans_value); } ASSERT(value_is_ok(best_value)); return best_value; }