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
2 changes: 2 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ add_library(ironphoenix_core STATIC
src/tt.cpp
src/eval.cpp
src/nnue.cpp
src/tuning.cpp
src/search.cpp
src/multipv.cpp
src/fen.cpp
Expand All @@ -33,6 +34,7 @@ add_library(ironphoenix_core STATIC
include/ironphoenix/eval.hpp
include/ironphoenix/lmr.hpp
include/ironphoenix/nnue.hpp
include/ironphoenix/tuning.hpp
)

add_library(IronPhoenix::Core ALIAS ironphoenix_core)
Expand Down
7 changes: 4 additions & 3 deletions include/ironphoenix/eval.hpp
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
#pragma once

#include "tuning.hpp"

namespace ironphoenix {

class Position;

namespace Eval {

struct Parameters final {

};
using Parameters = Tuning::EvalParameters;

[[nodiscard]] Parameters& parameters() noexcept;
[[nodiscard]] int evaluate(const Position& pos) noexcept;

}
Expand Down
5 changes: 4 additions & 1 deletion include/ironphoenix/lmr.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,10 @@ namespace ironphoenix {
std::array<std::array<std::uint8_t, LMR_MAX_MOVES + 1>,
LMR_MAX_DEPTH + 1>;

extern const LmrReductionTable lmrReduction;
// The table is rebuilt whenever an LMR tuning parameter changes.
extern LmrReductionTable lmrReduction;

void rebuildLmrReductionTable() noexcept;

inline int lmrBaseReduction(int depth, int moveCount) noexcept {
if (depth <= 0 || moveCount <= 0)
Expand Down
57 changes: 57 additions & 0 deletions include/ironphoenix/tuning.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
#pragma once

#include <iosfwd>
#include <string_view>

namespace ironphoenix {

class SearchEngine;

namespace Tuning {

struct SearchParameters final {
int aspirationStartDepth = 4;
int aspirationInitialDelta = 50;
int seePruneMaxDepth = 4;
int seePruneMarginPerDepth = 80;
int historyBonusScale = 64;
int historyBonusMax = 2048;
int counterMoveBonus = 32'000;
int checkMoveBonus = 10'000'000;
int lmrMinDepth = 3;
int lmrFullMoves = 3;
int lmrBaseX100 = 75;
int lmrDivisorX100 = 275;
};

struct EvalParameters final {
int pawnValue = 100;
int knightValue = 300;
int bishopValue = 400;
int rookValue = 500;
int queenValue = 1000;
int knightMobility = 4;
int bishopMobility = 4;
int rookMobility = 2;
int queenMobility = 1;
};

[[nodiscard]] SearchParameters& search() noexcept;
[[nodiscard]] EvalParameters& eval() noexcept;

void resetSearch() noexcept;
void resetEval() noexcept;
void resetAll() noexcept;

[[nodiscard]] bool setSearchParameter(std::string_view name, int value) noexcept;
[[nodiscard]] bool setEvalParameter(std::string_view name, int value) noexcept;

// UCI integration helpers. handleSetOption returns true when the option
// belongs to the tuning system, including recognized-but-invalid input.
[[nodiscard]] bool handleSetOption(std::string_view args, SearchEngine& searchEngine, std::ostream& out);
void printUciOptions(std::ostream& out);
void printCurrent(std::ostream& out);

} // namespace Tuning

} // namespace ironphoenix
40 changes: 30 additions & 10 deletions src/eval.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,36 @@
#include "ironphoenix/geometry.hpp"
#include "ironphoenix/nnue.hpp"
#include "ironphoenix/position.hpp"

#include <array>
#include "ironphoenix/tuning.hpp"

namespace ironphoenix::Eval {

namespace {

constexpr std::array<int, PIECE_TYPE_NB> EVAL_VALUE = {
0, 100, 300, 400, 500, 1000, 0
};
IRONPHOENIX_FORCE_INLINE int evalValue(PieceType pt) noexcept {
const auto& params = Tuning::eval();

constexpr std::array<int, PIECE_TYPE_NB> MOBILITY_WEIGHT = {
0, 0, 4, 4, 2, 1, 0
};
switch (pt) {
case PAWN: return params.pawnValue;
case KNIGHT: return params.knightValue;
case BISHOP: return params.bishopValue;
case ROOK: return params.rookValue;
case QUEEN: return params.queenValue;
default: return 0;
}
}

IRONPHOENIX_FORCE_INLINE int mobilityWeight(PieceType pt) noexcept {
const auto& params = Tuning::eval();

switch (pt) {
case KNIGHT: return params.knightMobility;
case BISHOP: return params.bishopMobility;
case ROOK: return params.rookMobility;
case QUEEN: return params.queenMobility;
default: return 0;
}
}

IRONPHOENIX_FORCE_INLINE Bitboard teamOccupancy(const Position& pos, Color c) noexcept {
const Color partner = static_cast<Color>(static_cast<unsigned>(c) ^ 2u);
Expand All @@ -28,7 +44,7 @@ namespace ironphoenix::Eval {

for (unsigned pt = PAWN; pt <= QUEEN; ++pt) {
material += pos.pieces(c, static_cast<PieceType>(pt)).popcount()
* EVAL_VALUE[pt];
* evalValue(static_cast<PieceType>(pt));
}

return material;
Expand Down Expand Up @@ -65,7 +81,7 @@ namespace ironphoenix::Eval {
}

const int mobility = (attacks & ~blockedByTeam).popcount();
score += mobility * MOBILITY_WEIGHT[static_cast<unsigned>(pt)];
score += mobility * mobilityWeight(pt);
}
};

Expand Down Expand Up @@ -102,6 +118,10 @@ namespace ironphoenix::Eval {

}

Parameters& parameters() noexcept {
return Tuning::eval();
}

int evaluate(const Position& pos) noexcept {
if (NNUE::loaded())
return NNUE::evaluate(pos);
Expand Down
22 changes: 15 additions & 7 deletions src/lmr.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#include "ironphoenix/lmr.hpp"
#include "ironphoenix/tuning.hpp"

#include <algorithm>
#include <cmath>
Expand All @@ -7,26 +8,29 @@ namespace ironphoenix {

namespace {

LmrReductionTable buildLmrReductionTable() {
LmrReductionTable buildLmrReductionTable() noexcept {
LmrReductionTable table{};
const auto& params = Tuning::search();

const double base = static_cast<double>(params.lmrBaseX100) / 100.0;
const double divisor = static_cast<double>(std::max(1, params.lmrDivisorX100)) / 100.0;

for (int depth = 1; depth <= LMR_MAX_DEPTH; ++depth) {
for (int moveCount = 1; moveCount <= LMR_MAX_MOVES; ++moveCount) {

if (depth < 3 || moveCount <= 3) {
if (depth < params.lmrMinDepth || moveCount <= params.lmrFullMoves) {
table[depth][moveCount] = 0;
continue;
}

const double raw =
0.75
base
+ (std::log(static_cast<double>(depth))
* std::log(static_cast<double>(moveCount))) / 2.75;
* std::log(static_cast<double>(moveCount))) / divisor;

int reduction = static_cast<int>(raw);

reduction = std::max(reduction, 1);

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

table[depth][moveCount] =
Expand All @@ -39,6 +43,10 @@ namespace ironphoenix {

}

const LmrReductionTable lmrReduction = buildLmrReductionTable();
LmrReductionTable lmrReduction = buildLmrReductionTable();

void rebuildLmrReductionTable() noexcept {
lmrReduction = buildLmrReductionTable();
}

}
26 changes: 12 additions & 14 deletions src/search.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

#include "ironphoenix/eval.hpp"
#include "ironphoenix/lmr.hpp"
#include "ironphoenix/tuning.hpp"

#include "ironphoenix/movegen.hpp"
#include "ironphoenix/see.hpp"
Expand All @@ -15,13 +16,6 @@
namespace ironphoenix {
namespace {

constexpr int ASPIRATION_START_DEPTH = 4;
constexpr int ASPIRATION_INITIAL_DELTA = 50;

constexpr int SEE_PRUNE_MAX_DEPTH = 4;
constexpr int SEE_PRUNE_MARGIN_PER_DEPTH = 80;
constexpr int QSEARCH_SEE_THRESHOLD = -50;

IRONPHOENIX_FORCE_INLINE Bitboard teamOccupancy(const Position& pos, Color c) noexcept {
const Color partner = static_cast<Color>(static_cast<unsigned>(c) ^ 2u);
return pos.occupancy(c) | pos.occupancy(partner);
Expand Down Expand Up @@ -124,7 +118,8 @@ namespace ironphoenix {
}

int SearchEngine::historyBonus(int depth) const noexcept {
return std::min(2048, 64 * depth * depth);
const auto& params = Tuning::search();
return std::min(params.historyBonusMax, params.historyBonusScale * depth * depth);
}

int SearchEngine::moveScore(const Position& pos, Move move, Move ttMove, int ply) const noexcept {
Expand All @@ -133,6 +128,7 @@ namespace ironphoenix {
if (isTerminalKingCapture(pos, move))
return 45'000'000;

const auto& tuning = Tuning::search();
const Color mover = pos.sideToMove();
const Piece movingPiece = pos.pieceAt(move.from());

Expand All @@ -158,7 +154,7 @@ namespace ironphoenix {
score += history_.continuationScore(ContinuationKind::PreviousPly, searchStack_[ply], movingPiece, move.to());

if (history_.counterMove(mover, searchStack_[ply]) == move)
score += 32'000;
score += tuning.counterMoveBonus;
}

if (ply >= 4 && searchStack_[ply - 3].valid()
Expand All @@ -167,7 +163,7 @@ namespace ironphoenix {
}

if (pos.givesCheck(move))
score += 10'000'000;
score += tuning.checkMoveBonus;

return score;
}
Expand Down Expand Up @@ -413,6 +409,7 @@ namespace ironphoenix {
}

const bool inCheckNode = pos.inCheck();
const auto& tuning = Tuning::search();

MoveList moves;
generatePseudoLegalMoves(pos, moves);
Expand Down Expand Up @@ -446,14 +443,14 @@ namespace ironphoenix {

if (!pvNode
&& !inCheckNode
&& depth <= SEE_PRUNE_MAX_DEPTH
&& depth <= tuning.seePruneMaxDepth
&& legalMoves > 0
&& move.isCapture()
&& !move.isPromotion()
&& move != ttMove
&& !givesCheckMove
&& !mateWindow) {
const int seeThreshold = -SEE_PRUNE_MARGIN_PER_DEPTH * depth;
const int seeThreshold = -tuning.seePruneMarginPerDepth * depth;
if (!seeGE(pos, move, seeThreshold, SeeMode::Legal))
continue;
}
Expand Down Expand Up @@ -654,6 +651,7 @@ namespace ironphoenix {
return;
}

const auto& tuning = Tuning::search();
const int requestedDepth = limits_.depth > 0 ? std::min(limits_.depth, MAX_PLY - 1) : 0;
int depth = 1;

Expand All @@ -669,7 +667,7 @@ namespace ironphoenix {

int score = 0;

if (depth < ASPIRATION_START_DEPTH ||
if (depth < tuning.aspirationStartDepth ||
bestScore >= MATE_THRESHOLD ||
bestScore <= -MATE_THRESHOLD)
{
Expand All @@ -686,7 +684,7 @@ namespace ironphoenix {
}
else
{
int delta = ASPIRATION_INITIAL_DELTA;
int delta = tuning.aspirationInitialDelta;

int alpha = std::max(-INF, bestScore - delta);
int beta = std::min(INF, bestScore + delta);
Expand Down
Loading
Loading