Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions eval.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ Value *mgPst[] = { nullptr, mg_pawn_table, mg_knight_table, mg_bishop_table, mg_
Value *egPst[] = { nullptr, eg_pawn_table, eg_knight_table, eg_bishop_table, eg_rook_table, eg_queen_table, eg_king_table };

// tuning slop here
/*

TUNE(SetRange(5, 30),
tempo,
SetRange(80, 120),
Expand Down Expand Up @@ -225,7 +225,7 @@ TUNE(SetRange(10, 100), minorImWt, SetRange(5, 50), bishopImWt, SetRange(10, 100
TUNE(SetRange(1, 50), rammedPawnPenalty);
TUNE(SetRange(1, 100), rookOnSeventhBonus);
TUNE(SetRange(1, 50), earlyQueenPenalty);
*/

EvalComponents eval_components(const chess::Position &board) {
constexpr int KnightPhase = 1;
constexpr int BishopPhase = 1;
Expand Down
1 change: 1 addition & 0 deletions main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ using namespace engine;
int main() {
std::cout << std::unitbuf;
std::cout << "cppchess_engine version " << BUILD_VERSION << '\n';
options.add("Threads", Option(1, 1, 1));
options.add("Move Overhead", Option(10, 0, 1000));
options.add("Hash", Option(16, 1, 1 << 25, [](const Option &o) {
try {
Expand Down
27 changes: 13 additions & 14 deletions search.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -358,7 +358,7 @@ Value doSearch(
Value maxScore = -VALUE_INFINITE;
Move bestMove = Move::none();
int movesSearched = 0;

Movelist quietsSearched;
for (size_t i = 0; i < moves.size(); ++i) {
Move move = moves[i];
if (ply == 0 && !session.tc.searchmoves.empty() &&
Expand Down Expand Up @@ -419,7 +419,7 @@ Value doSearch(
ext = 1;
if (ext == 0 && moves.size() == 1)
ext = 1;
if (ext == 0 && isCapture && movesSearched == 0)
if (ext == 0 && isCapture)
ext = 1;
board.doMove(move);

Expand Down Expand Up @@ -453,30 +453,28 @@ Value doSearch(

board.undoMove();
movesSearched++;

if (!isCapture && !givesCheck)
quietsSearched.push_back(move);
if (score > maxScore) {
maxScore = score;
bestMove = move;
}

if (score > alpha) {
if (score > alpha)
alpha = score;

if (alpha >= beta) {
if (!isCapture) {
int bonus = depth * depth;
if (is_win(score))
bonus += 4 * depth * depth;
session.historyHeuristic[(int)move.from()][(int)move.to()] =
std::clamp(session.historyHeuristic[(int)move.from()][(int)move.to()] + bonus, -16384, 16384);
}
} /*else if (!isCapture && depth > 0) {
int malus = -depth * depth;
session.historyHeuristic[(int)move.from()][(int)move.to()] =
std::clamp(session.historyHeuristic[(int)move.from()][(int)move.to()] + malus, -16384, 16384);
}*/

if (alpha >= beta) {
if (!isCapture) {
int malus = 300 * depth - 250;
for (Move move_ : quietsSearched)
if (move_ != move)
session.historyHeuristic[(int)move_.from()][(int)move_.to()] =
std::clamp(session.historyHeuristic[(int)move_.from()][(int)move_.to()] - malus, -16384, 16384);
if (session.killerMoves[ply][0] != move) {
session.killerMoves[ply][1] = session.killerMoves[ply][0];
session.killerMoves[ply][0] = move;
Expand Down Expand Up @@ -528,7 +526,7 @@ std::string extract_pv(const chess::Position &root, int maxPly) {
return pv;
}

void search(const chess::Position &board, const timeman::LimitsType timecontrol) {
uint64_t search(const chess::Position &board, const timeman::LimitsType timecontrol) {
stopSearch = false;
tt.newSearch();
static double originalTimeAdjust = -1;
Expand Down Expand Up @@ -655,5 +653,6 @@ void search(const chess::Position &board, const timeman::LimitsType timecontrol)
if (entry && entry->getMove() != Move::none().raw())
report(chess::uci::moveToUci(Move(entry->getMove()), board.chess960()));
}
return session.nodes;
}
} // namespace engine::search
3 changes: 2 additions & 1 deletion search.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#include "eval.h"
#include "timeman.h"
#include "tt.h"
#include <cstdint>
#include <position.h>
namespace engine::search {
struct Session {
Expand All @@ -18,7 +19,7 @@ struct Session {
chess::Color ogcolor;
};
void stop();
void search(const chess::Position &, const timeman::LimitsType);
uint64_t search(const chess::Position &, const timeman::LimitsType);
bool isStopped();
extern engine::TranspositionTable tt;
} // namespace engine::search
4 changes: 2 additions & 2 deletions timeman.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,12 @@ void TimeManagement::init(LimitsType &limits, chess::Color us, int ply, double &

// If we have no time, we don't need to fully initialize TM.
// startTime is used by movetime and useNodesTime is used in elapsed calls.
startTime = limits.startTime;
startTime = now();
if (limits.movetime != 0) {
optimumTime = maximumTime = TimePoint(limits.movetime);
return;
}
if (limits.time[us] == 0 && limits.movetime == 0) {
if (!limits.use_time_management()) {
optimumTime = maximumTime = INFINITE_TIME;
return;
}
Expand Down
4 changes: 2 additions & 2 deletions timeman.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ struct LimitsType {

// Init explicitly due to broken value-initialization of non POD in MSVC
LimitsType() {
time[chess::WHITE] = time[chess::BLACK] = inc[chess::WHITE] = inc[chess::BLACK] = movetime = startTime = TimePoint(0);
time[chess::WHITE] = time[chess::BLACK] = inc[chess::WHITE] = inc[chess::BLACK] = movetime = TimePoint(0);
movestogo = mate = perft = infinite = 0;
depth = 64;
nodes = 0;
Expand All @@ -25,7 +25,7 @@ struct LimitsType {
bool use_time_management() const { return time[chess::WHITE] || time[chess::BLACK]; }

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Check the moving side's clock before time-managing

When only the opponent's clock is provided/nonzero, e.g. go btime 30000 while White is to move, this now enables time management even though limits.time[us] is zero. TimeManagement::init() then computes scales from a zero clock (log10(time / 1000.0)), which can produce non-finite or effectively immediate/huge time limits; the previous side-specific guard avoided that path. Base the predicate on the side to move's clock instead of either clock.

Useful? React with 👍 / 👎.


std::vector<std::string> searchmoves;
TimePoint time[chess::COLOR_NB], inc[chess::COLOR_NB], movetime, startTime;
TimePoint time[chess::COLOR_NB], inc[chess::COLOR_NB], movetime;
int movestogo, depth, mate, perft, infinite;
uint64_t nodes;
bool ponderMode;
Expand Down
72 changes: 70 additions & 2 deletions uci.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -83,8 +83,6 @@ timeman::LimitsType parse_limits(std::istream &is) {
timeman::LimitsType limits;
std::string token;

limits.startTime = timeman::now(); // The search starts as early as possible

while (is >> token)
if (token == "searchmoves") // Needs to be the last command on the line
while (is >> token) {
Expand Down Expand Up @@ -240,6 +238,76 @@ void execCmd(const std::string &line) {
score = -score;
std::cout << score << std::endl;
break;
} else if (token == "bench") {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Stop any active search before benching

If bench is entered while a previous go search is still running, this branch starts repeated synchronous search::search() calls without first joining searchThread. Since search::search() resets the shared stop flag and uses the global transposition table, the benchmark and background search can run concurrently and interleave/corrupt engine state and output; other search-affecting commands call stop() before proceeding.

Useful? React with 👍 / 👎.

uint64_t nodes = 0;
const std::vector<std::string> Defaults = {
"rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1",
"r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq - 0 10",
"8/2p5/3p4/KP5r/1R3p1k/8/4P1P1/8 w - - 0 11",
"4rrk1/pp1n3p/3q2pQ/2p1pb2/2PP4/2P3N1/P2B2PP/4RRK1 b - - 7 19",
"rq3rk1/ppp2ppp/1bnpb3/3N2B1/3NP3/7P/PPPQ1PP1/2KR3R w - - 7 14 moves d4e6",
"r1bq1r1k/1pp1n1pp/1p1p4/4p2Q/4Pp2/1BNP4/PPP2PPP/3R1RK1 w - - 2 14 moves g2g4",
"r3r1k1/2p2ppp/p1p1bn2/8/1q2P3/2NPQN2/PPP3PP/R4RK1 b - - 2 15",
"r1bbk1nr/pp3p1p/2n5/1N4p1/2Np1B2/8/PPP2PPP/2KR1B1R w kq - 0 13",
"r1bq1rk1/ppp1nppp/4n3/3p3Q/3P4/1BP1B3/PP1N2PP/R4RK1 w - - 1 16",
"4r1k1/r1q2ppp/ppp2n2/4P3/5Rb1/1N1BQ3/PPP3PP/R5K1 w - - 1 17",
"2rqkb1r/ppp2p2/2npb1p1/1N1Nn2p/2P1PP2/8/PP2B1PP/R1BQK2R b KQ - 0 11",
"r1bq1r1k/b1p1npp1/p2p3p/1p6/3PP3/1B2NN2/PP3PPP/R2Q1RK1 w - - 1 16",
"3r1rk1/p5pp/bpp1pp2/8/q1PP1P2/b3P3/P2NQRPP/1R2B1K1 b - - 6 22",
"r1q2rk1/2p1bppp/2Pp4/p6b/Q1PNp3/4B3/PP1R1PPP/2K4R w - - 2 18",
"4k2r/1pb2ppp/1p2p3/1R1p4/3P4/2r1PN2/P4PPP/1R4K1 b - - 3 22",
"3q2k1/pb3p1p/4pbp1/2r5/PpN2N2/1P2P2P/5PP1/Q2R2K1 b - - 4 26",
"6k1/6p1/6Pp/ppp5/3pn2P/1P3K2/1PP2P2/3N4 b - - 0 1",
"3b4/5kp1/1p1p1p1p/pP1PpP1P/P1P1P3/3KN3/8/8 w - - 0 1",
"2K5/p7/7P/5pR1/8/5k2/r7/8 w - - 0 1 moves g5g6 f3e3 g6g5 e3f3",
"8/6pk/1p6/8/PP3p1p/5P2/4KP1q/3Q4 w - - 0 1",
"7k/3p2pp/4q3/8/4Q3/5Kp1/P6b/8 w - - 0 1",
"8/2p5/8/2kPKp1p/2p4P/2P5/3P4/8 w - - 0 1",
"8/1p3pp1/7p/5P1P/2k3P1/8/2K2P2/8 w - - 0 1",
"8/pp2r1k1/2p1p3/3pP2p/1P1P1P1P/P5KR/8/8 w - - 0 1",
"8/3p4/p1bk3p/Pp6/1Kp1PpPp/2P2P1P/2P5/5B2 b - - 0 1",
"5k2/7R/4P2p/5K2/p1r2P1p/8/8/8 b - - 0 1",
"6k1/6p1/P6p/r1N5/5p2/7P/1b3PP1/4R1K1 w - - 0 1",
"1r3k2/4q3/2Pp3b/3Bp3/2Q2p2/1p1P2P1/1P2KP2/3N4 w - - 0 1",
"6k1/4pp1p/3p2p1/P1pPb3/R7/1r2P1PP/3B1P2/6K1 w - - 0 1",
"8/3p3B/5p2/5P2/p7/PP5b/k7/6K1 w - - 0 1",
"5rk1/q6p/2p3bR/1pPp1rP1/1P1Pp3/P3B1Q1/1K3P2/R7 w - - 93 90",
"4rrk1/1p1nq3/p7/2p1P1pp/3P2bp/3Q1Bn1/PPPB4/1K2R1NR w - - 40 21",
"r3k2r/3nnpbp/q2pp1p1/p7/Pp1PPPP1/4BNN1/1P5P/R2Q1RK1 w kq - 0 16",
"3Qb1k1/1r2ppb1/pN1n2q1/Pp1Pp1Pr/4P2p/4BP2/4B1R1/1R5K b - - 11 40",
"4k3/3q1r2/1N2r1b1/3ppN2/2nPP3/1B1R2n1/2R1Q3/3K4 w - - 5 1",
"1r6/1P4bk/3qr1p1/N6p/3pp2P/6R1/3Q1PP1/1R4K1 w - - 1 42",

// 5-man positions
"8/8/8/8/5kp1/P7/8/1K1N4 w - - 0 1", // Kc2 - mate
"8/8/8/5N2/8/p7/8/2NK3k w - - 0 1", // Na2 - mate
"8/3k4/8/8/8/4B3/4KB2/2B5 w - - 0 1", // draw

// 6-man positions
"8/8/1P6/5pr1/8/4R3/7k/2K5 w - - 0 1", // Re5 - mate
"8/2p4P/8/kr6/6R1/8/8/1K6 w - - 0 1", // Ka2 - mate
"8/8/3P3k/8/1p6/8/1P6/1K3n2 b - - 0 1", // Nd2 - draw

// 7-man positions
"8/R7/2q5/8/6k1/8/1P5p/K6R w - - 0 124", // Draw

// Mate and stalemate positions
"6k1/3b3r/1p1p4/p1n2p2/1PPNpP1q/P3Q1p1/1R1RB1P1/5K2 b - - 0 1",
"r2r1n2/pp2bk2/2p1p2p/3q4/3PN1QP/2P3R1/P4PP1/5RK1 w - - 0 1",
"8/8/8/8/8/6k1/6p1/6K1 w - -",
"7k/7P/6K1/8/3B4/8/8/8 b - -",
};
timeman::LimitsType tc{};
tc.depth = 10;
auto start = std::chrono::high_resolution_clock::now();
for (auto &fen : Defaults) {
std::cerr << fen << '\n';
chess::Position pos(fen);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Parse bench move suffixes before searching

Several benchmark entries above include a UCI-style moves ... suffix, but constructing chess::Position directly from the whole string does not replay those moves, so those cases are searched from the base FEN instead of the intended post-move position (or may be rejected by strict FEN parsing). Reuse the same FEN-plus-moves parsing logic as handlePosition() for each bench entry.

Useful? React with 👍 / 👎.

nodes += search::search(pos, tc);
}
auto end = std::chrono::high_resolution_clock::now();
auto duration_ms = std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count();
std::cout << "nodes " << nodes << " nps " << nodes / (duration_ms / 1000.0) << std::endl;
} else if (token == "export_weights") {
std::string weights_header = "Weights.h";
ss >> weights_header;
Expand Down