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
3 changes: 2 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,11 @@ project(cppchess_engine)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
include(FetchContent)
set(BUILD_TESTING OFF)
FetchContent_Declare(
chesslib
GIT_REPOSITORY https://github.com/winapiadmin/chesslib.git
GIT_TAG maintaince_1
GIT_TAG main
)
FetchContent_MakeAvailable(chesslib)
add_executable(engine "main.cpp" "timeman.cpp" "timeman.h" "eval.h" "eval.cpp" "tune.h" "ucioption.h" "tune.cpp" "ucioption.cpp" "tt.h" "tt.cpp" "uci.cpp" "uci.h" "search.h" "search.cpp" "score.h" "score.cpp" "movepick.h" "movepick.cpp")
Expand Down
12 changes: 6 additions & 6 deletions eval.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -96,13 +96,13 @@ Value eg_king_table[64] = {-74, -35, -18, -18, -11, 15, 4, -17, -12, 17, 14,
23, 16, 7, -9, -27, -11, 4, 13, 14, 4, -5,
-17, -53, -34, -21, -11, -28, -14, -24, -43};

Value *mg_pesto_table[] = {
{0}, mg_pawn_table, mg_knight_table, mg_bishop_table,
mg_rook_table, mg_queen_table, mg_king_table};
Value *mg_pesto_table[] = {nullptr, mg_pawn_table, mg_knight_table,
mg_bishop_table, mg_rook_table, mg_queen_table,
mg_king_table};

Value *eg_pesto_table[] = {
{0}, eg_pawn_table, eg_knight_table, eg_bishop_table,
eg_rook_table, eg_queen_table, eg_king_table};
Value *eg_pesto_table[] = {nullptr, eg_pawn_table, eg_knight_table,
eg_bishop_table, eg_rook_table, eg_queen_table,
eg_king_table};

Value eval(const chess::Board &board) {
int pieceCount[10] = {
Expand Down
2 changes: 0 additions & 2 deletions eval.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
#pragma once
#include <cstdint>
#include <cstdlib>
#include <fwd_decl.h>
using Value = int;
namespace engine {
Expand Down
28 changes: 17 additions & 11 deletions main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,25 @@
#include "uci.h"
#include "ucioption.h"
#include <iostream>
#include <thread>
using namespace engine;
extern std::thread searchThread;
int main() {
options.add("Move Overhead", Option(10, 0, 1000));
options.add( //
"Hash", Option(16, 1, 1 << 25, [](const Option &o) {
search::tt.resize((o.operator int()));
return std::nullopt;
}));
options.add("Hash", Option(16, 1, 1 << 25, [](const Option &o) {
search::tt.resize((o.operator int()));
return std::nullopt;
}));

options.add( //
"Clear Hash", Option(+[](const Option &) {
search::tt.clear();
return std::nullopt;
}));
options.add("Clear Hash", Option(+[](const Option &) {
if (searchThread.joinable()) {
std::cout
<< "info string In search, do not modify hash table\n";
return std::nullopt;
}
search::tt.clear();

return std::nullopt;
}));
loop();
}
}
3 changes: 2 additions & 1 deletion score.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include "score.h"
#include <cassert>
#include <cmath>
namespace engine {
Score::Score(Value v) {
assert(-VALUE_INFINITE < v && v < VALUE_INFINITE);
Expand All @@ -14,4 +15,4 @@ Score::Score(Value v) {
score = (v > 0) ? Mate{distance} : Mate{-distance};
}
}
} // namespace engine
} // namespace engine
114 changes: 93 additions & 21 deletions search.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,12 @@
#include <atomic>
#include <moves_io.h>
#include <position.h>
#include <printers.h>
using namespace chess;
namespace engine {
TranspositionTable search::tt(16);
std::atomic<bool> stopSearch{false};
void search::stop() {
tt.clear();
stopSearch.store(true, std::memory_order_relaxed);
}
void search::stop() { stopSearch.store(true, std::memory_order_relaxed); }
struct Session {
timeman::TimeManagement tm;
timeman::LimitsType tc;
Expand All @@ -29,7 +27,11 @@ void update_pv(Move *pv, Move move, const Move *childPv) {
}
Value qsearch(Board &board, Value alpha, Value beta, Session &session,
int ply = 0) {
if (session.tm.elapsed() >= session.tm.optimum() ||
stopSearch.load(std::memory_order_relaxed))
return VALUE_NONE;
session.nodes++;
session.seldepth = std::max(session.seldepth, ply);
int standPat = eval::eval(board);
Value maxScore = standPat;
if (maxScore >= beta)
Expand All @@ -40,8 +42,11 @@ Value qsearch(Board &board, Value alpha, Value beta, Session &session,
board.legals<MoveGenType::CAPTURE>(moves);
for (Move move : moves) {
board.doMove(move);
Value score = -qsearch(board, -beta, -alpha, session, ply + 1);
Value score = qsearch(board, -beta, -alpha, session, ply + 1);
board.undoMove();
if (score == VALUE_NONE)
return VALUE_NONE;
score = -score;
if (score >= beta)
return score;
if (score > maxScore)
Expand All @@ -51,7 +56,7 @@ Value qsearch(Board &board, Value alpha, Value beta, Session &session,
}
return maxScore;
}
Value doSearch(Board &board, int depth, Value alpha, Value beta,
Value doSearch(Board board, int depth, Value alpha, Value beta,
Session &session, int ply = 0) {
if (ply >= MAX_PLY - 1)
return eval::eval(board);
Expand Down Expand Up @@ -97,7 +102,7 @@ Value doSearch(Board &board, int depth, Value alpha, Value beta,
preferred = Move(entry->getMove());
}
if (depth == 0) {
return qsearch(board, alpha, beta, session, ply + 1);
return qsearch(board, alpha, beta, session, ply);
}
Value maxScore = -VALUE_INFINITE;
Movelist moves;
Expand All @@ -107,20 +112,73 @@ Value doSearch(Board &board, int depth, Value alpha, Value beta,
return board.checkers() ? -MATE(ply) : 0;
}
movepick::orderMoves(board, moves, preferred, ply);
for (Move move : moves) {
if (bool useNMP = depth >= 3 && !board.checkers() && ply > 0) {
int R = 2 + depth / 6;
board.doNullMove();
Value score =
doSearch(board, depth - 1 - R, -beta, -beta + 1, session, ply + 1);

if (score == VALUE_NONE) {
board.undoMove();
return VALUE_NONE;
}
score = -score;
board.undoMove();
if (score >= beta)
return score;
}
for (size_t i = 0; i < moves.size(); ++i) {
Move move = moves[i];

bool isCapture = board.isCapture(move);
bool givesCheck = board.givesCheck(move) != CheckType::NO_CHECK;

// --- LMR reduction ---
int reduction = 0;
if (i >= 3 && depth >= 3 && !isCapture && !givesCheck) {
reduction = 1 + (int)(i / 6) + (depth / 8);

// history heuristic: good moves get reduced less
if (movepick::historyHeuristic[(int)move.from()][(int)move.to()] > 0)
reduction--;

reduction = std::max(0, reduction);
reduction = std::min(reduction, depth - 2);
}

board.doMove(move);

Value childScore =
doSearch(board, depth - 1, -beta, -alpha, session, ply + 1);
Value score;

board.undoMove();
if (i == 0) {
// --- First move: full window (PVS root move) ---
score = -doSearch(board, depth - 1, -beta, -alpha, session, ply + 1);

// ---- ABORT PROPAGATION ----
if (childScore == VALUE_NONE)
return VALUE_NONE;
if (score == VALUE_NONE) {
board.undoMove();
return VALUE_NONE;
}
} else {
// --- Null-window search (PVS + LMR) ---
score = doSearch(board, depth - 1 - reduction, -alpha - 1, -alpha,
session, ply + 1);
if (score == VALUE_NONE) {
board.undoMove();
return VALUE_NONE;
}
score = -score;
// --- Re-search if it improves alpha ---
if (score > alpha) {
score = doSearch(board, depth - 1, -beta, -alpha, session, ply + 1);
if (score == VALUE_NONE) {
board.undoMove();
return VALUE_NONE;
}
score = -score;
}
}

Value score = -childScore;
board.undoMove();

if (score > maxScore) {
maxScore = score;
Expand All @@ -129,18 +187,20 @@ Value doSearch(Board &board, int depth, Value alpha, Value beta,

if (score > alpha) {
alpha = score;
if (!board.isCapture(move))

if (!isCapture)
movepick::historyHeuristic[(int)move.from()][(int)move.to()] +=
depth * depth;
}

if (alpha >= beta) {
if (!board.isCapture(move)) {
// killer moves
if (!isCapture) {
if (movepick::killerMoves[ply][0] != move) {
movepick::killerMoves[ply][1] = movepick::killerMoves[ply][0];
movepick::killerMoves[ply][0] = move;
}
}

break;
}

Expand All @@ -165,6 +225,7 @@ Value doSearch(Board &board, int depth, Value alpha, Value beta,
}
void search::search(const chess::Board &board,
const timeman::LimitsType timecontrol) {
stopSearch = false;
static double originalTimeAdjust = -1;
Session session;
session.tc = timecontrol;
Expand All @@ -178,12 +239,11 @@ void search::search(const chess::Board &board,
// since MAX_PLY=64
session.pv[_][j] = Move::none();
}
session.nodes = 0;
auto board_ = board;
Value score_ =
doSearch(board_, i, -VALUE_INFINITE, VALUE_INFINITE, session);
if (session.tm.elapsed() >= session.tm.optimum() ||
stopSearch.load(std::memory_order_relaxed) || score_ == VALUE_NONE)
stopSearch.load(std::memory_order_relaxed) || abs(score_) == VALUE_NONE)
break;
InfoFull info{};
info.depth = i;
Expand All @@ -195,6 +255,18 @@ void search::search(const chess::Board &board,
info.timeMs = session.tm.elapsed();
info.multiPV = 1;
info.score = score_;
TTEntry *entry = tt.lookup(board.hash());
if (entry)
switch (entry->getFlag()) {
case LOWERBOUND:
info.bound = "lowerbound";
break;
case UPPERBOUND:
info.bound = "upperbound";
break;
default:
break;
}
std::string pv = "";
for (Move *m = session.pv[0]; *m != Move::none(); m++)
pv += chess::uci::moveToUci(*m, board.chess960()) + " ";
Expand Down Expand Up @@ -233,7 +305,7 @@ void search::search(const chess::Board &board,
info.nodes = 1;
info.score = 0;
info.multiPV = 1;
info.pv = chess::uci::moveToUci(best, board.chess960());
info.pv = std::string(chess::uci::moveToUci(best, board.chess960()));
report(info);

report(chess::uci::moveToUci(best, board.chess960()));
Expand Down
2 changes: 1 addition & 1 deletion tt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ TTEntry *TranspositionTable::lookup(uint64_t hash) {
TTEntry &e0 = table[index], &e1 = table[index + 1];
// Check the entries
for (TTEntry *e : {&e0, &e1}) {
if (e->key == hash)
if (e->key == hash && e->getGeneration() == this->time)
return e;
}
return nullptr;
Expand Down
6 changes: 3 additions & 3 deletions tt.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@ struct TTEntry {
<< GEN_SHIFT;

// getters
inline uint16_t getScore() const noexcept {
return static_cast<uint16_t>((pack & SCORE_MASK) >> SCORE_SHIFT);
inline int16_t getScore() const noexcept {
return static_cast<int16_t>((pack & SCORE_MASK) >> SCORE_SHIFT);
}

inline uint8_t getDepth() const noexcept {
Expand Down Expand Up @@ -176,4 +176,4 @@ class TranspositionTable {
return (used * 1000) / buckets;
}
};
} // namespace engine
} // namespace engine
Loading
Loading