Skip to content

Commit

Permalink
- VALUE_MAX_EVALの値を上方修正
Browse files Browse the repository at this point in the history
- VALUE_SUPERIORの値を上方修正
- VALUE_MIN_EVAL追加。
- 1手詰めのスコア、置換表に書き出すときにvalue_to_tt()していたの修正。
  • Loading branch information
yaneurao committed Oct 17, 2024
1 parent 9e5cba8 commit e9b205c
Show file tree
Hide file tree
Showing 2 changed files with 90 additions and 58 deletions.
106 changes: 62 additions & 44 deletions source/engine/yaneuraou-engine/yaneuraou-search.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1908,32 +1908,32 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo
// 1手詰めは次のnodeで詰むという解釈
bestValue = mate_in(ss->ply + 1);

ASSERT_LV3(pos.legal_promote(move));
ttWriter.write(posKey, value_to_tt(bestValue, ss->ply), ss->ttPv, BOUND_EXACT,
std::min(MAX_PLY - 1, depth + 6), move, VALUE_NONE, TT.generation());
ASSERT_LV3(pos.legal_promote(move));
ttWriter.write(posKey, /*value_to_tt(bestValue, ss->ply)*/ bestValue , ss->ttPv, BOUND_EXACT,
std::min(MAX_PLY - 1, depth + 6), move, VALUE_NONE , TT.generation());

// ■ 【計測資料 39.】 mate1plyの指し手を見つけた時に置換表の指し手でbeta cutする時と同じ処理をする。
// ■ 【計測資料 39.】 mate1plyの指し手を見つけた時に置換表の指し手でbeta cutする時と同じ処理をする。

// 兄弟局面でこのmateの指し手がよい指し手ある可能性があるので
// ここでttMoveでbeta cutする時と同様の処理を行うと短い時間ではわずかに強くなるっぽいのだが
// 長い時間で計測できる差ではなかったので削除。
// 兄弟局面でこのmateの指し手がよい指し手ある可能性があるので
// ここでttMoveでbeta cutする時と同様の処理を行うと短い時間ではわずかに強くなるっぽいのだが
// 長い時間で計測できる差ではなかったので削除。

/*
1手詰めを発見した時に、save()でdepthをどのように設定すべきか問題について。
/*
1手詰めを発見した時に、save()でdepthをどのように設定すべきか問題について。
即詰みは絶対であり、MAX_PLYの深さで探索した時の結果と同じであるから、
以前はMAX_PLYにしていたのだが、よく考えたら、即詰みがあるなら上位ノードで
枝刈りが発生してこのノードにはほぼ再訪問しないと考えられるのでこんなものが
置換表に残っている価値に乏しく、また、MAX_PLYにしてしまうと、
TTEntryのreplacement strategy上、depthが大きなTTEntryはかなりの優先度になり
いつまでもreplacementされない。
即詰みは絶対であり、MAX_PLYの深さで探索した時の結果と同じであるから、
以前はMAX_PLYにしていたのだが、よく考えたら、即詰みがあるなら上位ノードで
枝刈りが発生してこのノードにはほぼ再訪問しないと考えられるのでこんなものが
置換表に残っている価値に乏しく、また、MAX_PLYにしてしまうと、
TTEntryのreplacement strategy上、depthが大きなTTEntryはかなりの優先度になり
いつまでもreplacementされない。
こんな情報、lostしたところで1手詰めならmate1ply()で一手も進めずに得られる情報であり、
最優先にreplaceすべきTTEntryにも関わらずである。
こんな情報、lostしたところで1手詰めならmate1ply()で一手も進めずに得られる情報であり、
最優先にreplaceすべきTTEntryにも関わらずである。
かと言ってDEPTH_NONEにするとtt->depth()が 0 になってしまい、枝刈りがされなくなる。
そこで、depth + 6 ぐらいがベストであるようだ。
*/
かと言ってDEPTH_NONEにするとtt->depth()が 0 になってしまい、枝刈りがされなくなる。
そこで、depth + 6 ぐらいがベストであるようだ。
*/

return bestValue;
}
Expand All @@ -1947,8 +1947,8 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo
bestValue = mate_in(ss->ply + PARAM_WEAK_MATE_PLY);

ASSERT_LV3(pos.legal_promote(move));
ttWriter.write(posKey, value_to_tt(bestValue, ss->ply), ss->ttPv, BOUND_EXACT,
std::min(MAX_PLY - 1, depth + 8), move, VALUE_NONE, TT.generation());
ttWriter.write(posKey, bestValue, ss->ttPv, BOUND_EXACT,
std::min(MAX_PLY - 1, depth + 6), move, VALUE_NONE, TT.generation());

return bestValue;
}
Expand All @@ -1960,6 +1960,7 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo

// -----------------------
// Step 6. Static evaluation of the position
// Step 6. 局面の静的な評価
// -----------------------

// 局面の静的な評価
Expand All @@ -1977,16 +1978,21 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo
}
else if (excludedMove)
{
// Providing the hint that this node's accumulator will be used often brings significant Elo gain (~13 Elo)
// Providing the hint that this node's accumulator will be used often
// brings significant Elo gain (~13 Elo).

//Eval::NNUE::hint_common_parent_position(pos);
// このノードのアキュムレータが頻繁に使用されることを示唆するヒントを提供することで、
// かなりのElo向上(約13 Elo)が得られる。

// Eval::NNUE::hint_common_parent_position(pos, networks[numaAccessToken], refreshTable);
// TODO : → 今回のNNUEの計算は端折れるのか?

#if !defined(EXCLUDED_MOVE_FORCE_EVALUATE)
eval = ss->staticEval;
#else
eval = ss->staticEval = evaluate(pos);
#endif
// また、excludedMoveがあるときは、この局面の情報をTTに保存してはならない。
}
else if (ss->ttHit)
{
Expand Down Expand Up @@ -2146,7 +2152,8 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo
// ※ 統計値(mainHistoryとかstatScoreとか)のしきい値に関しては、やねうら王ではStockfishから調整しないことにしているので、
// 上のif式に出てくる定数については調整しないことにする。

return beta > VALUE_TB_LOSS_IN_MAX_PLY ? (eval + beta) / 2 : eval;
return beta >= VALUE_MIN_EVAL // > VALUE_TB_LOSS_IN_MAX_PLY
? (eval + beta) / 2 : eval;

// 次のようにするより、単にevalを返したほうが良いらしい。
// return eval - futility_margin(depth);
Expand All @@ -2169,7 +2176,7 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo
&& !excludedMove
// && pos.non_pawn_material(us) // これ終盤かどうかを意味する。将棋でもこれに相当する条件が必要かも。
&& ss->ply >= thisThread->nmpMinPly
&& beta > VALUE_TB_LOSS_IN_MAX_PLY
&& beta >= VALUE_MIN_EVAL // > VALUE_TB_LOSS_IN_MAX_PLY
// 同じ手番側に連続してnull moveを適用しない
)
{
Expand Down Expand Up @@ -2197,7 +2204,9 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo

// Do not return unproven mate or TB scores
// 証明されていないmate scoreやTB scoreはreturnで返さない。
if (nullValue >= beta && nullValue < VALUE_TB_WIN_IN_MAX_PLY)
if (nullValue >= beta
&& nullValue <= VALUE_MAX_EVAL // < VALUE_TB_WIN_IN_MAX_PLY
)
{
// 1手パスしてもbetaを上回りそうであることがわかったので
// これをもう少しちゃんと検証しなおす。
Expand Down Expand Up @@ -2272,7 +2281,7 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo

if ( !PvNode
&& depth > 3
&& std::abs(beta) < VALUE_TB_WIN_IN_MAX_PLY
&& std::abs(beta) <= VALUE_MAX_EVAL // < VALUE_TB_WIN_IN_MAX_PLY

// If value from transposition table is lower than probCutBeta, don't attempt probCut
// there and in further interactions with transposition table cutoff depth is set to depth - 3
Expand Down Expand Up @@ -2341,8 +2350,9 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo
ttWriter.write(posKey, value_to_tt(value, ss->ply),
ss->ttPv, BOUND_LOWER, depth - 3, move, ss->staticEval,TT.generation());

return std::abs(value) < VALUE_TB_WIN_IN_MAX_PLY ? value - (probCutBeta - beta)
: value;
return std::abs(value) <= VALUE_MAX_EVAL /* VALUE_TB_WIN_IN_MAX_PLY */
? value - (probCutBeta - beta)
: value;
}
}

Expand Down Expand Up @@ -2374,8 +2384,8 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo
&& (ttData.bound & BOUND_LOWER)
&& ttData.depth >= depth - 4
&& ttData.value >= probCutBeta
&& std::abs(ttData.value) < VALUE_TB_WIN_IN_MAX_PLY
&& std::abs(beta) < VALUE_TB_WIN_IN_MAX_PLY
&& std::abs(ttData.value) <= VALUE_MAX_EVAL // < VALUE_TB_WIN_IN_MAX_PLY
&& std::abs(beta) <= VALUE_MAX_EVAL // < VALUE_TB_WIN_IN_MAX_PLY
)
return probCutBeta;

Expand Down Expand Up @@ -2502,7 +2512,8 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo
if ( !rootNode
// 【計測資料 7.】 浅い深さでの枝刈りを行なうときに王手がかかっていないことを条件に入れる/入れない
// && pos.non_pawn_material(us) // これに相当する処理、将棋でも必要だと思う。
&& bestValue > VALUE_TB_LOSS_IN_MAX_PLY)
&& bestValue >= VALUE_MIN_EVAL // > VALUE_TB_LOSS_IN_MAX_PLY
)
{
// Skip quiet moves if movecount exceeds our FutilityMoveCount threshold (~8 Elo)
// もしmovecountがFutilityMoveCountのしきい値を超えていたなら、quietな指し手をskipする。
Expand Down Expand Up @@ -2563,8 +2574,10 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo
// Futility pruning: parent node (~13 Elo)
if (!ss->inCheck && lmrDepth < PARAM_FUTILITY_AT_PARENT_NODE_DEPTH && futilityValue <= alpha)
{
if (bestValue <= futilityValue && std::abs(bestValue) < VALUE_TB_WIN_IN_MAX_PLY
&& futilityValue < VALUE_TB_WIN_IN_MAX_PLY)
if (bestValue <= futilityValue
&& std::abs(bestValue) <= VALUE_MAX_EVAL // < VALUE_TB_WIN_IN_MAX_PLY
&& futilityValue <= VALUE_MAX_EVAL // < VALUE_TB_WIN_IN_MAX_PLY
)
bestValue = (bestValue + futilityValue * 3) / 4;
continue;
}
Expand Down Expand Up @@ -2631,7 +2644,7 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo
&& !excludedMove // 再帰的なsingular延長を除外する。
&& depth >= PARAM_SINGULAR_EXTENSION_DEPTH - (thisThread->completedDepth > 24) + ss->ttPv
/* && ttValue != VALUE_NONE Already implicit in the next condition */
&& std::abs(ttData.value) < VALUE_TB_WIN_IN_MAX_PLY // 詰み絡みのスコアはsingular extensionはしない。(Stockfish 10~)
&& std::abs(ttData.value) <= VALUE_MAX_EVAL // 詰み絡みのスコアはsingular extensionはしない。(Stockfish 10~)
&& (ttData.bound & BOUND_LOWER)
&& ttData.depth >= depth - 3)
// このnodeについてある程度調べたことが置換表によって証明されている。(ttMove == moveなのでttMove != Move::none())
Expand Down Expand Up @@ -3148,8 +3161,11 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo
ASSERT_LV5(moveCount || !ss->inCheck || excludedMove || !MoveList<LEGAL>(pos).size());

// Adjust best value for fail high cases at non-pv nodes
if (!PvNode && bestValue >= beta && std::abs(bestValue) < VALUE_TB_WIN_IN_MAX_PLY
&& std::abs(beta) < VALUE_TB_WIN_IN_MAX_PLY && std::abs(alpha) < VALUE_TB_WIN_IN_MAX_PLY)
if (!PvNode && bestValue >= beta
&& std::abs(bestValue)<= VALUE_MAX_EVAL // < VALUE_TB_WIN_IN_MAX_PLY
&& std::abs(beta) <= VALUE_MAX_EVAL // < VALUE_TB_WIN_IN_MAX_PLY
&& std::abs(alpha) <= VALUE_MAX_EVAL // < VALUE_TB_WIN_IN_MAX_PLY
)
bestValue = (bestValue * (depth + 2) + beta) / (depth + 3);

// Stockfishでは、ここのコードは以下のようになっているが、これは、
Expand Down Expand Up @@ -3526,7 +3542,7 @@ Value qsearch(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth)
// ttValue can be used as a better position evaluation (~13 Elo)
// ttValue は、より良い局面評価として使用できる(約13 Eloの向上)。

if (std::abs(ttData.value) < VALUE_TB_WIN_IN_MAX_PLY
if (std::abs(ttData.value) <= VALUE_MAX_EVAL // < VALUE_TB_WIN_IN_MAX_PLY
&& (ttData.bound & (ttData.value > bestValue ? BOUND_LOWER : BOUND_UPPER)))
bestValue = ttData.value;

Expand All @@ -3553,7 +3569,7 @@ Value qsearch(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth)
if (Mate::mate_1ply(pos) != Move::none())
{
//bestValue = mate_in(ss->ply + 1);
//tte->save(posKey, value_to_tt(bestValue, ss->ply), ss->ttPv, BOUND_EXACT,
//tte->save(posKey, bestValue , ss->ttPv, BOUND_EXACT,
// std::min(MAX_PLY - 1, depth + 6), move, unadjustedStaticEval);

// ⇨ 置換表に書き出しても得するかわからなかった。(V7.74taya-t9 vs V7.74taya-t12)
Expand Down Expand Up @@ -3612,7 +3628,7 @@ Value qsearch(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth)
if (bestValue >= beta)
{
// bestValueを少しbetaのほうに寄せる。
if (std::abs(bestValue) < VALUE_TB_WIN_IN_MAX_PLY)
if (std::abs(bestValue) <= VALUE_MAX_EVAL) // < VALUE_TB_WIN_IN_MAX_PLY)
bestValue = (3 * bestValue + beta) / 4;

if (!ss->ttHit)
Expand Down Expand Up @@ -3701,14 +3717,15 @@ Value qsearch(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth)
// 今回捕獲されるであろう駒による評価値の上昇分を
// 加算してもalpha値を超えそうにないならこの指し手は枝刈りしてしまう。

if (bestValue > VALUE_TB_LOSS_IN_MAX_PLY /* && pos.non_pawn_material(us) */)
if (bestValue >= VALUE_MIN_EVAL // > VALUE_TB_LOSS_IN_MAX_PLY /* && pos.non_pawn_material(us) */
)
{
// Futility pruning and moveCount pruning (~10 Elo)
// futility枝刈りとmove countに基づく枝刈り(約10 Eloの向上)

if ( !givesCheck
&& move.to_sq() != prevSq
&& futilityBase > VALUE_TB_LOSS_IN_MAX_PLY
&& futilityBase >= VALUE_MIN_EVAL // > VALUE_TB_LOSS_IN_MAX_PLY
// && type_of(move) != PROMOTION
// この最後の条件、入れたほうがいいのか?
// → 入れない方が良さげ。(V7.74taya-t50 VS V7.74taya-t51)
Expand Down Expand Up @@ -3882,7 +3899,8 @@ Value qsearch(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth)
// rootから詰みまでの手数。
}

if (std::abs(bestValue) < VALUE_TB_WIN_IN_MAX_PLY && bestValue >= beta)
if (std::abs(bestValue) <= VALUE_MAX_EVAL // < VALUE_TB_WIN_IN_MAX_PLY
&& bestValue >= beta)
bestValue = (3 * bestValue + beta) / 4;

// Save gathered info in transposition table. The static evaluation
Expand Down
42 changes: 28 additions & 14 deletions source/types.h
Original file line number Diff line number Diff line change
Expand Up @@ -418,6 +418,27 @@ enum Bound {
// --------------------

// 置換表に格納するときにあまりbit数が多いともったいないので値自体は16bitで収まる範囲で。
//
// ■ 注意
//
// 1. 評価値evalは、
// Stockfishでは、 VALUE_TB_LOSS_IN_MAX_PLY < eval < VALUE_TB_WIN_IN_MAX_PLY の範囲。
// やねうら王では、 VALUE_MIN_EVAL <= eval <= VALUE_MAX_EVALの範囲。(もしくは、abs(eval) <= VALUE_MAX_EVALの範囲。)
// 2. 詰みのスコアの下限値は、
// Stockfishでは、 VALUE_TB_WIN_IN_MAX_PLY
// やねうら王では、 VALUE_MAX_EVAL + 1
// 3. 詰まされスコアの上限値は、
// Stockfishでは、 VALUE_TB_LOSS_IN_MAX_PLY
// やねうら王では、 VALUE_MIN_EVAL - 1
//
// よって、
// Stockfishで value < VALUE_TB_WIN_IN_MAX_PLY のように書いてある場合、
// やねうら王では、value <= VALUE_MAX_EVAL と書き換える。
//
// Stockfishで、VALUE_TB_LOSS_IN_MAX_PLY > value のように書いてある場合、
// やねうら王では、value >= VALUE_MIN_EVAL と書き換える。
//
//
enum Value : int32_t
{
VALUE_ZERO = 0,
Expand All @@ -440,25 +461,18 @@ enum Value : int32_t

// チェスの終盤DB(tablebase)によって得られた詰みを表現するスコアらしいが、
// Stockfishとの互換性のためにこのシンボルはStockfishと同様に定義しておく。
VALUE_TB = VALUE_MATE_IN_MAX_PLY - 1,
VALUE_TB_WIN_IN_MAX_PLY = VALUE_MATE - MAX_PLY,
VALUE_TB = VALUE_MATE_IN_MAX_PLY - 1,
VALUE_TB_WIN_IN_MAX_PLY = VALUE_MATE - MAX_PLY,
VALUE_TB_LOSS_IN_MAX_PLY = -VALUE_TB_WIN_IN_MAX_PLY,


// 千日手による優等局面への突入したときのスコア
// これある程度離しておかないと、置換表に書き込んで、相手番から見て、これから
// singularの判定なんかをしようと思ったときに
// -VALUE_KNOWN_WIN - margin が、VALUE_MATED_IN_MAX_PLYを下回るとまずいので…。
VALUE_SUPERIOR = 28000,

// 評価関数の返す値の最大値(2**14ぐらいに収まっていて欲しいところだが..)
//
// ■ 注意
//
// やねうら王では、評価値evalは、abs(eval) <= VALUE_MAX_EVALの範囲。
// Stockfishでは、VALUE_TB_LOSS_IN_MAX_PLY < eval < VALUE_TB_WIN_IN_MAX_PLY の範囲。
// ※ これは詰みのスコアの仲間。
VALUE_SUPERIOR = VALUE_TB_WIN_IN_MAX_PLY - 1,

VALUE_MAX_EVAL = 27000,
// 評価関数の返す値の最大値、最小値
VALUE_MAX_EVAL = VALUE_SUPERIOR - 1,
VALUE_MIN_EVAL = -VALUE_MAX_EVAL,

// 評価関数がまだ呼び出されていないということを示すのに使う特殊な定数
// StateInfo::sum.p[0][0]にこの値を格納して、マーカーとするのだが、このsumのp[0][0]は、ΣBKPPの計算結果であり、
Expand Down

0 comments on commit e9b205c

Please sign in to comment.