Skip to content
Draft
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
8 changes: 8 additions & 0 deletions include/ironphoenix/tuning.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,14 @@ namespace ironphoenix {
int bishopMobility = 4;
int rookMobility = 2;
int queenMobility = 1;

// Development rewards pieces that leave their player's home back
// line. The score is tapered by remaining non-pawn material so the
// term naturally fades as the game leaves the opening.
int developmentKnight = 8;
int developmentBishop = 8;
int developmentRook = 2;
int developmentQueen = 0;
};

[[nodiscard]] SearchParameters& search() noexcept;
Expand Down
76 changes: 73 additions & 3 deletions src/eval.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,14 @@
#include "ironphoenix/position.hpp"
#include "ironphoenix/tuning.hpp"

#include <algorithm>

namespace ironphoenix::Eval {

namespace {

constexpr int STARTING_NON_PAWN_MATERIAL = 13'600;

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

Expand All @@ -34,11 +38,33 @@ namespace ironphoenix::Eval {
}
}

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

switch (pt) {
case KNIGHT: return params.developmentKnight;
case BISHOP: return params.developmentBishop;
case ROOK: return params.developmentRook;
case QUEEN: return params.developmentQueen;
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);
return pos.occupancy(c) | pos.occupancy(partner);
}

IRONPHOENIX_FORCE_INLINE bool onHomeBackLine(Color c, Square sq) noexcept {
switch (c) {
case RED: return Geometry::rankOf(sq) == 0;
case BLUE: return Geometry::fileOf(sq) == 0;
case YELLOW: return Geometry::rankOf(sq) == BOARD_RANKS - 1;
case GREEN: return Geometry::fileOf(sq) == BOARD_FILES - 1;
}
return false;
}

int materialForColor(const Position& pos, Color c) noexcept {
int material = 0;

Expand Down Expand Up @@ -93,18 +119,62 @@ namespace ironphoenix::Eval {
return score;
}

int playerScore(const Position& pos, Color c) noexcept {
int remainingNonPawnMaterial(const Position& pos) noexcept {
int material = 0;

for (unsigned ci = 0; ci < COLOR_NB; ++ci) {
const Color c = static_cast<Color>(ci);
for (unsigned pt = KNIGHT; pt <= QUEEN; ++pt) {
const PieceType pieceType = static_cast<PieceType>(pt);
material += pos.pieces(c, pieceType).popcount() * pieceValue(pieceType);
}
}

// Promotions can take the board above starting material. Development
// should never grow beyond its full opening weight.
return std::min(material, STARTING_NON_PAWN_MATERIAL);
}

int developmentForColor(const Position& pos, Color c, int phaseMaterial) noexcept {
int score = 0;

// Development is position based rather than move-history based: a
// surviving piece earns its bonus while it is off the player's home
// back line. A piece that is captured cannot continue earning it.
for (unsigned pt = KNIGHT; pt <= QUEEN; ++pt) {
const PieceType pieceType = static_cast<PieceType>(pt);
const int weight = developmentWeight(pieceType);
if (weight == 0)
continue;

Bitboard pieces = pos.pieces(c, pieceType);
while (pieces) {
const Square sq = popLsb(pieces);
if (!onHomeBackLine(c, sq))
score += weight;
}
}

// Taper toward zero as non-pawn material disappears. This keeps the
// term focused on opening development instead of rewarding arbitrary
// piece placement in simplified middlegames and endgames.
return score * phaseMaterial / STARTING_NON_PAWN_MATERIAL;
}

int playerScore(const Position& pos, Color c, int phaseMaterial) noexcept {
return materialForColor(pos, c)
+ mobilityForColor(pos, c);
+ mobilityForColor(pos, c)
+ developmentForColor(pos, c, phaseMaterial);
}

int handcraftedEvaluate(const Position& pos) noexcept {
int team0 = 0;
int team1 = 0;
const int phaseMaterial = remainingNonPawnMaterial(pos);

for (unsigned ci = 0; ci < COLOR_NB; ++ci) {
const Color c = static_cast<Color>(ci);
const int score = playerScore(pos, c);
const int score = playerScore(pos, c, phaseMaterial);

if (teamOf(c) == 0)
team0 += score;
Expand Down
24 changes: 22 additions & 2 deletions src/tuning.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,11 @@ namespace ironphoenix::Tuning {
|| name == "EvalKnightMobility"
|| name == "EvalBishopMobility"
|| name == "EvalRookMobility"
|| name == "EvalQueenMobility";
|| name == "EvalQueenMobility"
|| name == "EvalDevelopmentKnight"
|| name == "EvalDevelopmentBishop"
|| name == "EvalDevelopmentRook"
|| name == "EvalDevelopmentQueen";
}

bool isLmrParameter(std::string_view name) noexcept {
Expand Down Expand Up @@ -155,6 +159,14 @@ namespace ironphoenix::Tuning {
p.rookMobility = value;
else if (name == "EvalQueenMobility" && inRange(value, -100, 100))
p.queenMobility = value;
else if (name == "EvalDevelopmentKnight" && inRange(value, -100, 100))
p.developmentKnight = value;
else if (name == "EvalDevelopmentBishop" && inRange(value, -100, 100))
p.developmentBishop = value;
else if (name == "EvalDevelopmentRook" && inRange(value, -100, 100))
p.developmentRook = value;
else if (name == "EvalDevelopmentQueen" && inRange(value, -100, 100))
p.developmentQueen = value;
else
return false;

Expand Down Expand Up @@ -228,6 +240,10 @@ namespace ironphoenix::Tuning {
<< "option name EvalBishopMobility type spin default 4 min -100 max 100\n"
<< "option name EvalRookMobility type spin default 2 min -100 max 100\n"
<< "option name EvalQueenMobility type spin default 1 min -100 max 100\n"
<< "option name EvalDevelopmentKnight type spin default 8 min -100 max 100\n"
<< "option name EvalDevelopmentBishop type spin default 8 min -100 max 100\n"
<< "option name EvalDevelopmentRook type spin default 2 min -100 max 100\n"
<< "option name EvalDevelopmentQueen type spin default 0 min -100 max 100\n"
<< "option name ResetTuning type button\n";
}

Expand Down Expand Up @@ -256,7 +272,11 @@ namespace ironphoenix::Tuning {
<< " EvalKnightMobility=" << e.knightMobility
<< " EvalBishopMobility=" << e.bishopMobility
<< " EvalRookMobility=" << e.rookMobility
<< " EvalQueenMobility=" << e.queenMobility << '\n';
<< " EvalQueenMobility=" << e.queenMobility
<< " EvalDevelopmentKnight=" << e.developmentKnight
<< " EvalDevelopmentBishop=" << e.developmentBishop
<< " EvalDevelopmentRook=" << e.developmentRook
<< " EvalDevelopmentQueen=" << e.developmentQueen << '\n';
}

} // namespace ironphoenix::Tuning
Loading