void MoveToStr(int move, char *move_str) { static const char prom_char[5] = "nbrq"; // Move coordinates move_str[0] = File(Fsq(move)) + 'a'; move_str[1] = Rank(Fsq(move)) + '1'; move_str[2] = File(Tsq(move)) + 'a'; move_str[3] = Rank(Tsq(move)) + '1'; move_str[4] = '\0'; // Bugfix by Dave Kaye for compatibility with Knights GUI (Linux) and UCI specs // (needed if a GUI forces the engine to analyse in checkmate/stalemate position) if (strcmp(move_str, "a1a1") == 0) { strcpy(move_str, "0000"); } // Add promoted piece, if any if (IsProm(move)) { move_str[4] = prom_char[(move >> 12) & 3]; move_str[5] = '\0'; } }
void DecreaseHistory(POS *p, int move, int depth) { // Increment history counter history[p->pc[Fsq(move)]][Tsq(move)] -= depth * depth; // Prevent history counters from growing too high if (history[p->pc[Fsq(move)]][Tsq(move)] < -HIST_LIMIT) TrimHistory(); }
int MvvLva(POS *p, int move) { // Captures if (p->pc[Tsq(move)] != NO_PC) return TpOnSq(p, Tsq(move)) * 6 + 5 - TpOnSq(p, Fsq(move)); // Non-capturing promotions if (IsProm(move)) return PromType(move) - 5; return 5; }
int NextCaptureOrCheck(MOVES * m) // used in QuiesceCheck() { int move; switch (m->phase) { case 0: while (m->next < m->last) { move = SelectBest(m); if (BadCapture(m->p, move)) continue; return move; } m->phase = 1; case 1: m->last = GenerateQuietChecks(m->p, m->move); ScoreQuiet(m); m->phase = 2; case 2: while (m->next < m->last) { move = SelectBest(m); if (Swap(m->p, Fsq(move), Tsq(move)) < 0) continue; return move; } } return 0; }
void ScoreQuiet(MOVES *m) { int *movep, *valuep; int move_score; valuep = m->value; for (movep = m->move; movep < m->last; movep++) { move_score = history[m->p->pc[Fsq(*movep)]][Tsq(*movep)]; if (TpOnSq(m->p,Fsq(*movep)) != K) move_score += Param.mg_pst[m->p->side][TpOnSq(m->p,Fsq(*movep))][Tsq(*movep)] - Param.mg_pst[m->p->side][TpOnSq(m->p, Fsq(*movep))][Fsq(*movep)]; //if (Fsq(*movep) == m->ref_sq && m->ref_sq != -1) move_score += 2048; *valuep++ = move_score; } }
void UpdateHistory(POS *p, int last_move, int move, int depth, int ply) { // Don't update stuff used for move ordering if a move changes material balance if (p->pc[Tsq(move)] != NO_PC || IsProm(move) || MoveType(move) == EP_CAP) return; // Asserts suggested by Ferdinand Mosca because history[] would overflow // if p->pc[Fsq(move)] == NO_PC. If they ever fire, either move or board // data are corrupt. assert(p->pc[Fsq(move)] != NO_PC); // To detect no_pc assert(p->pc[Fsq(move)] <= NO_PC); // To detect beyond no_pc for deeper examination // Increment history counter history[p->pc[Fsq(move)]][Tsq(move)] += 2 * depth * depth; // Prevent history counters from growing too high if (history[p->pc[Fsq(move)]][Tsq(move)] > HIST_LIMIT) TrimHistory(); // Update refutation table, saving new move in the table indexed // by the coordinates of last move. last_move == 0 is a null move, // special case of last_move == -1 denotes situations when updating // refutation table is switched off: at root or in QuiesceFlee() if (last_move >= 0) refutation[Fsq(last_move)][Tsq(last_move)] = move; // Update killer moves, taking care that they are different if (move != killer[ply][0]) { killer[ply][1] = killer[ply][0]; killer[ply][0] = move; } }
int BadCapture(POS *p, int move) { int fsq = Fsq(move); int tsq = Tsq(move); // Captures that gain material or capture equal piece are good by definition if (tp_value[TpOnSq(p, tsq)] >= tp_value[TpOnSq(p, fsq)]) return 0; // Bishop takes knight and knight takes bishop are good irrespectively from // the way minor pieces' values are tuned if ((TpOnSq(p, fsq) == B) && (TpOnSq(p, tsq) == N)) return 0; if ((TpOnSq(p, fsq) == N) && (TpOnSq(p, tsq) == B)) return 0; // En passant captures are good by definition if (MoveType(move) == EP_CAP) return 0; // We have to evaluate this capture using expensive Static Exchange Evaluation return Swap(p, fsq, tsq) < 0; }
void sHistory::OnMoveReduced(int move) { cutoff [Fsq(move)] [Tsq(move)] = 51; }
void POS::UndoMove(int move, UNDO *u) { int sd = Opp(side); int op = side; int fsq = Fsq(move); int tsq = Tsq(move); int ftp = Tp(pc[tsq]); // moving piece int ttp = u->ttp; castle_flags = u->castle_flags; ep_sq = u->ep_sq; rev_moves = u->rev_moves; pawn_key = u->pawn_key; hash_key = u->hash_key; head--; pc[fsq] = Pc(sd, ftp); pc[tsq] = NO_PC; cl_bb[sd] ^= SqBb(fsq) | SqBb(tsq); tp_bb[ftp] ^= SqBb(fsq) | SqBb(tsq); #ifndef LEAF_PST mg_pst[sd] += Param.mg_pst_data[sd][ftp][fsq] - Param.mg_pst_data[sd][ftp][tsq]; eg_pst[sd] += Param.eg_pst_data[sd][ftp][fsq] - Param.eg_pst_data[sd][ftp][tsq]; #endif // Update king location if (ftp == K) king_sq[sd] = fsq; // Undo capture if (ttp != NO_TP) { pc[tsq] = Pc(op, ttp); cl_bb[op] ^= SqBb(tsq); tp_bb[ttp] ^= SqBb(tsq); phase += phase_value[ttp]; #ifndef LEAF_PST mg_pst[op] += Param.mg_pst_data[op][ttp][tsq]; eg_pst[op] += Param.eg_pst_data[op][ttp][tsq]; #endif cnt[op][ttp]++; } switch (MoveType(move)) { case NORMAL: break; case CASTLE: // define complementary rook move switch (tsq) { case C1: { fsq = A1; tsq = D1; break; } case G1: { fsq = H1; tsq = F1; break; } case C8: { fsq = A8; tsq = D8; break; } case G8: { fsq = H8; tsq = F8; break; } } pc[tsq] = NO_PC; pc[fsq] = Pc(sd, R); cl_bb[sd] ^= SqBb(fsq) | SqBb(tsq); tp_bb[R] ^= SqBb(fsq) | SqBb(tsq); #ifndef LEAF_PST mg_pst[sd] += Param.mg_pst_data[sd][R][fsq] - Param.mg_pst_data[sd][R][tsq]; eg_pst[sd] += Param.eg_pst_data[sd][R][fsq] - Param.eg_pst_data[sd][R][tsq]; #endif break; case EP_CAP: tsq ^= 8; pc[tsq] = Pc(op, P); cl_bb[op] ^= SqBb(tsq); tp_bb[P] ^= SqBb(tsq); phase += phase_value[P]; #ifndef LEAF_PST mg_pst[op] += Param.mg_pst_data[op][P][tsq]; eg_pst[op] += Param.eg_pst_data[op][P][tsq]; #endif cnt[op][P]++; break; case EP_SET: break; case N_PROM: case B_PROM: case R_PROM: case Q_PROM: pc[fsq] = Pc(sd, P); tp_bb[P] ^= SqBb(fsq); tp_bb[ftp] ^= SqBb(fsq); phase += phase_value[P] - phase_value[ftp]; #ifndef LEAF_PST mg_pst[sd] += Param.mg_pst_data[sd][P][fsq] - Param.mg_pst_data[sd][ftp][fsq]; eg_pst[sd] += Param.eg_pst_data[sd][P][fsq] - Param.eg_pst_data[sd][ftp][fsq]; #endif cnt[sd][P]++; cnt[sd][ftp]--; break; } side ^= 1; }
int Search(POS *p, int ply, int alpha, int beta, int depth, int was_null, int last_move, int last_capt_sq, int *pv) { int best, score, null_score, move, new_depth, new_pv[MAX_PLY]; int fl_check, fl_prunable_node, fl_prunable_move, mv_type, reduction; int is_pv = (beta > alpha + 1); int mv_tried = 0, quiet_tried = 0, fl_futility = 0; int mv_played[MAX_MOVES]; int mv_hist_score; int victim, last_capt; MOVES m[1]; UNDO u[1]; assert(ply > 0); // Quiescence search entry point if (depth <= 0) return QuiesceChecks(p, ply, alpha, beta, pv); // Periodically check for timeout, ponderhit or stop command nodes++; CheckTimeout(); // Quick exit on a timeout or on a statically detected draw if (abort_search) return 0; if (ply) *pv = 0; if (IsDraw(p)) return DrawScore(p); // Mate distance pruning int checkmatingScore = MATE - ply; if (checkmatingScore < beta) { beta = checkmatingScore; if (alpha >= checkmatingScore) return alpha; } int checkmatedScore = -MATE + ply; if (checkmatedScore > alpha) { alpha = checkmatedScore; if (beta <= checkmatedScore) return beta; } // Retrieving data from transposition table. We hope for a cutoff // or at least for a move to improve move ordering. move = 0; if (TransRetrieve(p->hash_key, &move, &score, alpha, beta, depth, ply)) { // For move ordering purposes, a cutoff from hash is treated // exactly like a cutoff from search if (score >= beta) UpdateHistory(p, last_move, move, depth, ply); // In pv nodes only exact scores are returned. This is done because // there is much more pruning and reductions in zero-window nodes, // so retrieving such scores in pv nodes works like retrieving scores // from slightly lower depth. if (!is_pv || (score > alpha && score < beta)) return score; } // Safeguard against exceeding ply limit if (ply >= MAX_PLY - 1) return Eval.Return(p, 1); // Are we in check? Knowing that is useful when it comes // to pruning/reduction decisions fl_check = InCheck(p); // INTERNAL ITERATIVE DEEPENING - we try to get a hash move to improve move ordering if (!move && is_pv && depth >= 6 && !fl_check) { Search(p, ply, alpha, beta, depth - 2, 0, 0, -1, new_pv); if (abort_search) return 0; TransRetrieve(p->hash_key, &move, &score, alpha, beta, depth, ply); } // Can we prune this node? fl_prunable_node = !fl_check && !is_pv && alpha > -MAX_EVAL && beta < MAX_EVAL; // Beta pruning / static null move if (use_beta_pruning && fl_prunable_node && depth <= 3 && !was_null) { int sc = Eval.Return(p, 1) - 120 * depth; // TODO: Tune me! if (sc > beta) return sc; } // Null move if (use_nullmove && fl_prunable_node && depth > 1 && !was_null && MayNull(p) ) { int eval = Eval.Return(p, 1); if (eval > beta) { new_depth = depth - ((823 + 67 * depth) / 256); // simplified Stockfish formula // omit null move search if normal search to the same depth wouldn't exceed beta // (sometimes we can check it for free via hash table) if (TransRetrieve(p->hash_key, &move, &null_score, alpha, beta, new_depth, ply)) { if (null_score < beta) goto avoid_null; } p->DoNull(u); if (new_depth > 0) score = -Search(p, ply + 1, -beta, -beta + 1, new_depth, 1, 0, -1, new_pv); else score = -QuiesceChecks(p, ply + 1, -beta, -beta + 1, new_pv); p->UndoNull(u); // Verification search (nb. immediate null move within it is prohibited) if (new_depth > 6 && score >= beta && use_null_verification) score = Search(p, ply, alpha, beta, new_depth - 5, 1, move, -1, new_pv); if (abort_search ) return 0; if (score >= beta) return score; } } avoid_null: // end of null move code // Razoring based on Toga II 3.0 if (use_razoring && fl_prunable_node && !move && !was_null && !(p->Pawns(p->side) & bbRelRank[p->side][RANK_7]) // no pawns to promote in one move && depth <= 3) { int threshold = beta - razor_margin[depth]; int eval = Eval.Return(p, 1); if (eval < threshold) { score = QuiesceChecks(p, ply, alpha, beta, pv); if (score < threshold) return score; } } // end of razoring code // Init moves and variables before entering main loop best = -INF; InitMoves(p, m, move, Refutation(last_move), ply); // Main loop while ((move = NextMove(m, &mv_type))) { // Gather data about the move mv_hist_score = history[p->pc[Fsq(move)]][Tsq(move)]; victim = TpOnSq(p, Tsq(move)); if (victim != NO_TP) last_capt = Tsq(move); else last_capt = -1; // Set futility pruning flag before the first applicable move is tried if (mv_type == MV_NORMAL && quiet_tried == 0) { if (use_futility && fl_prunable_node && depth <= 6) { if (Eval.Return(p, 1) + fut_margin[depth] < beta) fl_futility = 1; } } p->DoMove(move, u); if (Illegal(p)) { p->UndoMove(move, u); continue; } // Update move statistics // (needed for reduction/pruning decisions and for updating history score) mv_played[mv_tried] = move; mv_tried++; if (mv_type == MV_NORMAL) quiet_tried++; // Can we prune this move? fl_prunable_move = !InCheck(p) && (mv_type == MV_NORMAL) && (mv_hist_score < hist_limit); // Set new search depth new_depth = depth - 1; // Check extension (pv node or low depth) if (is_pv || depth < 9) { new_depth += InCheck(p); if (is_pv && Tsq(move) == last_capt_sq) new_depth += 1; } // Futility pruning if (fl_futility && fl_prunable_move && mv_tried > 1) { p->UndoMove(move, u); continue; } // Late move pruning if (use_lmp && fl_prunable_node && fl_prunable_move && quiet_tried > lmp_limit[depth] && depth <= 3 && MoveType(move) != CASTLE ) { p->UndoMove(move, u); continue; } // Late move reduction reduction = 0; if (use_lmr && depth >= 2 && mv_tried > 3 && alpha > -MAX_EVAL && beta < MAX_EVAL && !fl_check && fl_prunable_move && lmr_size[is_pv][depth][mv_tried] > 0 && MoveType(move) != CASTLE ) { // read reduction size from the table reduction = lmr_size[is_pv][depth][mv_tried]; // increase reduction on bad history score if (mv_hist_score < 0 && new_depth - reduction > 2 && lmr_hist_adjustement) reduction++; // reduce search depth new_depth -= reduction; } // a place to come back if reduction looks suspect re_search: // PVS if (best == -INF) score = -Search(p, ply + 1, -beta, -alpha, new_depth, 0, move, last_capt, new_pv); else { score = -Search(p, ply + 1, -alpha - 1, -alpha, new_depth, 0, move, last_capt, new_pv); if (!abort_search && score > alpha && score < beta) score = -Search(p, ply + 1, -beta, -alpha, new_depth, 0, move, last_capt, new_pv); } // Reduced move scored above alpha - we need to re-search it if (reduction && score > alpha) { new_depth += reduction; reduction = 0; goto re_search; } // Undo move p->UndoMove(move, u); if (abort_search) return 0; // Beta cutoff if (score >= beta) { if (!fl_check) { UpdateHistory(p, last_move, move, depth, ply); for (int mv = 0; mv < mv_tried; mv++) DecreaseHistory(p, mv_played[mv], depth); } TransStore(p->hash_key, move, score, LOWER, depth, ply); return score; } // Updating score and alpha if (score > best) { best = score; if (score > alpha) { alpha = score; BuildPv(pv, new_pv, move); } } } // end of the main loop // Return correct checkmate/stalemate score if (best == -INF) return InCheck(p) ? -MATE + ply : DrawScore(p); // Save score in the transposition table if (*pv) { if (!fl_check) { UpdateHistory(p, last_move, *pv, depth, ply); for (int mv = 0; mv < mv_tried; mv++) DecreaseHistory(p, mv_played[mv], depth); } TransStore(p->hash_key, *pv, best, EXACT, depth, ply); } else TransStore(p->hash_key, 0, best, UPPER, depth, ply); return best; }
void sHistory::UpdateCutoff(int move) { cutoff [Fsq(move)] [Tsq(move)] += 8; // NOTE: 8 and 9 work equally well in self-play, 7 and 10 untested }
int sHistory::MoveChangesMaterialBalance(sPosition *p, int move) { if (p->pc[Tsq(move)] != NO_PC || IsProm(move) || MoveType(move) == EP_CAP) return 1; return 0; }
int sHistory::Refutes(int lastMove, int move) { return (refutation [Fsq(lastMove)] [Tsq(lastMove)] == move); }
int Quiesce(POS *p, int ply, int alpha, int beta, int *pv) { int best, score, move, new_pv[MAX_PLY]; MOVES m[1]; UNDO u[1]; int op = Opp(p->side); // Statistics and attempt at quick exit if (InCheck(p)) return QuiesceFlee(p, ply, alpha, beta, pv); nodes++; CheckTimeout(); if (abort_search) return 0; *pv = 0; if (IsDraw(p)) return DrawScore(p); if (ply >= MAX_PLY - 1) return Eval.Return(p, 1); // Get a stand-pat score and adjust bounds // (exiting if eval exceeds beta) best = Eval.Return(p, 1); if (best >= beta) return best; if (best > alpha) alpha = best; #ifdef USE_QS_HASH // Transposition table read if (TransRetrieve(p->hash_key, &move, &score, alpha, beta, 0, ply)) return score; #endif InitCaptures(p, m); // Main loop while ((move = NextCapture(m))) { // Pruning in quiescence search // (not applicable if we are capturing last enemy piece) if (p->cnt[op][N] + p->cnt[op][B] + p->cnt[op][R] + p->cnt[op][Q] > 1) { // 1. Delta pruning if (best + tp_value[TpOnSq(p, Tsq(move))] + 300 < alpha) continue; // 2. SEE-based pruning of bad captures if (BadCapture(p, move)) continue; } p->DoMove(move, u); if (Illegal(p)) { p->UndoMove(move, u); continue; } score = -Quiesce(p, ply + 1, -beta, -alpha, new_pv); p->UndoMove(move, u); if (abort_search) return 0; // Beta cutoff if (score >= beta) { #ifdef USE_QS_HASH TransStore(p->hash_key, *pv, best, LOWER, 0, ply); #endif return score; } // Adjust alpha and score if (score > best) { best = score; if (score > alpha) { alpha = score; BuildPv(pv, new_pv, move); } } } #ifdef USE_QS_HASH if (*pv) TransStore(p->hash_key, *pv, best, EXACT, 0, ply); else TransStore(p->hash_key, 0, best, UPPER, 0, ply); #endif return best; }
int sBook::GetPolyglotMove(sPosition *p, int printOutput) { int bestMove = 0; int bestScore = 0; int maxWeight = 0; int sumOfWeights = 0; int pos; polyglot_move entry[1]; int move; int score; int values[100]; U64 key = GetPolyglotKey(p); char moveString[6]; nOfChoices = 0; if (bookFile != NULL && bookSize != 0) { srand(Timer.GetMS() ); for (pos = FindPos(key); pos < bookSize; pos++) { ReadEntry(entry,pos); if (entry->key != key) break; move = entry->move; score = entry->weight; // ugly hack to convert polyglot move to a real one int fsq = Tsq(move); int tsq = Fsq(move); // correction for castling moves if (fsq == E1 && tsq == H1 && p->kingSquare[WHITE] == E1) tsq = G1; if (fsq == E8 && tsq == H8 && p->kingSquare[BLACK] == E8) tsq = G8; if (fsq == E1 && tsq == A1 && p->kingSquare[WHITE] == E1) tsq = C1; if (fsq == E8 && tsq == A8 && p->kingSquare[BLACK] == E8) tsq = C8; // now we want to get a move with full data, not only from and to squares int realMove = (tsq << 6) | fsq; MoveToStr(realMove, moveString); realMove = StrToMove(p, moveString); if (maxWeight < score) maxWeight = score; sumOfWeights += score; moves[nOfChoices] = realMove; values[nOfChoices] = score; nOfChoices++; } // pick a move, filtering out those with significantly lower weight for (int i = 0; i<nOfChoices; i++) { // report about possible choices and rejected moves if (values[i] > 1 || maxWeight == 1) { if (printOutput) { printf("info string "); PrintMove(moves[i]); printf(" %d %%", (values[i] * 100) / sumOfWeights ); if (IsInfrequent(values[i], maxWeight)) printf(" infrequent "); } } // shall we pick this move? if (!IsInfrequent(values[i], maxWeight)) { bestScore += values[i]; if (my_random(bestScore) < values[i]) bestMove = moves[i]; } printf("\n"); } } //if (printOutput) PrintMissingMoves(p); return bestMove; }
void sHistory::OnMoveTried(int move) { cutoff [Fsq(move)] [Tsq(move)] -= 1; }
int sHistory::MoveIsBad(int move) { return (cutoff [Fsq(move)] [Tsq(move)] < Data.lmrHistLimit); }
int NextMove(MOVES *m, int *flag) { int move; switch (m->phase) { case 0: // return transposition table move, if legal move = m->trans_move; if (move && Legal(m->p, move)) { m->phase = 1; *flag = MV_HASH; return move; } case 1: // helper phase: generate captures m->last = GenerateCaptures(m->p, m->move); ScoreCaptures(m); m->next = m->move; m->badp = m->bad; m->phase = 2; case 2: // return good captures, save bad ones on the separate list while (m->next < m->last) { move = SelectBest(m); if (move == m->trans_move) continue; if (BadCapture(m->p, move)) { *m->badp++ = move; continue; } *flag = MV_CAPTURE; return move; } case 3: // first killer move move = m->killer1; if (move && move != m->trans_move && m->p->pc[Tsq(move)] == NO_PC && Legal(m->p, move)) { m->phase = 4; *flag = MV_KILLER; return move; } case 4: // second killer move move = m->killer2; if (move && move != m->trans_move && m->p->pc[Tsq(move)] == NO_PC && Legal(m->p, move)) { m->phase = 5; *flag = MV_KILLER; return move; } case 5: // refutation move move = m->ref_move; if (move && move != m->trans_move && m->p->pc[Tsq(move)] == NO_PC && move != m->killer1 && move != m->killer2 && Legal(m->p, move)) { m->phase = 6; *flag = MV_NORMAL; return move; } case 6: // helper phase: generate quiet moves m->last = GenerateQuiet(m->p, m->move); ScoreQuiet(m); m->next = m->move; m->phase = 7; case 7: // return quiet moves while (m->next < m->last) { move = SelectBest(m); if (move == m->trans_move || move == m->killer1 || move == m->killer2 || move == m->ref_move) continue; *flag = MV_NORMAL; return move; } m->next = m->bad; m->phase = 8; case 8: // return bad captures if (m->next < m->badp) { *flag = MV_BADCAPT; return *m->next++; } } return 0; }
int sHistory::GetRefutation(int lastMove) { return refutation [Fsq(lastMove)] [Tsq(lastMove)]; }
int Refutation(int move) { return refutation[Fsq(move)][Tsq(move)]; }
void sHistory::UpdateHistory(sPosition *p, int move, int depth) { history[p->pc[Fsq(move)]][Tsq(move)] += depth * depth; }
void sManipulator::DoMove(sPosition *p, int move, UNDO *u) { int side = p->side; // moving side int fsq = Fsq(move); // start square int tsq = Tsq(move); // target square int ftp = TpOnSq(p, fsq); // moving piece int ttp = TpOnSq(p, tsq); // captured piece U64 bbMove = SqBb(fsq) | SqBb(tsq); // optimization from Stockfish // save data for undoing a move u->ttp = ttp; u->castleFlags = p->castleFlags; u->epSquare = p->epSquare; u->reversibleMoves = p->reversibleMoves; u->hashKey = p->hashKey; u->pawnKey = p->pawnKey; p->repetitionList[p->head++] = p->hashKey; // update reversible move counter (zeroing is done on captures and pawn moves) p->reversibleMoves++; p->hashKey ^= zobCastle[p->castleFlags]; p->castleFlags &= castleMask[fsq] & castleMask[tsq]; p->hashKey ^= zobCastle[p->castleFlags]; // clear en passant square if (p->epSquare != NO_SQ) { p->hashKey ^= zobEp[File(p->epSquare)]; p->epSquare = NO_SQ; } // move a piece from start square p->pc[fsq] = NO_PC; p->pc[tsq] = Pc(side, ftp); p->hashKey ^= zobPiece[Pc(side, ftp)][fsq] ^ zobPiece[Pc(side, ftp)][tsq]; if (ftp == P) { p->reversibleMoves = 0; p->pawnKey ^= zobPiece[Pc(side, ftp)][fsq] ^ zobPiece[Pc(side, ftp)][tsq]; } p->bbCl[side] ^= bbMove; p->bbTp[ftp] ^= bbMove; p->pstMg[side] += Data.pstMg[side][ftp][tsq] - Data.pstMg[side][ftp][fsq]; p->pstEg[side] += Data.pstEg[side][ftp][tsq] - Data.pstEg[side][ftp][fsq]; // on a king move update king location data if (ftp == K) p->kingSquare[side] = tsq; // capture if (ttp != NO_TP) { p->reversibleMoves = 0; p->hashKey ^= zobPiece[Pc(Opp(side), ttp)][tsq]; if (ttp == P) p->pawnKey ^= zobPiece[Pc(Opp(side), ttp)][tsq]; p->bbCl[Opp(side)] ^= SqBb(tsq); p->bbTp[ttp] ^= SqBb(tsq); p->pcCount[Opp(side)][ttp]--; p->pieceMat[Opp(side)] -= Data.matValue[ttp]; p->phase -= Data.phaseValue[ttp]; p->pstMg[Opp(side)] -= Data.pstMg[Opp(side)][ttp][tsq]; p->pstEg[Opp(side)] -= Data.pstEg[Opp(side)][ttp][tsq]; } switch (MoveType(move)) { case NORMAL: break; case CASTLE: if (tsq > fsq) { fsq += 3; tsq -= 1; } else { fsq -= 4; tsq += 1; } p->pc[fsq] = NO_PC; p->pc[tsq] = Pc(side, R); p->hashKey ^= zobPiece[Pc(side, R)][fsq] ^ zobPiece[Pc(side, R)][tsq]; p->bbCl[side] ^= SqBb(fsq) | SqBb(tsq); p->bbTp[R] ^= SqBb(fsq) | SqBb(tsq); p->pstMg[side] += Data.pstMg[side][R][tsq] - Data.pstMg[side][R][fsq]; p->pstEg[side] += Data.pstEg[side][R][tsq] - Data.pstEg[side][R][fsq]; break; case EP_CAP: tsq ^= 8; p->pc[tsq] = NO_PC; p->hashKey ^= zobPiece[Pc(Opp(side), P)][tsq]; p->pawnKey ^= zobPiece[Pc(Opp(side), P)][tsq]; p->bbCl[Opp(side)] ^= SqBb(tsq); p->bbTp[P] ^= SqBb(tsq); p->pcCount[Opp(side)][P]--; p->phase -= Data.phaseValue[P]; p->pstMg[Opp(side)] -= Data.pstMg[Opp(side)][P][tsq]; p->pstEg[Opp(side)] -= Data.pstEg[Opp(side)][P][tsq]; break; case EP_SET: tsq ^= 8; if (bbPawnAttacks[side][tsq] & bbPc(p, Opp(side), P)) { p->epSquare = tsq; p->hashKey ^= zobEp[File(tsq)]; } break; // promotion: (1) add promoted piece and add values associated with it case N_PROM: // (2) remove promoted pawn and substract values associated with it case B_PROM: case R_PROM: case Q_PROM: ftp = PromType(move); p->pc[tsq] = Pc(side, ftp); p->hashKey ^= zobPiece[Pc(side, P)][tsq] ^ zobPiece[Pc(side, ftp)][tsq]; p->pawnKey ^= zobPiece[Pc(side, P)][tsq]; p->bbTp[P] ^= SqBb(tsq); p->bbTp[ftp]^= SqBb(tsq); p->pcCount[side][ftp]++; p->pcCount[side][P]--; p->pieceMat[side] += Data.matValue[ftp]; p->phase += Data.phaseValue[ftp] - Data.phaseValue[P]; p->pstMg[side] += Data.pstMg[side][ftp][tsq] - Data.pstMg[side][P][tsq]; p->pstEg[side] += Data.pstEg[side][ftp][tsq] - Data.pstEg[side][P][tsq]; break; } p->side ^= 1; p->hashKey ^= SIDE_RANDOM; }
void sHistory::UpdateRefutation(int lastMove, int move) { refutation [Fsq(lastMove)] [Tsq(lastMove)] = move; }
int SearchRoot(POS *p, int ply, int alpha, int beta, int depth, int *pv) { int best, score, move, new_depth, new_pv[MAX_PLY]; int fl_check, fl_prunable_move, mv_type, reduction; int mv_tried = 0, quiet_tried = 0; int mv_played[MAX_MOVES]; int mv_hist_score; int victim, last_capt; int move_change = 0; fl_has_choice = 0; MOVES m[1]; UNDO u[1]; // Periodically check for timeout, ponderhit or stop command nodes++; CheckTimeout(); // Quick exit if (abort_search) return 0; // Retrieving data from transposition table. We hope for a cutoff // or at least for a move to improve move ordering. move = 0; if (TransRetrieve(p->hash_key, &move, &score, alpha, beta, depth, ply)) { // For move ordering purposes, a cutoff from hash is treated // exactly like a cutoff from search if (score >= beta) UpdateHistory(p, -1, move, depth, ply); // Root node is a pv node, so we return only exact scores if (score > alpha && score < beta) return score; } // Are we in check? Knowing that is useful when it comes // to pruning/reduction decisions fl_check = InCheck(p); // Init moves and variables before entering main loop best = -INF; InitMoves(p, m, move, Refutation(-1), ply); // Main loop while ((move = NextMove(m, &mv_type))) { mv_hist_score = history[p->pc[Fsq(move)]][Tsq(move)]; victim = TpOnSq(p, Tsq(move)); if (victim != NO_TP) last_capt = Tsq(move); else last_capt = -1; p->DoMove(move, u); if (Illegal(p)) { p->UndoMove(move, u); continue; } // Update move statistics (needed for reduction/pruning decisions) mv_played[mv_tried] = move; mv_tried++; if (mv_tried > 1) fl_has_choice = 1; // we have a choice between at least two root moves if (depth > 16 && verbose) DisplayCurrmove(move, mv_tried); if (mv_type == MV_NORMAL) quiet_tried++; fl_prunable_move = !InCheck(p) && (mv_type == MV_NORMAL); // Set new search depth new_depth = depth - 1 + InCheck(p); // Late move reduction reduction = 0; if (use_lmr && depth >= 2 && mv_tried > 3 && mv_hist_score < hist_limit && alpha > -MAX_EVAL && beta < MAX_EVAL && !fl_check && fl_prunable_move && lmr_size[1][depth][mv_tried] > 0 && MoveType(move) != CASTLE ) { reduction = lmr_size[1][depth][mv_tried]; // increase reduction on bad history score if (mv_hist_score < 0 && new_depth - reduction > 2 && lmr_hist_adjustement) reduction++; new_depth -= reduction; } re_search: // PVS if (best == -INF) score = -Search(p, ply + 1, -beta, -alpha, new_depth, 0, move, last_capt, new_pv); else { score = -Search(p, ply + 1, -alpha - 1, -alpha, new_depth, 0, move, last_capt, new_pv); if (!abort_search && score > alpha && score < beta) score = -Search(p, ply + 1, -beta, -alpha, new_depth, 0, move, last_capt, new_pv); } // Reduced move scored above alpha - we need to re-search it if (reduction && score > alpha) { new_depth += reduction; reduction = 0; goto re_search; } p->UndoMove(move, u); if (abort_search) return 0; // Beta cutoff if (score >= beta) { if (!fl_check) { UpdateHistory(p, -1, move, depth, ply); for (int mv = 0; mv < mv_tried; mv++) DecreaseHistory(p, mv_played[mv], depth); } TransStore(p->hash_key, move, score, LOWER, depth, ply); // Update search time depending on whether the first move has changed if (depth > 4) { if (pv[0] != move) Timer.OnNewRootMove(); else Timer.OnOldRootMove(); } // Change the best move and show the new pv BuildPv(pv, new_pv, move); DisplayPv(score, pv); return score; } // Updating score and alpha if (score > best) { best = score; if (score > alpha) { alpha = score; // Update search time depending on whether the first move has changed if (depth > 4) { if (pv[0] != move) Timer.OnNewRootMove(); else Timer.OnOldRootMove(); } // Change the best move and show the new pv BuildPv(pv, new_pv, move); DisplayPv(score, pv); } } } // end of the main loop // Return correct checkmate/stalemate score if (best == -INF) return InCheck(p) ? -MATE + ply : DrawScore(p); // Save score in the transposition table if (*pv) { if (!fl_check) { UpdateHistory(p, -1, *pv, depth, ply); for (int mv = 0; mv < mv_tried; mv++) DecreaseHistory(p, mv_played[mv], depth); } TransStore(p->hash_key, *pv, best, EXACT, depth, ply); } else TransStore(p->hash_key, 0, best, UPPER, depth, ply); return best; }