static int move_value(int move, const board_t * board, int height, int trans_killer, int ThreadId) { int value; ASSERT(move_is_ok(move)); ASSERT(board!=NULL); ASSERT(height_is_ok(height)); ASSERT(trans_killer==MoveNone||move_is_ok(trans_killer)); if (false) { } else if (move == trans_killer) { // transposition table killer value = TransScore; } else if (move_is_tactical(move,board)) { // capture or promote value = capture_value(move,board); } else if (move == Killer[ThreadId][height][0]) { // killer 1 value = KillerScore; } else if (move == Killer[ThreadId][height][1]) { // killer 2 value = KillerScore - 2; } else if (height > 2 && move == Killer[ThreadId][height-2][0]) { // killer 1 value = KillerScore - 1; } else if (height > 2 && move == Killer[ThreadId][height-2][1]) { // killer 2 value = KillerScore - 3; } else { // quiet move value = quiet_move_value(move,board,ThreadId); } 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; }
static int history_prob(int move, const board_t * board, int ThreadId) { int value; unsigned int index; ASSERT(move_is_ok(move)); ASSERT(board!=NULL); ASSERT(!move_is_tactical(move,board) || see_move(move,board) < 0 || move_is_under_promote(move)); index = history_index(move,board); ASSERT(HistHit[index]<=HistTot[index]); ASSERT(HistTot[index]<HistTotMax); // int * int assumed to be int by C++, large values and overflows need to use double on the interim product. value = int(double(double(HistHit[index]) * HistoryMax) / HistTot[index]); // // for uint16 or short or unsigned short the simple formula is OK. // value = ( ( (HistHit[index]) * HistoryMax) / HistTot[index]); // HistoryMax instead ASSERT(value>=1 && value<=16384); return value; }
void good_move(int move, const board_t * board, int depth, int height) { int index; int i; ASSERT(move_is_ok(move)); ASSERT(board!=NULL); ASSERT(depth_is_ok(depth)); ASSERT(height_is_ok(height)); if (move_is_tactical(move,board)) return; // killer if (Killer[height][0] != move) { Killer[height][1] = Killer[height][0]; Killer[height][0] = move; } ASSERT(Killer[height][0]==move); ASSERT(Killer[height][1]!=move); // history index = history_index(move,board); History[index] += HISTORY_INC(depth); if (History[index] >= HistoryMax) { for (i = 0; i < HistorySize; i++) { History[i] = (History[i] + 1) / 2; } } }
static bool capture_is_dangerous(int move, const board_t * board) { int piece, capture; ASSERT(move_is_ok(move)); ASSERT(board!=NULL); ASSERT(move_is_tactical(move,board)); piece = MOVE_PIECE(move,board); if (PIECE_IS_PAWN(piece) && PAWN_RANK(MOVE_TO(move),board->turn) >= Rank7) { return true; } capture = move_capture(move,board); if (PIECE_IS_QUEEN(capture)) return true; if (PIECE_IS_PAWN(capture) && PAWN_RANK(MOVE_TO(move),board->turn) <= Rank2) { return true; } return false; }
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; }
void history_good(int move, const board_t * board, int ThreadId) { unsigned int index; ASSERT(move_is_ok(move)); ASSERT(board!=NULL); ASSERT(!move_is_tactical(move,board)); // if ( move_is_tactical(move,board)) return; // history index = history_index(move,board); HistHit[index]++; HistTot[index]++; if (HistTot[index] >= HistTotMax) { HistHit[index] = (HistHit[index] + 1) / 2; HistTot[index] = (HistTot[index] + 1) / 2; } ASSERT(HistHit[index]<=HistTot[index]); ASSERT(HistTot[index]<HistTotMax); }
static int move_value_simple(int move, const board_t * board) { int value; ASSERT(move_is_ok(move)); ASSERT(board!=NULL); value = HistoryScore; if (move_is_tactical(move,board)) value = mvv_lva(move,board); return value; }
void history_success(int move, const board_t * board) { int index; ASSERT(move_is_ok(move)); ASSERT(board!=NULL); if (move_is_tactical(move,board)) return; // history index = history_index(move,board); FailHighStats[index].success++; }
static int history_index(int move, const board_t * board) { int index; ASSERT(move_is_ok(move)); ASSERT(board!=NULL); ASSERT(!move_is_tactical(move,board)); index = PIECE_TO_12(board->square[MOVE_FROM(move)]) * 64 + SQUARE_TO_64(MOVE_FROM(move)) * 64 + SQUARE_TO_64(MOVE_TO(move)); ASSERT(index>=0&&index<HistorySize); return index; }
static int quiet_move_value(int move, const board_t * board, int ThreadId) { int value; unsigned int index; ASSERT(move_is_ok(move)); ASSERT(board!=NULL); ASSERT(!move_is_tactical(move,board)); index = history_index(move,board); value = HistoryScore + History[index]; ASSERT(value>=HistoryScore&&value<=KillerScore-4); return value; }
static bool move_is_dangerous(int move, const board_t * board) { int piece; ASSERT(move_is_ok(move)); ASSERT(board!=NULL); ASSERT(!move_is_tactical(move,board)); piece = MOVE_PIECE(move,board); if (PIECE_IS_PAWN(piece) && is_passed(board,MOVE_TO(move)) /*PAWN_RANK(MOVE_TO(move),board->turn) >= Rank7*/) { return true; } return false; }
static int history_prob(int move, const board_t * board) { int value; int index; ASSERT(move_is_ok(move)); ASSERT(board!=NULL); ASSERT(!move_is_tactical(move,board)); index = history_index(move,board); ASSERT(HistHit[index]<=HistTot[index]); ASSERT(HistTot[index]<HistoryMax); value = (HistHit[index] * 16384) / HistTot[index]; ASSERT(value>=0&&value<=16384); return value; }
static int capture_value(int move, const board_t * board) { int value; ASSERT(move_is_ok(move)); ASSERT(board!=NULL); ASSERT(move_is_tactical(move,board)); value = mvv_lva(move,board); if (capture_is_good(move,board,false)) { value += GoodScore; } else { value += BadScore; } ASSERT(value>=-30000&&value<=+30000); return value; }
static unsigned int history_index(int move, const board_t * board) { unsigned int index; int move_from = MOVE_FROM(move); int piece_12 = PIECE_TO_12(board->square[move_from]); ASSERT(move_is_ok(move)); ASSERT(board!=NULL); ASSERT(!move_is_tactical(move,board)); // index = PIECE_TO_12(board->square[MOVE_FROM(move)]) * 64 + SQUARE_TO_64(MOVE_TO(move)); fruit 2.1 // index = PIECE_TO_12(board->square[MOVE_FROM(move)]) * (64*64) + SQUARE_TO_64(MOVE_FROM(move)) * 64 + SQUARE_TO_64(MOVE_TO(move)); Toga II 1.2.1 (corrected?). index = 64 * piece_12 + SQUARE_TO_64(MOVE_TO(move)); // fruit 2.1 and Toga II 1.4beta5c ASSERT(index >= 0 && index < HistorySize); return index; }
void history_bad(int move, const board_t * board) { int index; ASSERT(move_is_ok(move)); ASSERT(board!=NULL); if (move_is_tactical(move,board)) return; // history index = history_index(move,board); HistTot[index]++; if (HistTot[index] >= HistoryMax) { HistHit[index] = (HistHit[index] + 1) / 2; HistTot[index] = (HistTot[index] + 1) / 2; } ASSERT(HistHit[index]<=HistTot[index]); ASSERT(HistTot[index]<HistoryMax); }
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; }
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; }
static int full_search(board_t * board, int alpha, int beta, int depth, int height, mv_t pv[], int node_type) { bool in_check; bool single_reply; bool mate_threat; int trans_move, trans_depth, trans_min_depth, trans_max_depth, trans_min_value, trans_max_value; int min_value, max_value; int old_alpha; int value, best_value; int move, best_move; int new_depth; int played_nb; int i; int opt_value; bool reduced; int mb; attack_t attack[1]; sort_t sort[1]; undo_t undo[1]; mv_t new_pv[HeightMax]; mv_t played[256]; int FutilityMargin; ASSERT(board!=NULL); ASSERT(range_is_ok(alpha,beta)); ASSERT(depth_is_ok(depth)); ASSERT(height_is_ok(height)); ASSERT(pv!=NULL); ASSERT(node_type==NodePV||node_type==NodeCut||node_type==NodeAll); ASSERT(board_is_legal(board)); // horizon? if (depth <= 0){ if (node_type == NodePV) CheckDepth = 1 - CheckNb - 1; else CheckDepth = 1 - CheckNb; return full_quiescence(board,alpha,beta,0,height,pv); } // init SearchStack[height].best_move = MoveNone; SearchStack[height].move = MoveNone; SearchStack[height].threat_move = MoveNone; SearchStack[height].reduced = false; mate_threat = 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; } } // transposition table trans_move = MoveNone; if (UseTrans && depth >= TransDepth) { if (trans_retrieve(Trans,board->key,&trans_move,&trans_min_depth,&trans_max_depth,&trans_min_value,&trans_max_value)) { // trans_move is now updated if (node_type != NodePV) { if (UseMateValues) { if (trans_min_value > +ValueEvalInf && trans_min_depth < depth) { trans_min_depth = depth; } if (trans_max_value < -ValueEvalInf && trans_max_depth < depth) { trans_max_depth = depth; } } min_value = -ValueInf; if (DEPTH_MATCH(trans_min_depth,depth)) { min_value = value_from_trans(trans_min_value,height); if (min_value >= beta) return min_value; } max_value = +ValueInf; if (DEPTH_MATCH(trans_max_depth,depth)) { max_value = value_from_trans(trans_max_value,height); if (max_value <= alpha) return max_value; } if (min_value == max_value) return min_value; // exact match } } } // height limit if (height >= HeightMax-1) return eval(board, alpha, beta); // more init old_alpha = alpha; best_value = ValueNone; best_move = MoveNone; played_nb = 0; attack_set(attack,board); in_check = ATTACK_IN_CHECK(attack); // null-move pruning if (UseNull && depth >= NullDepth && node_type != NodePV) { if (!in_check && !value_is_mate(beta) && do_null(board) && (!UseNullEval || depth <= NullReduction+1 || eval(board,alpha, beta) >= beta)) { // null-move search new_depth = depth - NullReduction - 1; //new_depth = depth - R_adpt(board->piece_size[board->turn]+board->pawn_size[board->turn],depth,NullReduction) - 1; move_do_null(board,undo); value = -full_search(board,-beta,-beta+1,new_depth,height+1,new_pv,NODE_OPP(node_type)); move_undo_null(board,undo); // verification search if (UseVer && depth > VerReduction) { if (value >= beta && (!UseVerEndgame || do_ver(board))) { new_depth = depth - VerReduction; ASSERT(new_depth>0); value = full_no_null(board,alpha,beta,new_depth,height,new_pv,NodeCut,trans_move,&move); if (value >= beta) { ASSERT(move==new_pv[0]); played[played_nb++] = move; best_move = move; SearchStack[height].move = move; SearchStack[height].best_move = move; best_value = value; pv_copy(pv,new_pv); goto cut; } } } // pruning if (value >= beta) { if (value > +ValueEvalInf) value = +ValueEvalInf; // do not return unproven mates ASSERT(!value_is_mate(value)); // pv_cat(pv,new_pv,MoveNull); best_move = MoveNone; best_value = value; goto cut; } SearchStack[height].threat_move = SearchStack[height+1].best_move; /* if (SearchStack[height-1].reduced){ // Idea by Tord Romstad if (MOVE_FROM(SearchStack[height+1].best_move) == MOVE_TO(SearchStack[height-1].move)) return alpha-1; */ /* if(((MOVE_TO(SearchStack[height - 2].threat_move) == MOVE_FROM(SearchStack[height - 2].move)) && (MOVE_TO(SearchStack[height].threat_move) == MOVE_TO(SearchStack[height - 2].move))) || ((MOVE_TO(SearchStack[height - 2].threat_move) != MOVE_FROM(SearchStack[height - 2].move)) && (MOVE_TO(SearchStack[height].threat_move) == MOVE_TO(SearchStack[height - 2].threat_move)))) return alpha-1; */ // } } } // mate threat /* mate_threat = false; if (value <= VALUE_MATE(height+2)){ mate_threat = true; } */ // Internal Iterative Deepening if (UseIID && depth >= IIDDepth && node_type == NodePV && trans_move == MoveNone) { // new_depth = depth - IIDReduction; new_depth = MIN(depth - IIDReduction,depth/2); ASSERT(new_depth>0); value = full_search(board,alpha,beta,new_depth,height,new_pv,node_type); if (value <= alpha) value = full_search(board,-ValueInf,beta,new_depth,height,new_pv,node_type); trans_move = new_pv[0]; } // move generation sort_init(sort,board,attack,depth,height,trans_move); single_reply = false; if (in_check && LIST_SIZE(sort->list) == 1) single_reply = true; // HACK // move loop opt_value = +ValueInf; while ((move=sort_next(sort)) != MoveNone) { SearchStack[height].move = move; // history_tried(move,board); // extensions new_depth = full_new_depth(depth,move,board,single_reply,mate_threat,node_type==NodePV, height); // futility pruning if (UseFutility && depth <= 2 && node_type != NodePV) { if (!in_check && new_depth < depth && !move_is_tactical(move,board) && !move_is_dangerous(move,board)) { ASSERT(!move_is_check(move,board)); // optimistic evaluation if (opt_value == +ValueInf) { if (depth==2){ FutilityMargin = FutilityMargin2; } else{ FutilityMargin = FutilityMargin1; } opt_value = eval(board,alpha,beta) + FutilityMargin; ASSERT(opt_value<+ValueInf); } value = opt_value; // pruning if (value <= alpha) { if (value > best_value) { best_value = value; PV_CLEAR(pv); } continue; } } } // history pruning /* reduced = false; if (UseHistory && depth >= HistoryDepth && node_type != NodePV) { if (!in_check && played_nb >= HistoryMoveNb && new_depth < depth) { ASSERT(best_value!=ValueNone); ASSERT(played_nb>0); ASSERT(sort->pos>0&&move==LIST_MOVE(sort->list,sort->pos-1)); if (history_reduction(move,board) == true) { ASSERT(value>=0&&value<16384); ASSERT(move!=trans_move); ASSERT(!move_is_tactical(move,board)); ASSERT(!move_is_check(move,board)); new_depth--; reduced = true; } } } */ // history pruning reduced = false; value = sort->value; // history score if (!in_check && depth < SearchCurrent->max_extensions / 2 && node_type != NodePV && new_depth < depth && value < HistoryValue / depth) continue; if (UseHistory && depth >= HistoryDepth && node_type != NodePV) { if (!in_check && played_nb >= HistoryMoveNb && new_depth < depth) { ASSERT(best_value!=ValueNone); ASSERT(played_nb>0); ASSERT(sort->pos>0&&move==LIST_MOVE(sort->list,sort->pos-1)); value = sort->value; // history score if (value < HistoryValue) { ASSERT(value>=0&&value<16384); ASSERT(move!=trans_move); ASSERT(!move_is_tactical(move,board)); ASSERT(!move_is_check(move,board)); new_depth--; reduced = true; if (UseExtendedHistory && value < HistoryValue / 2 && depth >= 8){ new_depth--; } } } } SearchStack[height].reduced = reduced; // recursive search move_do(board,move,undo); if (node_type != NodePV || best_value == ValueNone) { // first move value = -full_search(board,-beta,-alpha,new_depth,height+1,new_pv,NODE_OPP(node_type)); } else { // other moves value = -full_search(board,-alpha-1,-alpha,new_depth,height+1,new_pv,NodeCut); if (value > alpha) { // && value < beta value = -full_search(board,-beta,-alpha,new_depth,height+1,new_pv,NodePV); } } // history-pruning re-search if (HistoryReSearch && reduced && value > alpha /* was >= beta */) { ASSERT(node_type!=NodePV); //history_very_bad(move,board); SearchStack[height].reduced = false; new_depth++; ASSERT(new_depth==depth-1); value = -full_search(board,-beta,-alpha,new_depth,height+1,new_pv,NODE_OPP(node_type)); } move_undo(board,move,undo); played[played_nb++] = move; 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){ // history_success(move,board); goto cut; } } } if (node_type == NodeCut) node_type = NodeAll; } // ALL node if (best_value == ValueNone) { // no legal move if (in_check) { ASSERT(board_is_mate(board)); return VALUE_MATE(height); } else { ASSERT(board_is_stalemate(board)); return ValueDraw; } } cut: ASSERT(value_is_ok(best_value)); // move ordering if (best_move != MoveNone) { good_move(best_move,board,depth,height); if (best_value >= beta && !move_is_tactical(best_move,board)) { ASSERT(played_nb>0&&played[played_nb-1]==best_move); for (i = 0; i < played_nb-1; i++) { move = played[i]; ASSERT(move!=best_move); history_bad(move,board); } history_good(best_move,board); } } // transposition table if (UseTrans && depth >= TransDepth) { trans_move = best_move; trans_depth = depth; trans_min_value = (best_value > old_alpha) ? value_to_trans(best_value,height) : -ValueInf; trans_max_value = (best_value < beta) ? value_to_trans(best_value,height) : +ValueInf; trans_store(Trans,board->key,trans_move,trans_depth,trans_min_value,trans_max_value); } return best_value; }
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_search(board_t * board, int alpha, int beta, int depth, int height, mv_t pv[], int node_type, bool extended, int ThreadId) { bool in_check; bool single_reply; bool good_cap; int trans_move, trans_depth, trans_flags, trans_value; int old_alpha; int value, best_value; int move, best_move; int new_depth; int played_nb; int i; int opt_value; bool reduced, cap_extended; attack_t attack[1]; sort_t sort[1]; undo_t undo[1]; mv_t new_pv[HeightMax]; mv_t played[256]; int FutilityMargin; int probe_score, probe_depth; int newHistoryValue; int threshold; int reduction; int last_move; int quiet_move_count; 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(node_type==NodePV||node_type==NodeCut||node_type==NodeAll); ASSERT(board_is_legal(board)); // horizon? if (depth <= 0){ if (node_type == NodePV) SearchCurrent[ThreadId]->CheckDepth = 1 - SearchCurrent[ThreadId]->CheckNb - 1; else SearchCurrent[ThreadId]->CheckDepth = 1 - SearchCurrent[ThreadId]->CheckNb; return full_quiescence(board,alpha,beta,0,height,pv,ThreadId); } // 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 <= 5){ // probe_depth = (2 * SearchCurrent[ThreadId]->act_iteration) / 3; // if ((board->piece_nb <=4 || height <= probe_depth) // && (height >= probe_depth // || board->cap_sq != SquareNone || PIECE_IS_PAWN(board->moving_piece)) // ) { // // if (probe_bitbases(board, probe_score)){ // if (probe_score < 0 && board_is_mate(board)) return VALUE_MATE(height); // probe_score = value_from_trans(probe_score,height); // /* trans_move = MoveNone; // trans_depth = depth; // trans_flags = TransUnknown; // if (probe_score > alpha) trans_flags |= TransLower; // if (probe_score < beta) trans_flags |= TransUpper; // trans_store(Trans,board->key,trans_move,trans_depth,trans_flags,probe_score);*/ // 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 trans_move = MoveNone; if (UseTrans && depth >= TransDepth) { if (trans_retrieve(Trans,&found_entry,board->key,&trans_move,&trans_depth,&trans_flags,&trans_value)) { // trans_move is now updated /* if (found_entry->depth != trans_depth){ found_entry->depth = trans_depth; }*/ if (node_type != NodePV /*|| ThreadId > 0*/) { if (UseMateValues) { if (trans_depth < depth) { if (trans_value < -ValueEvalInf && TRANS_IS_UPPER(trans_flags)) { trans_depth = depth; trans_flags = TransUpper; } else if (trans_value > +ValueEvalInf && TRANS_IS_LOWER(trans_flags)) { trans_depth = depth; trans_flags = TransLower; } } if (trans_depth >= depth) { trans_value = value_from_trans(trans_value,height); if ((UseExact && TRANS_IS_EXACT(trans_flags)) || (TRANS_IS_LOWER(trans_flags) && trans_value >= beta) || (TRANS_IS_UPPER(trans_flags) && trans_value <= alpha)) { return trans_value; } /* if (TRANS_IS_EXACT(trans_flags)) return trans_value; if (TRANS_IS_LOWER(trans_flags)) { if (trans_value >= beta) return trans_value; if (trans_value > alpha) alpha = trans_value; } if (TRANS_IS_UPPER(trans_flags)) { if (trans_value <= alpha) return trans_value; if (trans_value < beta) beta = trans_value; } */ } } } } } // height limit if (height >= HeightMax-1) return eval(board, alpha, beta, ThreadId); // more init old_alpha = alpha; best_value = ValueNone; best_move = MoveNone; played_nb = 0; last_move = SearchCurrent[ThreadId]->last_move; attack_set(attack,board); in_check = ATTACK_IN_CHECK(attack); // null-move pruning if (UseNull && depth >= NullDepth && node_type != NodePV && !(trans_move != MoveNone && TRANS_IS_UPPER(trans_flags) && trans_depth >= depth - NullReduction - depth/4 && trans_value < beta)) { // if hash table tells us we are < beta, the null move search probably wastes time // bugfix 25-3-2013 (trans_move condition added) was LOS ~90%, + 10 elo. Probably not nearly that good but it's a bugfix...) if (!in_check && !value_is_mate(beta) && do_null(board) && (!UseNullEval || depth <= NullReduction+1 || eval(board,alpha, beta, ThreadId) >= beta)) { // null-move search // if (depth < 11) new_depth = depth - NullReduction - depth/4; // JD: from Stockfish, 10 elo better // else // new_depth = depth - NullReduction - 2; //new_depth = depth - R_adpt(board->piece_size[board->turn]+board->pawn_size[board->turn],depth,NullReduction) - 1; move_do_null(board,undo); SearchCurrent[ThreadId]->last_move = MoveNone; // refutation table value = -full_search(board,-beta,-beta+1,new_depth,height+1,new_pv,NODE_OPP(node_type),false,ThreadId); move_undo_null(board,undo); // pruning if (value >= beta) { if (value > +ValueEvalInf) value = +ValueEvalInf; // do not return unproven mates ASSERT(!value_is_mate(value)); // pv_cat(pv,new_pv,MoveNull); best_move = MoveNone; best_value = value; goto cut; } } } // Razoring: idea by Tord Romstad (Glaurung), fixed by Jerry Donald if (node_type != NodePV && !in_check && trans_move == MoveNone && depth <= 3){ threshold = beta - RazorMargin - (depth-1)*39; // Values from Protector (Raimund Heid) if (eval(board,threshold-1, threshold, ThreadId) < threshold){ value = full_quiescence(board,threshold-1,threshold,0,height,pv,ThreadId); if (value < threshold) // corrected - was < beta which is too risky at depth > 1 return value; } } // Internal Iterative Deepening if (UseIID && depth >= IIDDepth && node_type == NodePV && trans_move == MoveNone) { new_depth = MIN(depth - IIDReduction,depth/2); ASSERT(new_depth>0); value = full_search(board,alpha,beta,new_depth,height,new_pv,node_type,false,ThreadId); if (value <= alpha) value = full_search(board,-ValueInf,beta,new_depth,height,new_pv,node_type,false,ThreadId); trans_move = new_pv[0]; } // move generation sort_init(sort,board,attack,depth,height,trans_move,last_move,ThreadId); single_reply = false; if (in_check && LIST_SIZE(sort->list) == 1) single_reply = true; // HACK // move loop opt_value = +ValueInf; good_cap = true; quiet_move_count = 0; while ((move=sort_next(sort,ThreadId)) != MoveNone) { // extensions new_depth = full_new_depth(depth,move,board,single_reply,node_type==NodePV, height, extended, &cap_extended, ThreadId); // history pruning value = sort->value; // history score if (!in_check && depth <= 6 && node_type != NodePV && new_depth < depth && value < 2 * HistoryValue / (depth + depth % 2) /*2*/ && played_nb >= 1+depth && !move_is_dangerous(move,board)){ continue; } // futility pruning if (UseFutility && node_type != NodePV && depth <= 5) { if (!in_check && new_depth < depth&& !move_is_tactical(move,board) && !move_is_dangerous(move,board)) { ASSERT(!move_is_check(move,board)); // move count based pruning (added by Jerry Donald: ~+20 elo!) if (quiet_move_count >= MoveCountLimit[depth])continue; quiet_move_count++; // optimistic evaluation (Chris Formula) if (opt_value == +ValueInf) { if (depth>=2){ FutilityMargin = FutilityMargin2 + (depth % 2) * 100; } else{ FutilityMargin = FutilityMargin1; } opt_value = eval(board,alpha-FutilityMargin,alpha,ThreadId) + FutilityMargin; ASSERT(opt_value<+ValueInf); } value = opt_value; // pruning if (value <= alpha) { if (value > best_value) { best_value = value; PV_CLEAR(pv); } continue; } } } // Late Move Reductions // init reduced = false; reduction = 0; // lookup reduction if (UseHistory) { if (!in_check && new_depth < depth && played_nb >= HistoryMoveNb && depth >= HistoryDepth && !move_is_dangerous(move,board)) { if (good_cap && !move_is_tactical(move,board)){ good_cap = false; } if (!good_cap){ reduction = (node_type == NodePV ? quietPvMoveReduction[depth<64 ? depth: 63][played_nb<64? played_nb: 63]: quietMoveReduction[depth<64 ? depth: 63][played_nb<64? played_nb: 63]); // reduced bad captures less if (move_is_tactical(move,board)) reduction = reduction / 2; // bad captures // set reduction flag if (reduction > 0) reduced = true; } } } // recursive search move_do(board,move,undo); SearchCurrent[ThreadId]->last_move = move; if (node_type != NodePV || best_value == ValueNone) { // first move or non-pv value = -full_search(board,-beta,-alpha,new_depth-reduction,height+1,new_pv,NODE_OPP(node_type),cap_extended,ThreadId); // The move was reduced and fails high; so we research with full depth if (reduced && value >= beta){ value = -full_search(board,-beta,-alpha,new_depth,height+1,new_pv,NODE_OPP(node_type),cap_extended,ThreadId); } } else { // other moves (all PV children) value = -full_search(board,-alpha-1,-alpha,new_depth-reduction,height+1,new_pv,NodeCut,cap_extended,ThreadId); // In case of fail high: // If reduced then we try a research with node_type = NodePV if (value > alpha && reduced){ // && value < beta value = -full_search(board,-beta,-alpha,new_depth-reduction,height+1,new_pv,NodePV,cap_extended,ThreadId); // Still fails high! We research to full depth if (reduced && value >= beta){ value = -full_search(board,-beta,-alpha,new_depth,height+1,new_pv,NodePV,cap_extended,ThreadId); } // If not reduced we research as a PV node } else if (value > alpha){ value = -full_search(board,-beta,-alpha,new_depth,height+1,new_pv,NodePV,cap_extended,ThreadId); } } move_undo(board,move,undo); played[played_nb++] = move; 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; } } } if (node_type == NodeCut) node_type = NodeAll; } // ALL node if (best_value == ValueNone) { // no legal move if (in_check) { ASSERT(board_is_mate(board)); return VALUE_MATE(height); } else { ASSERT(board_is_stalemate(board)); return ValueDraw; } } cut: ASSERT(value_is_ok(best_value)); // move ordering if (best_move != MoveNone) { good_move(best_move,board,depth,height,ThreadId); if (best_value >= beta && !move_is_tactical(best_move,board)) { // check is 204b and d, e // refutation table ~5 elo if (last_move != MoveNull && last_move != MoveNone)refutation_update(best_move, last_move, board, ThreadId); ASSERT(played_nb>0&&played[played_nb-1]==best_move); for (i = 0; i < played_nb-1; i++) { move = played[i]; ASSERT(move!=best_move); history_bad(move,board,ThreadId); bad_move(move,board,depth,height,ThreadId); // added JD ~ 5 elo } history_good(best_move,board,ThreadId); } } // transposition table if (UseTrans && depth >= TransDepth) { trans_move = best_move; trans_depth = depth; 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); } return best_value; }
int sort_next_qs(sort_t * sort) { int move; int gen; ASSERT(sort!=NULL); while (true) { while (sort->pos < LIST_SIZE(sort->list)) { // next move move = LIST_MOVE(sort->list,sort->pos); sort->pos++; ASSERT(move!=MoveNone); // test if (false) { } else if (sort->test == TEST_LEGAL) { if (!pseudo_is_legal(move,sort->board)) continue; } else if (sort->test == TEST_CAPTURE_QS) { ASSERT(move_is_tactical(move,sort->board)); if (!capture_is_good(move,sort->board,false)) continue; if (!pseudo_is_legal(move,sort->board)) continue; } else if (sort->test == TEST_CHECK_QS) { ASSERT(!move_is_tactical(move,sort->board)); ASSERT(move_is_check(move,sort->board)); if (see_move(move,sort->board) < 0) continue; if (!pseudo_is_legal(move,sort->board)) continue; } else { ASSERT(false); return MoveNone; } ASSERT(pseudo_is_legal(move,sort->board)); return move; } // next stage gen = Code[sort->gen++]; if (false) { } else if (gen == GEN_EVASION_QS) { gen_pseudo_evasions(sort->list,sort->board,sort->attack); note_moves_simple(sort->list,sort->board); list_sort(sort->list); sort->test = TEST_LEGAL; } else if (gen == GEN_CAPTURE_QS) { gen_captures(sort->list,sort->board); note_mvv_lva(sort->list,sort->board); list_sort(sort->list); sort->test = TEST_CAPTURE_QS; } else if (gen == GEN_CHECK_QS) { gen_quiet_checks(sort->list,sort->board); sort->test = TEST_CHECK_QS; } else { ASSERT(gen==GEN_END); return MoveNone; } sort->pos = 0; } }
int sort_next(sort_t * sort, int ThreadId) { int move; int gen; ASSERT(sort!=NULL); while (true) { while (sort->pos < LIST_SIZE(sort->list)) { // next move move = LIST_MOVE(sort->list,sort->pos); sort->value = HistoryMax; // default score, HistoryMax instead of 16384 sort->pos++; ASSERT(move!=MoveNone); // test if (false) { } else if (sort->test == TEST_NONE) { // no-op } else if (sort->test == TEST_TRANS_KILLER) { if (!move_is_pseudo(move,sort->board)) continue; if (!pseudo_is_legal(move,sort->board)) continue; } else if (sort->test == TEST_GOOD_CAPTURE) { ASSERT(move_is_tactical(move,sort->board)); if (move == sort->trans_killer) continue; if (!capture_is_good(move,sort->board,sort->in_pv)) { LIST_ADD(sort->bad,move); continue; } if (!pseudo_is_legal(move,sort->board)) continue; } else if (sort->test == TEST_BAD_CAPTURE) { ASSERT(move_is_tactical(move,sort->board)); ASSERT(!capture_is_good(move,sort->board,sort->in_pv)); ASSERT(move!=sort->trans_killer); if (!pseudo_is_legal(move,sort->board)) continue; sort->value = HistoryBadCap; // WHM(31) } else if (sort->test == TEST_KILLER) { if (move == sort->trans_killer) continue; if (!quiet_is_pseudo(move,sort->board)) continue; if (!pseudo_is_legal(move,sort->board)) continue; ASSERT(!move_is_tactical(move,sort->board)); sort->value = HistoryKiller; // WHM(31) } else if (sort->test == TEST_QUIET) { ASSERT(!move_is_tactical(move,sort->board)); if (move == sort->trans_killer) continue; if (move == sort->killer_1) continue; if (move == sort->killer_2) continue; if (move == sort->killer_3) continue; if (move == sort->killer_4) continue; if (!pseudo_is_legal(move,sort->board)) continue; sort->value = history_prob(move,sort->board,ThreadId); } else { ASSERT(false); return MoveNone; } ASSERT(pseudo_is_legal(move,sort->board)); return move; } // next stage gen = Code[sort->gen++]; if (false) { } else if (gen == GEN_TRANS) { LIST_CLEAR(sort->list); if (sort->trans_killer != MoveNone) LIST_ADD(sort->list,sort->trans_killer); sort->test = TEST_TRANS_KILLER; } else if (gen == GEN_GOOD_CAPTURE) { gen_captures(sort->list,sort->board); note_mvv_lva(sort->list,sort->board); list_sort(sort->list); LIST_CLEAR(sort->bad); sort->test = TEST_GOOD_CAPTURE; } else if (gen == GEN_BAD_CAPTURE) { list_copy(sort->list,sort->bad); sort->test = TEST_BAD_CAPTURE; } else if (gen == GEN_KILLER) { LIST_CLEAR(sort->list); if (sort->killer_1 != MoveNone) LIST_ADD(sort->list,sort->killer_1); if (sort->killer_2 != MoveNone) LIST_ADD(sort->list,sort->killer_2); if (sort->killer_3 != MoveNone) LIST_ADD(sort->list,sort->killer_3); if (sort->killer_4 != MoveNone) LIST_ADD(sort->list,sort->killer_4); sort->test = TEST_KILLER; } else if (gen == GEN_QUIET) { gen_quiet_moves(sort->list,sort->board); note_quiet_moves(sort->list,sort->board,sort->in_pv,ThreadId); list_sort(sort->list); sort->test = TEST_QUIET; } else { ASSERT(gen==GEN_END); return MoveNone; } sort->pos = 0; } }