int main(int argc, char *argv[]) { #if HAVE_SETBUF setbuf(stdin, NULL); #endif OpenLogFile("Amy.log"); InitMoves(); InitAll(); HashInit(); /* * Process rc file first, then command line options. This way command * line options can override rc file settings. */ ProcessRCFile(); ProcessOptions(argc, argv); ShowVersion(); AllocateHT(); InitEGTB(EGTBPath); RecogInit(); DoBookLearning(); Print(0, "\n"); strcpy(AutoSaveFileName, GetTmpFileName()); /* Ensure true random behavior. */ InitRandom(GetTime()); StateMachine(); return 0; }
int Perft(POS *p, int ply, int depth) { int move = 0; int fl_mv_type; MOVES m[1]; UNDO u[1]; int mv_cnt = 0; InitMoves(p, m, 0, 0, ply); while (move = NextMove(m, &fl_mv_type)) { p->DoMove(move, u); if (Illegal(p)) { p->UndoMove(move, u); continue; } if (depth == 1) mv_cnt++; else mv_cnt += Perft(p, ply + 1, depth - 1); p->UndoMove(move, u); } return mv_cnt; }
int QuiesceFlee(POS *p, int ply, int alpha, int beta, int *pv) { int best, score, move, new_pv[MAX_PLY]; int fl_check, mv_type; int is_pv = (beta > alpha + 1); MOVES m[1]; UNDO u[1]; // 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); // 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, 0, ply)) 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); // Init moves and variables before entering main loop best = -INF; InitMoves(p, m, move, -1, ply); // Main loop while ((move = NextMove(m, &mv_type))) { p->DoMove(move, u); if (Illegal(p)) { p->UndoMove(move, u); continue; } score = -Quiesce(p, ply, -beta, -alpha, new_pv); p->UndoMove(move, u); if (abort_search) return 0; // Beta cutoff if (score >= beta) { TransStore(p->hash_key, move, score, LOWER, 0, 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) TransStore(p->hash_key, *pv, best, EXACT, 0, ply); else TransStore(p->hash_key, 0, best, UPPER, 0, ply); return best; }
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; }
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; }