void move_undo_null(board_t * board, const undo_t * undo) { ASSERT(board!=NULL); ASSERT(undo!=NULL); ASSERT(board_is_legal(board)); ASSERT(!board_is_check(board)); // update board info board->turn = undo->turn; board->ep_square = undo->ep_square; board->ply_nb = undo->ply_nb; board->cap_sq = undo->cap_sq; board->key = undo->key; // update key stack ASSERT(board->sp>0); board->sp--; // debug ASSERT(board_is_ok(board)); }
void move_do_null(board_t * board, undo_t * undo) { int sq; ASSERT(board!=NULL); ASSERT(undo!=NULL); ASSERT(board_is_legal(board)); ASSERT(!board_is_check(board)); // initialise undo undo->turn = board->turn; undo->ep_square = board->ep_square; undo->ply_nb = board->ply_nb; undo->cap_sq = board->cap_sq; undo->moving_piece = board->moving_piece; undo->key = board->key; // update key stack ASSERT(board->sp<StackSize); board->stack[board->sp++] = board->key; // update turn board->turn = COLOUR_OPP(board->turn); board->key ^= RANDOM_64(RandomTurn); // update en-passant square sq = board->ep_square; if (sq != SquareNone) { board->key ^= RANDOM_64(RandomEnPassant+SQUARE_FILE(sq)-FileA); board->ep_square = SquareNone; } // update move number board->ply_nb = 0; // HACK: null move is considered as a conversion // update last square board->cap_sq = SquareNone; board->moving_piece = PieceNone256; // debug ASSERT(board_is_ok(board)); }
static bool simple_stalemate(const board_t * board) { int me, opp; int king; int opp_flag; int from, to; int capture; const inc_t * inc_ptr; int inc; ASSERT(board!=NULL); ASSERT(board_is_legal(board)); ASSERT(!board_is_check(board)); // lone king? me = board->turn; if (board->piece_size[me] != 1 || board->pawn_size[me] != 0) return false; // no // king in a corner? king = KING_POS(board,me); if (king != A1 && king != H1 && king != A8 && king != H8) return false; // no // init opp = COLOUR_OPP(me); opp_flag = COLOUR_FLAG(opp); // king can move? from = king; for (inc_ptr = KingInc; (inc=*inc_ptr) != IncNone; inc_ptr++) { to = from + inc; capture = board->square[to]; if (capture == Empty || FLAG_IS(capture,opp_flag)) { if (!is_attacked(board,to,opp)) return false; // legal king move } } // no legal move ASSERT(board_is_stalemate((board_t*)board)); return true; }
int search_full_root(list_t * list, board_t * board, int a, int b, int depth, int search_type, int ThreadId) { int value; ASSERT(list_is_ok(list)); ASSERT(board_is_ok(board)); ASSERT(depth_is_ok(depth)); ASSERT(search_type==SearchNormal||search_type==SearchShort); ASSERT(list==SearchRoot[ThreadId]->list); ASSERT(!LIST_IS_EMPTY(list)); ASSERT(board==SearchCurrent[ThreadId]->board); ASSERT(board_is_legal(board)); ASSERT(depth>=1); value = full_root(list,board,a,b,depth,0,search_type, ThreadId); ASSERT(value_is_ok(value)); ASSERT(LIST_VALUE(list,0)==value); return value; }
int search_full_root(list_t * list, board_t * board, int depth, int search_type) { int value, a, b; ASSERT(list_is_ok(list)); ASSERT(board_is_ok(board)); ASSERT(depth_is_ok(depth)); ASSERT(search_type==SearchNormal||search_type==SearchShort); ASSERT(list==SearchRoot->list); ASSERT(!LIST_IS_EMPTY(list)); ASSERT(board==SearchCurrent->board); ASSERT(board_is_legal(board)); ASSERT(depth>=1); if (SearchBest[SearchCurrent->multipv].value == 0){ a = -ValueInf; b = +ValueInf; } else{ a = SearchBest[SearchCurrent->multipv].value - 40; b = SearchBest[SearchCurrent->multipv].value + 40; } if (SearchInput->multipv > 0){ a = -ValueInf; b = +ValueInf; } value = full_root(list,board,a,b,depth,0,search_type); ASSERT(value_is_ok(value)); ASSERT(LIST_VALUE(list,0)==value); return value; }
void board_init_list(board_t * board) { int sq_64, sq, piece; int colour, pos; int i, size; int square; int order; int file; ASSERT(board!=NULL); // init for (sq = 0; sq < SquareNb; sq++) { board->pos[sq] = -1; } board->piece_nb = 0; for (piece = 0; piece < 12; piece++) board->number[piece] = 0; // piece lists for (colour = 0; colour < ColourNb; colour++) { // piece list pos = 0; for (sq_64 = 0; sq_64 < 64; sq_64++) { sq = SQUARE_FROM_64(sq_64); piece = board->square[sq]; if (piece != Empty && !piece_is_ok(piece)) my_fatal("board_init_list(): illegal position\n"); if (COLOUR_IS(piece,colour) && !PIECE_IS_PAWN(piece)) { if (pos >= 16) my_fatal("board_init_list(): illegal position\n"); ASSERT(pos>=0&&pos<16); board->pos[sq] = pos; board->piece[colour][pos] = sq; pos++; board->piece_nb++; board->number[PIECE_TO_12(piece)]++; } } if (board->number[COLOUR_IS_WHITE(colour)?WhiteKing12:BlackKing12] != 1) my_fatal("board_init_list(): illegal position\n"); ASSERT(pos>=1&&pos<=16); board->piece[colour][pos] = SquareNone; board->piece_size[colour] = pos; // MV sort size = board->piece_size[colour]; for (i = 1; i < size; i++) { square = board->piece[colour][i]; piece = board->square[square]; order = PIECE_ORDER(piece); for (pos = i; pos > 0 && order > PIECE_ORDER(board->square[(sq=board->piece[colour][pos-1])]); pos--) { ASSERT(pos>0&&pos<size); board->piece[colour][pos] = sq; ASSERT(board->pos[sq]==pos-1); board->pos[sq] = pos; } ASSERT(pos>=0&&pos<size); board->piece[colour][pos] = square; ASSERT(board->pos[square]==i); board->pos[square] = pos; } // debug if (DEBUG) { for (i = 0; i < board->piece_size[colour]; i++) { sq = board->piece[colour][i]; ASSERT(board->pos[sq]==i); if (i == 0) { // king ASSERT(PIECE_IS_KING(board->square[sq])); } else { ASSERT(!PIECE_IS_KING(board->square[sq])); ASSERT(PIECE_ORDER(board->square[board->piece[colour][i]])<=PIECE_ORDER(board->square[board->piece[colour][i-1]])); } } } // pawn list for (file = 0; file < FileNb; file++) { board->pawn_file[colour][file] = 0; } pos = 0; for (sq_64 = 0; sq_64 < 64; sq_64++) { sq = SQUARE_FROM_64(sq_64); piece = board->square[sq]; if (COLOUR_IS(piece,colour) && PIECE_IS_PAWN(piece)) { if (pos >= 8 || SQUARE_IS_PROMOTE(sq)) my_fatal("board_init_list(): illegal position\n"); ASSERT(pos>=0&&pos<8); board->pos[sq] = pos; board->pawn[colour][pos] = sq; pos++; board->piece_nb++; board->number[PIECE_TO_12(piece)]++; board->pawn_file[colour][SQUARE_FILE(sq)] |= BIT(PAWN_RANK(sq,colour)); } } ASSERT(pos>=0&&pos<=8); board->pawn[colour][pos] = SquareNone; board->pawn_size[colour] = pos; if (board->piece_size[colour] + board->pawn_size[colour] > 16) my_fatal("board_init_list(): illegal position\n"); } // last square board->cap_sq = SquareNone; // PST board->opening = board_opening(board); board->endgame = board_endgame(board); // hash key for (i = 0; i < board->ply_nb; i++) board->stack[i] = 0; // HACK board->sp = board->ply_nb; board->key = hash_key(board); board->pawn_key = hash_pawn_key(board); board->material_key = hash_material_key(board); // legality if (!board_is_legal(board)) my_fatal("board_init_list(): illegal position\n"); // debug ASSERT(board_is_ok(board)); }
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)); }
void move_undo(board_t * board, int move, const undo_t * undo) { int me; int from, to; int piece, pos; int rook; ASSERT(board!=NULL); ASSERT(move_is_ok(move)); ASSERT(undo!=NULL); // init me = undo->turn; from = MOVE_FROM(move); to = MOVE_TO(move); piece = board->square[to]; ASSERT(COLOUR_IS(piece,me)); // castle if (MOVE_IS_CASTLE(move)) { rook = Rook64 | COLOUR_FLAG(me); // HACK if (to == G1) { square_move(board,F1,H1,rook,false); } else if (to == C1) { square_move(board,D1,A1,rook,false); } else if (to == G8) { square_move(board,F8,H8,rook,false); } else if (to == C8) { square_move(board,D8,A8,rook,false); } else { ASSERT(false); } } // move the piece backward if (MOVE_IS_PROMOTE(move)) { // promote ASSERT(piece==move_promote(move)); square_clear(board,to,piece,false); piece = PAWN_MAKE(me); pos = undo->pawn_pos; square_set(board,from,piece,pos,false); } else { // normal move square_move(board,to,from,piece,false); } // put the captured piece back if (undo->capture) { square_set(board,undo->capture_square,undo->capture_piece,undo->capture_pos,false); } // update board info board->turn = undo->turn; board->flags = undo->flags; board->ep_square = undo->ep_square; board->ply_nb = undo->ply_nb; board->cap_sq = undo->cap_sq; board->opening = undo->opening; board->endgame = undo->endgame; board->key = undo->key; board->pawn_key = undo->pawn_key; board->material_key = undo->material_key; // update key stack ASSERT(board->sp>0); board->sp--; // debug ASSERT(board_is_ok(board)); ASSERT(board_is_legal(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_no_null(board_t * board, int alpha, int beta, int depth, int height, mv_t pv[], int node_type, int trans_move, int * best_move) { int value, best_value; int move; int new_depth; attack_t attack[1]; sort_t sort[1]; undo_t undo[1]; mv_t new_pv[HeightMax]; 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(trans_move==MoveNone||move_is_ok(trans_move)); ASSERT(best_move!=NULL); ASSERT(board_is_legal(board)); ASSERT(!board_is_check(board)); ASSERT(depth>=1); // 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(); } attack_set(attack,board); ASSERT(!ATTACK_IN_CHECK(attack)); *best_move = MoveNone; best_value = ValueNone; // move loop sort_init(sort,board,attack,depth,height,trans_move); while ((move=sort_next(sort)) != MoveNone) { SearchStack[height].move = move; new_depth = full_new_depth(depth,move,board,false,false,false,height); move_do(board,move,undo); value = -full_search(board,-beta,-alpha,new_depth,height+1,new_pv,NODE_OPP(node_type)); 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 => stalemate ASSERT(board_is_stalemate(board)); best_value = ValueDraw; } 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 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_root(list_t * list, board_t * board, int alpha, int beta, int depth, int height, int search_type) { int old_alpha; int value, best_value[MultiPVMax]; int i, move, j; int new_depth; undo_t undo[1]; mv_t new_pv[HeightMax]; bool found; ASSERT(list_is_ok(list)); ASSERT(board_is_ok(board)); ASSERT(range_is_ok(alpha,beta)); ASSERT(depth_is_ok(depth)); ASSERT(height_is_ok(height)); ASSERT(search_type==SearchNormal||search_type==SearchShort); ASSERT(list==SearchRoot->list); ASSERT(!LIST_IS_EMPTY(list)); ASSERT(board==SearchCurrent->board); ASSERT(board_is_legal(board)); ASSERT(depth>=1); // 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--; if (SearchCurrent->multipv == 0) for (i = 0; i < LIST_SIZE(list); i++) list->value[i] = ValueNone; old_alpha = alpha; best_value[SearchCurrent->multipv] = ValueNone; // move loop for (i = 0; i < LIST_SIZE(list); i++) { move = LIST_MOVE(list,i); if (SearchCurrent->multipv > 0){ found = false; for (j = 0; j < SearchCurrent->multipv; j++){ if (SearchBest[j].pv[0] == move){ found = true; break; } } if (found == true) continue; } SearchStack[height].move = move; SearchRoot->depth = depth; SearchRoot->move = move; SearchRoot->move_pos = i; SearchRoot->move_nb = LIST_SIZE(list); search_update_root(); new_depth = full_new_depth(depth,move,board,board_is_check(board)&&LIST_SIZE(list)==1,false,true, height); move_do(board,move,undo); if (search_type == SearchShort || best_value[SearchCurrent->multipv] == ValueNone) { // first move value = -full_search(board,-beta,-alpha,new_depth,height+1,new_pv,NodePV); if (value <= alpha){ // research old_alpha = -ValueInf; value = -full_search(board,-beta,ValueInf,new_depth,height+1,new_pv,NodePV); } else if (value >= beta){ // research value = -full_search(board,-ValueInf,-alpha,new_depth,height+1,new_pv,NodePV); } } else { // other moves value = -full_search(board,-alpha-1,-alpha,new_depth,height+1,new_pv,NodeCut); if (value > alpha) { // && value < beta SearchRoot->change = true; SearchRoot->easy = false; SearchRoot->flag = false; search_update_root(); value = -full_search(board,-beta,-alpha,new_depth,height+1,new_pv,NodePV); } } move_undo(board,move,undo); if (value <= alpha) { // upper bound list->value[i] = old_alpha; } else if (value >= beta) { // lower bound list->value[i] = beta; } else { // alpha < value < beta => exact value list->value[i] = value; } if (value > best_value[SearchCurrent->multipv] && (best_value[SearchCurrent->multipv] == ValueNone || value > alpha)) { SearchBest[SearchCurrent->multipv].move = move; SearchStack[height].best_move = move; SearchBest[SearchCurrent->multipv].value = value; if (value <= alpha) { // upper bound SearchBest[SearchCurrent->multipv].flags = SearchUpper; } else if (value >= beta) { // lower bound SearchBest[SearchCurrent->multipv].flags = SearchLower; } else { // alpha < value < beta => exact value SearchBest[SearchCurrent->multipv].flags = SearchExact; } SearchBest[SearchCurrent->multipv].depth = depth; pv_cat(SearchBest[SearchCurrent->multipv].pv,new_pv,move); search_update_best(); } if (value > best_value[SearchCurrent->multipv]) { best_value[SearchCurrent->multipv] = value; if (value > alpha) { if (search_type == SearchNormal) alpha = value; if (value >= beta) break; } } } ASSERT(value_is_ok(best_value)); list_sort(list); ASSERT(SearchBest->move==LIST_MOVE(list,0)); ASSERT(SearchBest->value==best_value); if (UseTrans && best_value[SearchCurrent->multipv] > old_alpha && best_value[SearchCurrent->multipv] < beta) { pv_fill(SearchBest[SearchCurrent->multipv].pv,board); } return best_value[SearchCurrent->multipv]; }
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; }
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; }