Skip to content

Commit adeded2

Browse files
lucasartmcostalba
authored andcommitted
Symmetric King Safety: take 2
Another attempt at retiring current asymmetric king evaluation and use a much simpler symmetric one. As a good side effect we can avoid recalculating eval after a null move. Tested in no-regression mode and passed STC LLR: 2.96 (-2.94,2.94) [-3.00,1.00] Total: 21580 W: 3752 L: 3632 D: 14196 LTC LLR: 2.96 (-2.94,2.94) [-3.00,1.00] Total: 18253 W: 2593 L: 2469 D: 13191 And a LTC regression test against SF DD to verify we don't have regression against weaker engines due to some kind of 'contempt' effect: ELO: 54.69 +-2.1 (95%) LOS: 100.0% Total: 40000 W: 11072 L: 4827 D: 24101 bench: 8205159
1 parent 187a9fe commit adeded2

File tree

4 files changed

+36
-47
lines changed

4 files changed

+36
-47
lines changed

src/evaluate.cpp

Lines changed: 16 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -90,9 +90,9 @@ namespace {
9090
}
9191

9292
// Evaluation weights, indexed by evaluation term
93-
enum { Mobility, PawnStructure, PassedPawns, Space, KingDangerUs, KingDangerThem };
93+
enum { Mobility, PawnStructure, PassedPawns, Space, KingSafety };
9494
const struct Weight { int mg, eg; } Weights[] = {
95-
{289, 344}, {233, 201}, {221, 273}, {46, 0}, {271, 0}, {307, 0}
95+
{289, 344}, {233, 201}, {221, 273}, {46, 0}, {318, 0}
9696
};
9797

9898
typedef Value V;
@@ -150,12 +150,11 @@ namespace {
150150
S(0, 0), S(0, 0), S(56, 70), S(56, 70), S(76, 99), S(86, 118)
151151
};
152152

153-
// Hanging[side to move] contains a bonus for each enemy hanging piece
154-
const Score Hanging[2] = { S(23, 20) , S(35, 45) };
153+
// Hanging contains a bonus for each enemy hanging piece
154+
const Score Hanging = S(23, 20);
155155

156156
#undef S
157157

158-
const Score Tempo = make_score(24, 11);
159158
const Score RookOnPawn = make_score(10, 28);
160159
const Score RookOpenFile = make_score(43, 21);
161160
const Score RookSemiopenFile = make_score(19, 10);
@@ -194,9 +193,9 @@ namespace {
194193
const int BishopCheck = 2;
195194
const int KnightCheck = 3;
196195

197-
// KingDanger[Color][attackUnits] contains the actual king danger weighted
198-
// scores, indexed by color and by a calculated integer number.
199-
Score KingDanger[COLOR_NB][128];
196+
// KingDanger[attackUnits] contains the actual king danger weighted
197+
// scores, indexed by a calculated integer number.
198+
Score KingDanger[128];
200199

201200

202201
// apply_weight() weighs score 'v' by weight 'w' trying to prevent overflow
@@ -426,9 +425,7 @@ namespace {
426425
| ei.attackedBy[Them][BISHOP] | ei.attackedBy[Them][ROOK]);
427426

428427
if (b)
429-
attackUnits += QueenContactCheck
430-
* popcount<Max15>(b)
431-
* (Them == pos.side_to_move() ? 2 : 1);
428+
attackUnits += QueenContactCheck * popcount<Max15>(b);
432429
}
433430

434431
// Analyse the enemy's safe rook contact checks. Firstly, find the
@@ -446,9 +443,7 @@ namespace {
446443
| ei.attackedBy[Them][BISHOP] | ei.attackedBy[Them][QUEEN]);
447444

448445
if (b)
449-
attackUnits += RookContactCheck
450-
* popcount<Max15>(b)
451-
* (Them == pos.side_to_move() ? 2 : 1);
446+
attackUnits += RookContactCheck * popcount<Max15>(b);
452447
}
453448

454449
// Analyse the enemy's safe distance checks for sliders and knights
@@ -482,7 +477,7 @@ namespace {
482477

483478
// Finally, extract the king danger score from the KingDanger[]
484479
// array and subtract the score from evaluation.
485-
score -= KingDanger[Us == Search::RootColor][attackUnits];
480+
score -= KingDanger[attackUnits];
486481
}
487482

488483
if (Trace)
@@ -521,8 +516,7 @@ namespace {
521516

522517
b = weakEnemies & ~ei.attackedBy[Them][ALL_PIECES];
523518
if (b)
524-
score += more_than_one(b) ? Hanging[Us != pos.side_to_move()] * popcount<Max15>(b)
525-
: Hanging[Us == pos.side_to_move()];
519+
score += more_than_one(b) ? Hanging * popcount<Max15>(b) : Hanging;
526520
}
527521

528522
if (Trace)
@@ -677,9 +671,9 @@ namespace {
677671
Thread* thisThread = pos.this_thread();
678672

679673
// Initialize score by reading the incrementally updated scores included
680-
// in the position object (material + piece square tables) and adding a
681-
// Tempo bonus. Score is computed from the point of view of white.
682-
score = pos.psq_score() + (pos.side_to_move() == WHITE ? Tempo : -Tempo);
674+
// in the position object (material + piece square tables).
675+
// Score is computed from the point of view of white.
676+
score = pos.psq_score();
683677

684678
// Probe the material hash table
685679
ei.mi = Material::probe(pos, thisThread->materialTable, thisThread->endgames);
@@ -831,7 +825,7 @@ namespace {
831825
<< " | MG EG | MG EG | MG EG \n"
832826
<< "---------------------+-------------+-------------+-------------\n";
833827

834-
format_row(ss, "Material, PST, Tempo", PST);
828+
format_row(ss, "Material, PST", PST);
835829
format_row(ss, "Material imbalance", IMBALANCE);
836830
format_row(ss, "Pawns", PAWN);
837831
format_row(ss, "Knights", KNIGHT);
@@ -885,9 +879,7 @@ namespace Eval {
885879
for (int t = 0, i = 1; i < 100; ++i)
886880
{
887881
t = int(std::min(Peak, std::min(0.4 * i * i, t + MaxSlope)));
888-
889-
KingDanger[1][i] = apply_weight(make_score(t, 0), Weights[KingDangerUs]);
890-
KingDanger[0][i] = apply_weight(make_score(t, 0), Weights[KingDangerThem]);
882+
KingDanger[i] = apply_weight(make_score(t, 0), Weights[KingSafety]);
891883
}
892884
}
893885

src/search.cpp

Lines changed: 17 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,6 @@ namespace Search {
4242
LimitsType Limits;
4343
std::vector<RootMove> RootMoves;
4444
Position RootPos;
45-
Color RootColor;
4645
Time::point SearchTime;
4746
StateStackPtr SetupStates;
4847
}
@@ -77,6 +76,9 @@ namespace {
7776
return (Depth) Reductions[PvNode][i][std::min(int(d) / ONE_PLY, 63)][std::min(mn, 63)];
7877
}
7978

79+
// Tempo bonus. Must be handled by search to preserve eval symmetry.
80+
const int Tempo = 17;
81+
8082
size_t MultiPV, PVIdx;
8183
TimeManager TimeMgr;
8284
double BestMoveChanges;
@@ -180,12 +182,11 @@ uint64_t Search::perft(Position& pos, Depth depth) {
180182

181183
void Search::think() {
182184

183-
RootColor = RootPos.side_to_move();
184-
TimeMgr.init(Limits, RootPos.game_ply(), RootColor);
185+
TimeMgr.init(Limits, RootPos.game_ply(), RootPos.side_to_move());
185186

186187
int cf = Options["Contempt Factor"] * PawnValueEg / 100; // From centipawns
187-
DrawValue[ RootColor] = VALUE_DRAW - Value(cf);
188-
DrawValue[~RootColor] = VALUE_DRAW + Value(cf);
188+
DrawValue[ RootPos.side_to_move()] = VALUE_DRAW - Value(cf);
189+
DrawValue[~RootPos.side_to_move()] = VALUE_DRAW + Value(cf);
189190

190191
if (RootMoves.empty())
191192
{
@@ -203,8 +204,8 @@ void Search::think() {
203204
log << "\nSearching: " << RootPos.fen()
204205
<< "\ninfinite: " << Limits.infinite
205206
<< " ponder: " << Limits.ponder
206-
<< " time: " << Limits.time[RootColor]
207-
<< " increment: " << Limits.inc[RootColor]
207+
<< " time: " << Limits.time[RootPos.side_to_move()]
208+
<< " increment: " << Limits.inc[RootPos.side_to_move()]
208209
<< " moves to go: " << Limits.movestogo
209210
<< "\n" << std::endl;
210211
}
@@ -472,7 +473,7 @@ namespace {
472473
bestValue = -VALUE_INFINITE;
473474
ss->currentMove = ss->ttMove = (ss+1)->excludedMove = bestMove = MOVE_NONE;
474475
ss->ply = (ss-1)->ply + 1;
475-
(ss+1)->skipNullMove = false; (ss+1)->reduction = DEPTH_ZERO;
476+
(ss+1)->skipNullMove = (ss+1)->nullChild = false; (ss+1)->reduction = DEPTH_ZERO;
476477
(ss+2)->killers[0] = (ss+2)->killers[1] = MOVE_NONE;
477478

478479
// Used to send selDepth info to GUI
@@ -483,7 +484,7 @@ namespace {
483484
{
484485
// Step 2. Check for aborted search and immediate draw
485486
if (Signals.stop || pos.is_draw() || ss->ply > MAX_PLY)
486-
return ss->ply > MAX_PLY && !inCheck ? evaluate(pos) : DrawValue[pos.side_to_move()];
487+
return ss->ply > MAX_PLY && !inCheck ? evaluate(pos) + Tempo : DrawValue[pos.side_to_move()];
487488

488489
// Step 3. Mate distance pruning. Even if we mate at the next move our score
489490
// would be at best mate_in(ss->ply+1), but if alpha is already bigger because
@@ -538,7 +539,7 @@ namespace {
538539
{
539540
// Never assume anything on values stored in TT
540541
if ((ss->staticEval = eval = tte->eval_value()) == VALUE_NONE)
541-
eval = ss->staticEval = evaluate(pos);
542+
eval = ss->staticEval = evaluate(pos) + Tempo;
542543

543544
// Can ttValue be used as a better position evaluation?
544545
if (ttValue != VALUE_NONE)
@@ -547,7 +548,7 @@ namespace {
547548
}
548549
else
549550
{
550-
eval = ss->staticEval = evaluate(pos);
551+
eval = ss->staticEval = ss->nullChild ? -(ss-1)->staticEval + 2 * Tempo : evaluate(pos) + Tempo;
551552
TT.store(posKey, VALUE_NONE, BOUND_NONE, DEPTH_NONE, MOVE_NONE, ss->staticEval);
552553
}
553554

@@ -607,10 +608,10 @@ namespace {
607608
+ int(eval - beta) / PawnValueMg * ONE_PLY;
608609

609610
pos.do_null_move(st);
610-
(ss+1)->skipNullMove = true;
611+
(ss+1)->skipNullMove = (ss+1)->nullChild = true;
611612
nullValue = depth-R < ONE_PLY ? -qsearch<NonPV, false>(pos, ss+1, -beta, -beta+1, DEPTH_ZERO)
612613
: - search<NonPV, false>(pos, ss+1, -beta, -beta+1, depth-R, !cutNode);
613-
(ss+1)->skipNullMove = false;
614+
(ss+1)->skipNullMove = (ss+1)->nullChild = false;
614615
pos.undo_null_move();
615616

616617
if (nullValue >= beta)
@@ -1065,7 +1066,7 @@ namespace {
10651066

10661067
// Check for an instant draw or if the maximum ply has been reached
10671068
if (pos.is_draw() || ss->ply > MAX_PLY)
1068-
return ss->ply > MAX_PLY && !InCheck ? evaluate(pos) : DrawValue[pos.side_to_move()];
1069+
return ss->ply > MAX_PLY && !InCheck ? evaluate(pos) + Tempo : DrawValue[pos.side_to_move()];
10691070

10701071
// Decide whether or not to include checks: this fixes also the type of
10711072
// TT entry depth that we are going to use. Note that in qsearch we use
@@ -1102,15 +1103,15 @@ namespace {
11021103
{
11031104
// Never assume anything on values stored in TT
11041105
if ((ss->staticEval = bestValue = tte->eval_value()) == VALUE_NONE)
1105-
ss->staticEval = bestValue = evaluate(pos);
1106+
ss->staticEval = bestValue = evaluate(pos) + Tempo;
11061107

11071108
// Can ttValue be used as a better position evaluation?
11081109
if (ttValue != VALUE_NONE)
11091110
if (tte->bound() & (ttValue > bestValue ? BOUND_LOWER : BOUND_UPPER))
11101111
bestValue = ttValue;
11111112
}
11121113
else
1113-
ss->staticEval = bestValue = evaluate(pos);
1114+
ss->staticEval = bestValue = ss->nullChild ? -(ss-1)->staticEval + 2 * Tempo : evaluate(pos) + Tempo;
11141115

11151116
// Stand pat. Return immediately if static value is at least beta
11161117
if (bestValue >= beta)

src/search.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,8 @@ struct Stack {
4545
Move killers[2];
4646
Depth reduction;
4747
Value staticEval;
48-
int skipNullMove;
48+
bool skipNullMove;
49+
bool nullChild;
4950
};
5051

5152

@@ -101,7 +102,6 @@ extern volatile SignalsType Signals;
101102
extern LimitsType Limits;
102103
extern std::vector<RootMove> RootMoves;
103104
extern Position RootPos;
104-
extern Color RootColor;
105105
extern Time::point SearchTime;
106106
extern StateStackPtr SetupStates;
107107

src/uci.cpp

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -197,11 +197,7 @@ void UCI::loop(int argc, char* argv[]) {
197197
<< "\n" << Options
198198
<< "\nuciok" << sync_endl;
199199

200-
else if (token == "eval")
201-
{
202-
Search::RootColor = pos.side_to_move(); // Ensure it is set
203-
sync_cout << Eval::trace(pos) << sync_endl;
204-
}
200+
else if (token == "eval") sync_cout << Eval::trace(pos) << sync_endl;
205201
else if (token == "ucinewgame") TT.clear();
206202
else if (token == "go") go(pos, is);
207203
else if (token == "position") position(pos, is);

0 commit comments

Comments
 (0)