Skip to content

やねうら王の更新履歴2024

yaneurao edited this page Jun 27, 2024 · 23 revisions

TODO :

残り作業
・AVX512のバイナリ、実機で動作しない件、調査する。
・ふかうら王のvast.aiの勉強会動画をYouTubeのやねちゃんねるにアップする。
- ふかうら王、mateの手数が最大になるように逃げるように…。
	⇨ これ、あまり意味ないか…。

適用検討中のcommit
- MCP more after a bad singular search : https://github.com/official-stockfish/Stockfish/commit/b34a690cd4aa6d828ae0f47b427167f4e6392db7
- Add helpers for managing aligned memory : https://github.com/official-stockfish/Stockfish/commit/00a28ae325688346e63a452b2050bd1491085359
- Use futility margin in razoring margin : https://github.com/official-stockfish/Stockfish/commit/36eb9bc783d35842571d0d4313349b964892d9ca

2024/6/27

  • configコマンドで評価関数アーキテクチャが表示されるようにした。

2024/6/27 V8.31

  • makefile、NNUEの任意アーキテクチャのビルド対応。

2024/6/25

  • NNUE architecture、ビルド時に生成されたheader fileの読み込みに対応

  • NNUEのarchitecture headerを動的に生成するスクリプト作った。

  • k_p_256x2-32-32.hのファイル名 kp_256x2-32-32.h にrename。(入力特徴量名を'_'の左側に持ってきたいため。)

  • halfkp_vm_256x2-32-32.hもhalfkpvm_256x2-32-32.h とrename。

  • NNUEのarchitectureのheaderでifdefで囲ってないものがあったのを修正。

2024/6/24

  • GitHub Actionsのmac用、OSをmacOS-11⇨13に変更。

  • clang-18 on Ubuntu 24.04でlinkerのエラーになっていたの修正。

2024/6/16

  • makebook peta_shockコマンド、一時ファイルの書き出すpathが一貫していなかったの修正。

2024/6/12

  • Makefile、macOS用の説明が間違っていたの修正。

  • NNUEのnn.binの読み込み失敗の時に、FileNotFoundなのかFileReadErrorなのかFileMismatchなのかがわかるようにした。

    • Tools::ResultCodeにいくつか追加した。

2024/6/11

  • clang, gccで32bit環境用にSSE4.2用、AVX2用がビルドできない件、修正。

2024/6/10

  • やねうら王のインストール手順の古い説明(あとで復活させるか考える)

1) 駒得型        (ファイル名 : YaneuraOu_MaterialLv1_XXX.exe)

→ これは駒得だけを評価する思考エンジンなので、評価関数のファイルは不要です。

💡 MaterialLv2~は、以下の連載記事で作ったもので、単なる駒得の評価関数よりは強くなりますが、実行ファイルはたくさんあるので毎回は配布はしていません。

【連載】評価関数を作ってみよう!その1 : http://yaneuraou.yaneu.com/2020/11/17/make-evaluate-function/	 

5) tanuki-詰将棋エンジン(tanuki_MATE_XXX.exe)

💡 これは、詰将棋を解くの専用の思考エンジンなので評価関数のファイルは不要です。

- XXXのところには、avx2やsse42のようにCPUの種別が入ります。
- ファイル名の途中にlearnとついているものは、評価関数の学習のために用いるもので、通常対局用ではありません。

評価関数の種類に関して詳しくは以下の説明もご覧ください。
- [評価関数の種類 - やねうら王のビルド手順](やねうら王のビルド手順#評価関数の種類)

2024/6/4

やねうらお — 今日 19:21
■ Stockfishが地雷の件

Stockfishの2024年3月~6月5日現在に至るまでのcommit、やねうら王にマージして強くなる改良が一つもなさそう。これはマジで吐きそう。

せめて同じ強さが維持できるなら、マージするのだが、マージしてR3とかR5ずつ損していった結果、最終的にR25ぐらい弱いのはさすがにどうにもならない。

rollbackして半年ぐらい寝かせておくか…。😵‍💫
やねうらお — 今日 22:10
■ AVX512関係のアレ

やねうら王でAVX512用にビルドしたやつ、時々動かないとか何とか言う問題、もしかして⇓これと関係あるのかな。64 bytesでalignされてないメモリ使ってるところがどこかにある、的な..🤔 

https://github.com/official-stockfish/Stockfish/commit/00a28ae325688346e63a452b2050bd1491085359
GitHub
Add helpers for managing aligned memory · official-stockfish/Stockf...
Previously, we had two type aliases, LargePagePtr and AlignedPtr, which
required manually initializing the aligned memory for the pointer.

The new helpers:

- make_unique_aligned
- make_unique_lar...
やねうらお — 今日 22:17
やねうら王のメモリのアロケーション、これで書き換えるの、わりと大変なのだ..。
ついでに、StockfishではNUMAのアロケーションもだいぶアルゴリズムが変わったみたいなのだ。
マージしても1㍉も強くならないような改造が90%なのだ。あとの9%はマージすると弱くなる改造なのだ。残りの1%がマージすると強くなる改造なのだ。終わり。😢
やねうらお — 今日 19:25
欲を言うならStockfishを忠実にマージしたやねうら王と、強くなるcommitだけマージしたやねうら王と、2つ用意したいところではあるなー。長い時間だと前者の方が強いということはありえなくはないしなー。

でもそんなのソースコードを管理するの、めちゃめちゃ大変なんだよなー。
いまですら、GitHub版と開発版(支援者向け)と手元の開発版と3つあるのに..。

2024/5/31 - 2024/6/9 V8.30

// V8.30git

  • バージョン V8.30git 実行ファイルあとでGitHubのReleasesのところに生やす。

・バージョンナンバーをV8.30gitに変更。

engine1 = YaneuraOuNNUE1024_2024.05.28-cl4_avx2.exe , eval = tanuki20240131
engine2 = YaneuraOuNNUE1024_830y.exe , eval = tanuki20240131
T2,b1000,1061 - 135 - 1804(37.03% R-92.21[-103.26,-81.16]) winrate black , white = 51.34% , 48.66%
T2,b2000,1083 - 178 - 1739(38.38% R-82.27[-93.33,-71.21]) winrate black , white = 53.58% , 46.42%

engine1 = YaneuraOuNNUE1024_V820DEV-cl4_avx2.exe , eval = tanuki20240131
engine2 = YaneuraOuNNUE1024_830y.exe , eval = tanuki20240131
T2,b1000,1357 - 155 - 1488(47.7% R-16.01[-26.73,-5.29]) winrate black , white = 51.95% , 48.05%
T2,b2000,1328 - 231 - 1441(47.96% R-14.19[-25.05,-3.32]) winrate black , white = 52.69% , 47.31%

engine1 = YaneuraOuNNUE1024X2_8_32-V830GitHub_AVX2.exe , eval = tanuki20240131
engine2 = YaneuraOuNNUE1024_830y.exe , eval = tanuki20240131
T2,b1000,978 - 136 - 1886(34.15% R-114.08[-125.34,-102.82]) winrate black , white = 52.16% , 47.84%
T2,b2000,1005 - 203 - 1792(35.93% R-100.47[-111.73,-89.21]) winrate black , white = 53.45% , 46.55%

engine1 = YaneuraOuNNUE1024X2_8_32-V830DEV_AVX2.exe , eval = tanuki20240131
engine2 = YaneuraOuNNUE1024_830y.exe , eval = tanuki20240131
T2,b1000,1312 - 145 - 1543(45.95% R-28.17[-38.9,-17.44]) winrate black , white = 54.05% , 45.95%
T2,b2000,1266 - 213 - 1521(45.43% R-31.88[-42.75,-21.01]) winrate black , white = 50.74% , 49.26%

・Stockfishの6月1日~6月6日のcommit対応終わり。

// 830y

よさげなcommitだけ反映。

  • 830q
  • 830r
  • 830s
    • これいいかどうかわからん
  • 830t
    • これは適用済み

      engine1 = YaneuraOuNNUE_V830_1024a.exe , eval = tanuki20240131 engine2 = YaneuraOuNNUE1024_830y.exe , eval = tanuki20240131 T2,b1000,1509 - 170 - 1321(53.32% R23.11[12.35,33.88]) winrate black , white = 53.96% , 46.04% T2,b2000,1400 - 211 - 1389(50.2% R1.37[-9.45,12.19]) winrate black , white = 53.28% , 46.72% T2,b4000,1402 - 291 - 1307(51.75% R12.19[1.2,23.17]) winrate black , white = 53.3% , 46.7% ⇨ 長い持ち時間では悪くない可能性があるっぽい。

      T2,b8000,668 - 194 - 738(47.51% R-17.31[-32.57,-2.06]) winrate black , white = 53.63% , 46.37% T2,b8000,701 - 189 - 700(50.04% R0.25[-15.02,15.51]) winrate black , white = 53.18% , 46.82% T2,b8000,656 - 180 - 634(50.85% R5.93[-9.98,21.83]) winrate black , white = 53.33% , 46.67% T2,b8000,321 - 94 - 295(52.11% R14.67[-8.36,37.7]) winrate black , white = 50.49% , 49.51% ⇨ 2346-2367 -R1.5 計測不可な差なので、これは良し。

      GitHub版と強さ比較する。

// 830x

よさげなcommitだけ反映

  • V830g
    • 機能変更なし
  • V830p
    • +R10ぐらい強くなるのか?
  • V830h
    • 誤差のはず

      engine1 = YaneuraOuNNUE_V830_1024a.exe , eval = tanuki20240131 engine2 = YaneuraOuNNUE1024_830x.exe , eval = tanuki20240131 T2,b1000,1398 - 172 - 1430(49.43% R-3.93[-14.68,6.81]) winrate black , white = 51.98% , 48.02% T2,b2000,1346 - 211 - 1443(48.26% R-12.09[-22.91,-1.26]) winrate black , white = 52.85% , 47.15% T2,b4000,1350 - 306 - 1344(50.11% R0.77[-10.23,11.78]) winrate black , white = 53.45% , 46.55%

      ⇨ まあ、良し。

// 830w

よさげなcommitだけ反映。

  • V830c
  • V830d
  • V830e
    • reductionまわり、よくわからんからStockfishの最新コードから丸ごともってきて差し替えた。 ⇨ このcommit、なんかまずそう?

      engine1 = YaneuraOuNNUE_V830_1024a.exe , eval = tanuki20240131 engine2 = YaneuraOuNNUE1024_830w.exe , eval = tanuki20240131 T2,b1000,1430 - 163 - 1407(50.41% R2.82[-7.91,13.54]) winrate black , white = 50.26% , 49.74% T2,b2000,1419 - 203 - 1378(50.73% R5.09[-5.71,15.9]) winrate black , white = 51.56% , 48.44% T2,b4000,1370 - 274 - 1356(50.26% R1.78[-9.16,12.73]) winrate black , white = 52.75% , 47.25%

// 830v

あかん。ぜんぶrollbackして、弱くならなかったcommitだけを適用する。

  • 830aから。

影響なさそうなやつだけまずは反映。これでR±0のはず。

engine1 = YaneuraOuNNUE_V830_1024a.exe , eval = tanuki20240131
engine2 = YaneuraOuNNUE1024_830v.exe , eval = tanuki20240131
T2,b1000,1406 - 170 - 1424(49.68% R-2.21[-12.95,8.53]) winrate black , white = 53.0% , 47.0%
T2,b2000,1357 - 223 - 1420(48.87% R-7.88[-18.73,2.96]) winrate black , white = 52.36% , 47.64%

// 830u

  • Tweak first picked move (ttMove) reduction rule : https://github.com/official-stockfish/Stockfish/commit/de1ae4949daf2c6d36c50e51c132cee808e2ade0

    engine1 = YaneuraOuNNUE1024_830t.exe , eval = tanuki20240131 engine2 = YaneuraOuNNUE1024_830u.exe , eval = tanuki20240131 T2,b1000,1471 - 114 - 1415(50.97% R6.74[-3.9,17.38]) winrate black , white = 51.52% , 48.48% ⇨ 悪そう..

    engine1 = YaneuraOuNNUE_V830_1024a.exe , eval = tanuki20240131 engine2 = YaneuraOuNNUE1024_830u.exe , eval = tanuki20240131 T2,b1000,1529 - 136 - 1335(53.39% R23.57[12.87,34.27]) winrate black , white = 51.68% , 48.32% T2,b2000,969 - 129 - 872(52.63% R18.32[4.99,31.66]) winrate black , white = 52.91% , 47.09%

// 830t

// 830s

// 830r

// 830q

// 830p ☆

// 830o

  • Add extra bonus for high-depth condition : https://github.com/official-stockfish/Stockfish/commit/fa114266fa7ea996c6d2ef12c625547b1aefddc1

    engine1 = YaneuraOuNNUE1024_830n.exe , eval = tanuki20240131 engine2 = YaneuraOuNNUE1024_830o.exe , eval = tanuki20240131 T2,b1000,1424 - 121 - 1455(49.46% R-3.74[-14.39,6.91]) winrate black , white = 52.17% , 47.83% T2,b2000,1414 - 164 - 1422(49.86% R-0.98[-11.71,9.75]) winrate black , white = 52.26% , 47.74% T2,b4000,1255 - 205 - 1200(51.12% R7.78[-3.75,19.32]) winrate black , white = 53.08% , 46.92% ⇨ 悪いっぽい..??

    engine1 = YaneuraOuNNUE_V830_1024a.exe , eval = tanuki20240131 engine2 = YaneuraOuNNUE1024_830o.exe , eval = tanuki20240131 T2,b1000,1515 - 144 - 1341(53.05% R21.19[10.48,31.91]) winrate black , white = 52.49% , 47.51%

// 830n

  • V830lからV830iだけrollback

    engine1 = YaneuraOuNNUE_V830_1024a.exe , eval = tanuki20240131 engine2 = YaneuraOuNNUE1024_830n.exe , eval = tanuki20240131 T2,b1000,1516 - 145 - 1339(53.1% R21.57[10.85,32.28]) winrate black , white = 51.21% , 48.79% T2,b2000,1456 - 195 - 1349(51.91% R13.26[2.46,24.06]) winrate black , white = 52.69% , 47.31% T2,b4000,1058 - 195 - 977(51.99% R13.84[1.16,26.51]) winrate black , white = 52.38% , 47.62%

// 830m

  • V830lからV830kだけrollback

    PARAM_NULL_MOVE_DYNAMIC_GAMMA = 132 ⇦ 177 に戻す

      Depth R = std::min(int(eval - beta) / PARAM_NULL_MOVE_DYNAMIC_GAMMA, 6) + depth / 3 + 5;
      	⇓戻す
      Depth R = std::min(int(eval - beta) / PARAM_NULL_MOVE_DYNAMIC_GAMMA, 6) + depth / 3 + 4;
    

    engine1 = YaneuraOuNNUE_V830_1024a.exe , eval = tanuki20240131 engine2 = YaneuraOuNNUE1024_830m.exe , eval = tanuki20240131 T2,b1000,1473 - 157 - 1370(51.81% R12.59[1.87,23.32]) winrate black , white = 54.03% , 45.97% T2,b2000,1507 - 177 - 1316(53.38% R23.54[12.76,34.32]) winrate black , white = 52.6% , 47.4% T2,b4000,1023 - 169 - 868(54.1% R28.54[15.36,41.73]) winrate black , white = 54.79% , 45.21%

    ⇨ V830k、必要だったみたい。

// V830l

  • opponentWorsening導入。

  • Simplifying improving and worsening deduction formulas : https://github.com/official-stockfish/Stockfish/commit/dcb02337844d71e56df57b9a8ba17646f953711c

    engine1 = YaneuraOuNNUE1024_830k.exe , eval = tanuki20240131 engine2 = YaneuraOuNNUE1024_830l.exe , eval = tanuki20240131 T2,b1000,1467 - 132 - 1401(51.15% R8.0[-2.68,18.67]) winrate black , white = 51.64% , 48.36% T2,b2000,1363 - 185 - 1452(48.42% R-10.99[-21.76,-0.21]) winrate black , white = 52.79% , 47.21% T2,b4000,1223 - 221 - 1216(50.14% R1.0[-10.57,12.57]) winrate black , white = 52.32% , 47.68% ⇨ そんな悪くなさそう

// V830k

PARAM_NULL_MOVE_DYNAMIC_GAMMA = 132 ⇨ 177 に変更

engine1 = YaneuraOuNNUE1024_830j.exe , eval = tanuki20240131
engine2 = YaneuraOuNNUE1024_830k.exe , eval = tanuki20240131
T2,b1000,1479 - 131 - 1390(51.55% R10.78[0.11,21.45]) winrate black , white = 50.82% , 49.18%
T2,b2000,1354 - 182 - 1464(48.05% R-13.57[-24.34,-2.8]) winrate black , white = 52.27% , 47.73%
T2,b4000,1074 - 204 - 1072(50.05% R0.32[-12.01,12.66]) winrate black , white = 51.26% , 48.74%
⇨ 悪くなかった。

engine1 = YaneuraOuNNUE_V830_1024a.exe , eval = tanuki20240131
engine2 = YaneuraOuNNUE1024_830k.exe , eval = tanuki20240131
T2,b1000,1452 - 158 - 1390(51.09% R7.58[-3.14,18.3]) winrate black , white = 51.94% , 48.06%
T2,b2000,1477 - 191 - 1332(52.58% R17.95[7.16,28.75]) winrate black , white = 52.69% , 47.31%
T2,b4000,1362 - 255 - 1283(51.49% R10.38[-0.73,21.5]) winrate black , white = 53.42% , 46.58%
⇨ aに対しては強くなってなさそうなので、まずそうなのをrollbackする。

// V830j

// V830i

  • Simplify the recently introduced ply-based cmh bonus factor. : https://github.com/official-stockfish/Stockfish/commit/81e21a69f02164fd988d5636a47c8790a1174b81

    engine1 = YaneuraOuNNUE1024_830h.exe , eval = tanuki20240131 engine2 = YaneuraOuNNUE1024_830i.exe , eval = tanuki20240131 T2,b1000,1446 - 168 - 1386(51.06% R7.36[-3.38,18.1]) winrate black , white = 52.75% , 47.25% T2,b2000,1436 - 213 - 1351(51.52% R10.6[-0.23,21.43]) winrate black , white = 51.67% , 48.33% ⇨ 悪い..bonusまわり、そこだけいじると悪くなるんだよなー。

// V830h

// V830g

// V830f

  • Allow tt cutoffs for shallower depths in certain conditions : https://github.com/official-stockfish/Stockfish/commit/b280d2f06553e8c8d98379fe547f3b995cc56d59

    engine1 = YaneuraOuNNUE_V830_1024e.exe , eval = tanuki20240131 engine2 = YaneuraOuNNUE_V830_1024f.exe , eval = tanuki20240131 T2,b1000,1460 - 156 - 1384(51.34% R9.29[-1.43,20.0]) winrate black , white = 52.32% , 47.68% T2,b2000,1383 - 247 - 1370(50.24% R1.64[-9.25,12.53]) winrate black , white = 52.05% , 47.95% T2,b4000,1408 - 239 - 1353(51.0% R6.92[-3.95,17.8]) winrate black , white = 53.21% , 46.79% ⇨ 誤差? ⇨ 気分悪いので適用せんとこ。

// V830e

  • Addition of new scaling comments : https://github.com/official-stockfish/Stockfish/commit/6db47ed71aac3b1667dd68a08c39bfde0fe0a2ab engine1 = YaneuraOuNNUE_V830_1024d.exe , eval = tanuki20240131 engine2 = YaneuraOuNNUE_V830_1024e.exe , eval = tanuki20240131 T2,b1000,1442 - 152 - 1406(50.63% R4.39[-6.32,15.1]) winrate black , white = 51.23% , 48.77% T2,b2000,1405 - 207 - 1388(50.3% R2.11[-8.7,12.93]) winrate black , white = 53.42% , 46.58% ⇨ 誤差なので採用する。

// V830d

// V830c

#if defined(__INTEL_COMPILER)
	// 最適化でprefetch命令を削除するのを回避するhack。MSVCとgccは問題ない。
	__asm__("");
#endif

⇨ __INTEL_COMPILER ⇨ これ対象外になったんか?滅んだのか? ⇨ 滅んだわけではなさそう。いいや、これはこれで。

// V830b

// 現時点のものをV830aとする

やねうらお — 今日 14:23 ■ やねうら王の実行ファイル多すぎ問題

やねうら王、CPUごとに実行ファイル分かれてる上に、評価関数ごとにも分かれてて、これはなんとかしないと非常にまずい状態。(評価関数 主要なのが6個とCPUアーキテクチャが主要なの8個ほどある。これだけで48個も実行ファイルがある。)

⇨ CPU自動判別してかつ評価関数に応じた実行ファイルを起動してくれるか、差し替えてくれるか何かしてくれる何かを用意すべき。

■ 来週の予定 ・M2 Mac mini買う ・M1以降、NPU(Neural Processing Unit)がついてるらしいのでふかうら王を動かす ・Electron将棋 + Mac用やねうら王/ふかうら王 + モデルファイル の 3つをバンドルして配布する(やねうら王支援者向け?)

  • WikiトップページとGitHubのトップページ調整。

2024/5/30

  • clangでevallearn版のビルドで計測がでる件、修正。その3。

learn/learner.cpp:1512:15: warning: use of infinity is undefined behavior due to the currently enabl 1512 | best_loss = std::numeric_limits::infinity(); | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

            if (latest_loss < best_loss) {
                cout << " < best (" << best_loss << "), accepted" << endl;
                best_loss = latest_loss;
                best_nn_directory = Path::Combine((std::string)Options["EvalSaveDir"], dir_name);
                trials = newbob_num_trials;
            } else {

のようにbest_lossは、lossの最小値を記録しておくための変数で、infinityで初期化しているのは単にある程度大きな値で初期化しておけば、実際のlossはそれよりは小さな値であるから、うまく更新されるだろうという感じのコードでございました。(lastest_lossがINFになることは普通はないと思うので..)

そんなわけで、best_lossの初期値は別に99999とかその程度でも問題なくて、 best_loss = std::numeric_limits::max(); に変更しようと思いました。🙆

  • workflowからg++10,g++11を削除。g++12,g++13を追加。clang 11~14を削除。clang 15を残して、clang 18を追加。

  • makefile macos対応。

  • evallearnを指定してClangでコンパイルしたときにwarningが出ていたの修正。その2。

  • evallearnを指定してClangでコンパイルしたときにwarningが出ていたの修正。

2024/5/28

  • MSYS2+clang18でビルドした実行ファイルが落ちる件に対応
    • std::chrono::high_resolution_clock::now()を呼び出すとセグフォになる。 ⇨ 代わりにsteady_clockを用いるようにした。 std::chrono::steady_clock::now()

      note: https://github.com/yaneurao/YaneuraOu/issues/278

      ビルド時にstdlibをlibc++に指定すると実行可能なのでlibstdc++に問題があるということなのでしょうか。

2024/5/14

  • eval_learn版、version文字列が数字以外だとコンパイルエラーになるの修正。

2024/5/9 V8.30

  • GitHubのreleasesのところからWindows用の実行ファイルをdownloadできるようにした。

  • ふかうら王、最大GPUの数を設定できるように。

    • コンパイルオプションでMAX_GPU=32のように指定できるようにした。

2024/5/7

  • YANEURAOU_ENGINE_NNUE_HALFKP_1024X2_8_64 : NNUE型評価関数(halfkp_1024x2-8-64) 対応
    • このアーキテクチャが使われているのは、tanuki-wcsc34の評価関数

2024/5/2

  • PV mate拡張する。上位4手ずつBFSで調べる。
PV_Mate_Search_Threads 2
UCT_Threads1 2
isready
usinewgame
position startpos moves 7g7f 8c8d 6i7h 9c9d 9g9f 8d8e 8h7g 6c6d 7i6h 4a3b 1g1f 1c1d 2g2f 3c3d 3i3h 3a4b 7g2b+ 3b2b 6h7g 7a6b 2f2e 4b3c 3g3f 6a5b 5i6h 7c7d 4g4f 2b3b 2i3g 5a4b 3h4g 8a7c 4i4h 6b6c 2h2i 5b6b 4g5f 8b8a 6g6f 6c5d 6h7i 5d6c 7i8h 6c5d 5f6g 5d5e 3f3e 3d3e 3g4e 3c4d 2e2d 2c2d 6g5f 5e4f 2i2d P*2c 2d2f 3e3f P*3c 2a3c 4e3c+ 3b3c 2f3f 4d3e 3f3h P*3f P*4g 3f3g+ 4h3g 4f5g+ P*5d 5c5d N*4f 3e4f 3g4f P*3g 3h2h N*8f 8g8f 8e8f 7g8f 8a8f P*8g 8f7f S*7g 7f7e N*4e 3c3b B*8f N*6c 8f7e 6c7e R*2a 4c4d 2a9a+ 4d4e L*4d 4b3c 4f4e B*6i P*7f N*3f 2h1h 5g5f 7f7e S*3d N*2e 3d2e 9a1a 5f5e 4e5e 5d5e 1a4a P*8f L*3e N*3d 7g8f P*4h 7h6h 6i4g+ 3e3d 2e3d S*4c 3b4c 4d4c+ 3d4c G*3e S*3d P*4d 4c5b 4a3a L*3b 3e3d 3c3d 3a3b 3d4e S*5h B*7f 5h4g 7f3b L*4f 4e5d 4d4c+ 5b4c 7e7d P*8e 7d7c+ 8e8f 7c6b 8f8g+ 8h8g P*8f 8g8f S*7d B*7b 5d5c 6b6c 7d6c N*4e 5c6b 7b6c+ 6b6c N*7e 6c5d S*6c 5d4d 4g3f G*3e 4e5c+ 3e4f 1h4h P*4g 5c4c 4d4c 3f3e P*8e 8f8e L*8a P*8b R*8h N*8f S*7c P*4d 4c3c G*4c 3b4c 4d4c+ 3c4c S*9c 8a8b P*8d L*8a 7e8c+ 8b8c B*6a 4c3b 8d8c+ N*7a P*3c 3b2b 6a7b+ 8a8c P*8d 8c8d 9c8d 7c8d 8e8d 8h8f+ L*8e N*8a 3c3b+
go btime 6000 wtime 9000 binc 1000 winc 1000
  • PVのmove_count >= 1000の時だけfpu_reductionをrootと同じにする。
		// rootNodeであるか?
		const bool root_or_pv = parent == nullptr || (pv && current->move_count >= 1000);
  • PVではfpu_reductionをrootと同じにする。

    PV mate fpu VS PV mate 1m1f 21-9-47 ⇨ 大敗した。

  • ふかうら王、leaf nodeで不詰が証明されていたら詰みチェックを端折るようにした。

    • CPU負荷、あまり変わらない。ちょっと無駄だったかも…。
		// ループの外でsqrtしておく。
		// sumは、double型で計算しているとき、sqrt()してからfloatにしたほうがいいか?
		const float sqrt_sum = sqrtf((float)sum);

		float c, fpu_reduction, parent_q, init_u;
		if (pv && !root)
		{
			// rootからの手数で按分する。



		}
		else {
			c = root ?
				FastLog((sum + options.c_base_root + 1.0f) / options.c_base_root) + options.c_init_root :
				FastLog((sum + options.c_base + 1.0f) / options.c_base) + options.c_init;
			fpu_reduction = (root ? options.c_fpu_reduction_root : options.c_fpu_reduction) * sqrtf(current->visited_nnrate);
			parent_q = sum_win > 0 ? std::max(0.0f, (float)(sum_win / sum) - fpu_reduction) : 0.0f;
			init_u = sum == 0 ? 1.0f : sqrt_sum;
		}

2024/4/30

  • ふかうら王にPV lineの詰み探索導入。
    • エンジンオプション追加。
      • PV_Mate_Search_Threads : PV lineの詰み探索のスレッド数
      • PV_Mate_Search_Nodes : PV lineの詰み探索の1局面の最大ノード数
  • ふかうら王、rootのdf-pnのmateのコード削除。
    • エンジンオプション削除
      • RootMateSearchNodesLimitオプション削除
    • Rootでdf-pnを呼び出していたコード掃除した。
  • Mate::Dfpn64 Thread.stopを見るのではなく、stopフラグを持つように変更。
PV_Mate_Search_Threads 1
UCT_Threads1 2
isready
usinewgame
position startpos moves 7g7f 8c8d 6i7h 9c9d 9g9f 8d8e 8h7g 6c6d 7i6h 4a3b 1g1f 1c1d 2g2f 3c3d 3i3h 3a4b 7g2b+ 3b2b 6h7g 7a6b 2f2e 4b3c 3g3f 6a5b 5i6h 7c7d 4g4f 2b3b 2i3g 5a4b 3h4g 8a7c 4i4h 6b6c 2h2i 5b6b 4g5f 8b8a 6g6f 6c5d 6h7i 5d6c 7i8h 6c5d 5f6g 5d5e 3f3e 3d3e 3g4e 3c4d 2e2d 2c2d 6g5f 5e4f 2i2d P*2c 2d2f 3e3f P*3c 2a3c 4e3c+ 3b3c 2f3f 4d3e 3f3h P*3f P*4g 3f3g+ 4h3g 4f5g+ P*5d 5c5d N*4f 3e4f 3g4f P*3g 3h2h N*8f 8g8f 8e8f 7g8f 8a8f P*8g 8f7f S*7g 7f7e N*4e 3c3b B*8f N*6c 8f7e 6c7e R*2a 4c4d 2a9a+ 4d4e L*4d 4b3c 4f4e B*6i P*7f N*3f 2h1h 5g5f 7f7e S*3d N*2e 3d2e 9a1a 5f5e 4e5e 5d5e 1a4a P*8f L*3e N*3d 7g8f P*4h 7h6h 6i4g+ 3e3d 2e3d S*4c 3b4c 4d4c+ 3d4c G*3e S*3d P*4d 4c5b 4a3a L*3b 3e3d 3c3d 3a3b 3d4e S*5h B*7f 5h4g 7f3b L*4f 4e5d 4d4c+ 5b4c 7e7d P*8e 7d7c+ 8e8f 7c6b 8f8g+ 8h8g P*8f 8g8f S*7d B*7b 5d5c 6b6c 7d6c N*4e 5c6b 7b6c+ 6b6c N*7e 6c5d S*6c 5d4d 4g3f G*3e 4e5c+ 3e4f 1h4h P*4g 5c4c 4d4c 3f3e P*8e 8f8e L*8a P*8b R*8h N*8f S*7c P*4d 4c3c G*4c 3b4c 4d4c+ 3c4c S*9c 8a8b P*8d L*8a 7e8c+ 8b8c B*6a 4c3b 8d8c+ N*7a P*3c 3b2b 6a7b+ 8a8c P*8d 8c8d 9c8d 7c8d 8e8d 8h8f+ L*8e N*8a 3c3b+ 2b3b L*3d 3b2a 1f1e S*7e
go btime 6000 wtime 9000 binc 1000 winc 1000
position startpos moves 7g7f 8c8d 6i7h 9c9d 9g9f 8d8e 8h7g 6c6d 7i6h 4a3b 1g1f 1c1d 2g2f 3c3d 3i3h 3a4b 7g2b+ 3b2b 6h7g 7a6b 2f2e 4b3c 3g3f 6a5b 5i6h 7c7d 4g4f 2b3b 2i3g 5a4b 3h4g 8a7c 4i4h 6b6c 2h2i 5b6b 4g5f 8b8a 6g6f 6c5d 6h7i 5d6c 7i8h 6c5d 5f6g 5d5e 3f3e 3d3e 3g4e 3c4d 2e2d 2c2d 6g5f 5e4f 2i2d P*2c 2d2f 3e3f P*3c 2a3c 4e3c+ 3b3c 2f3f 4d3e 3f3h P*3f P*4g 3f3g+ 4h3g 4f5g+ P*5d 5c5d N*4f 3e4f 3g4f P*3g 3h2h N*8f 8g8f 8e8f 7g8f 8a8f P*8g 8f7f S*7g 7f7e N*4e 3c3b B*8f N*6c 8f7e 6c7e R*2a 4c4d 2a9a+ 4d4e L*4d 4b3c 4f4e B*6i P*7f N*3f 2h1h 5g5f 7f7e S*3d N*2e 3d2e 9a1a 5f5e 4e5e 5d5e 1a4a P*8f L*3e N*3d 7g8f P*4h 7h6h 6i4g+ 3e3d 2e3d S*4c 3b4c 4d4c+ 3d4c G*3e S*3d P*4d 4c5b 4a3a L*3b 3e3d 3c3d 3a3b 3d4e S*5h B*7f 5h4g 7f3b L*4f 4e5d 4d4c+ 5b4c 7e7d P*8e 7d7c+ 8e8f 7c6b 8f8g+ 8h8g P*8f 8g8f S*7d B*7b 5d5c 6b6c 7d6c N*4e 5c6b 7b6c+ 6b6c N*7e 6c5d S*6c 5d4d 4g3f G*3e 4e5c+ 3e4f 1h4h P*4g 5c4c 4d4c 3f3e P*8e 8f8e L*8a P*8b R*8h N*8f S*7c P*4d 4c3c G*4c 3b4c 4d4c+ 3c4c S*9c 8a8b P*8d L*8a 7e8c+ 8b8c B*6a 4c3b 8d8c+ N*7a P*3c 3b2b 6a7b+ 8a8c P*8d 8c8d 9c8d 7c8d 8e8d 8h8f+ L*8e N*8a 3c3b+ 2b3b L*3d 3b2a 1f1e S*7e 8d9d G*9c
go btime 6000 wtime 9000 binc 1000 winc 1000
isready
position startpos moves 2g2f 8c8d 7g7f 8d8e 8h7g 1c1d 3i3h 7a7b 7i8h
go btime 60000 wtime 60000 binc 1000 winc 1000
PV_Mate_Search_Threads 4
isready
usinewgame
position startpos moves 7g7f 8c8d 7i6h 6c6d 8h7g 3c3d 4g4f 2b7g+ 6h7g 9c9d 3i3h
go btime 59000 wtime 60000 binc 1000 winc 1000
PV_Mate_Search_Threads 2
isready
usinewgame
position startpos moves 7g7f 8c8d 8h7g 4a3b 7i6h 3a4b 3i4h 3c3d 7g2b+ 3b2b 1g1f 1c1d 4g4f 2b3b 9g9f 9c9d 6i7h 7a6b 3g3f 6c6d 2g2f 7c7d 2i3g 4b3c 4h4g 8a7c 4i4h 5a4b 6g6f 6b6c 4g5f 6a6b 2h2i 6c5d 5i6i 8d8e 6h7g 6d6e 6f6e 8e8f 7g8f 5d6e 5f6e 7c6e 6i5h 3d3e 3g4e 3e3f P*3h 3c4d P*6f P*8e S*7a 8e8f 7a8b 8f8g+ 6f6e 8g7h N*3d 4b3a 8b7c+ S*3c 4e3c+ 4d3c S*2b 3b2b R*8a P*6a 7c6b 3c3d 8a6a+ 3a3b 6b5b 2c2d 6a3a 3b2c 3a3d 2c3d B*6g S*4e 6g4e 3d3e S*5e R*6h 5h4g B*1b 4e1b+ 6h4h+ 4g4h G*3g 3h3g 3f3g+ 4h3g S*3f 3g2h G*2g 2h3i N*4g 3i4h 2b1b S*4d 4c4d 5e4d 3e4d G*4c 4d3d
go btime 27000 wtime 17000 binc 1000 winc 1000
  • テスト用の局面。9手詰め。
PV_Mate_Search_Threads 2
isready
usinewgame
position startpos moves 2g2f 8c8d 2f2e 4a3b 6i7h 8d8e 3i3h 7a7b 9g9f 9c9d 3g3f 8e8f 8g8f 8b8f 5i6h 1c1d 2i3g 7c7d 2e2d 2c2d 2h2d P*2c 2d7d 8f8b P*8g 7b7c 7d7e 3c3d 7e2e 4c4d 7g7f 3a4b 4g4f 4b4c 3h4g 7c6d 4i4h 6a5b 6h5h 2a3c 2e2i 8b7b 7h7g 5c5d 1g1f 4d4e 3g4e 3c4e 4f4e 6d6e 5g5f 5d5e N*4d 5e5f 6g6f 4c4d 4e4d 6e5d 4g5f 2b4d S*4e P*4g 4h4g 5d4e 5f4e 4d7a 4g5g P*5f 5g5f N*6d 5h6g 6d5f 4e5f N*6d 5f6e 8a7c N*6h 7c6e 6f6e 7a9c 6e6d S*6i N*7e S*5h 6g5f P*5e 5f4f 6c6d 7g6f 5b5c 6f5e P*5d 5e4e 7b8b P*5b 5c5b 8h1a+ 8b8g+ L*7g G*4g 4f5f 6i7h 4e5d 7h7i 5d6d 9c8b N*4d S*5e 5f5e 8b6d 5e6e 6d7e 4d5b+ 5a5b S*6c 5b6c S*7d 6c5b G*6c 5b4b 7f7e P*6d 6c6d N*6a P*5d S*2b B*4d 2b1a 5d5c+ 4b3a 4d1a+ B*5a P*2b 3b2b S*4b 5a4b 5c4b 3a4b 1a2b S*7f 6e5e P*5d 6d5d G*4e 5e4e 8g8c
go
UCT_Threads1 1
PV_Mate_Search_Threads 2
isready
usinewgame
position sfen 1p3+p+pp1/1+Pg4n1/1+Pn2pksp/1Sp1K4/1+r6L/4pG3/PPP2LPPP/1B2R2L1/BN1PP1GNL b g2s 1
go btime 10000 wtime 10000 byoyomi 1000

⇨ 詰みは発見できたものの、指し手がそれにならないなー。なんぞこれ…。タイミングによるんかな…。うーむ..

  1. mateちゃんと出せるようにする。
  2. matedちゃんと出せるようにする。
  3. df-pnの詰み、手数がちゃんと出るようにする。

⇨ mate見つけるとSetLose書き込みしてしまうから、その枝から先に 探索がいかなくなって、古い情報のまま、nodeも展開されないから、 PV lineがそこではなくなって、node展開されてないからPV mateも 機能しなくて、読み筋がいつまでも更新されないのか? ⇨ 違う気がする。よく考える。 ⇨ cp 3000ってeval_coef = 285だと詰みスコアか。そうか…。

PV_Mate_Search_Threads 2 isready usinewgame position sfen 1p3+p+pp1/1+Pg4n1/1+Pn2pksp/1Sp1K4/1+r6L/4pG3/PPP2LPPP/1B2R2L1/BN1PP1GNL b g2s 1 go

PV_Mate_Search_Threads 2
isready
usinewgame
position startpos moves 2g2f 8c8d 2f2e 4a3b 6i7h 8d8e 3i3h 7a7b 9g9f 9c9d 3g3f 8e8f 8g8f 8b8f 5i6h 1c1d 2i3g 7c7d 2e2d 2c2d 2h2d P*2c 2d7d 8f8b P*8g 7b7c 7d7e 3c3d 7e2e 4c4d 7g7f 3a4b 4g4f 4b4c 3h4g 7c6d 4i4h 6a5b 6h5h 2a3c 2e2i 8b7b 7h7g 5c5d 1g1f 4d4e 3g4e 3c4e 4f4e 6d6e 5g5f 5d5e N*4d 5e5f 6g6f 4c4d 4e4d 6e5d 4g5f 2b4d S*4e P*4g 4h4g 5d4e 5f4e 4d7a 4g5g P*5f 5g5f N*6d 5h6g 6d5f 4e5f N*6d 5f6e 8a7c N*6h 7c6e 6f6e 7a9c 6e6d S*6i N*7e S*5h 6g5f P*5e 5f4f 6c6d 7g6f 5b5c 6f5e P*5d 5e4e 7b8b P*5b 5c5b 8h1a+ 8b8g+ L*7g G*4g 4f5f 6i7h 4e5d 7h7i 5d6d 9c8b N*4d S*5e 5f5e 8b6d 5e6e 6d7e 4d5b+ 5a5b S*6c 5b6c S*7d 6c5b G*6c 5b4b 7f7e P*6d 6c6d N*6a P*5d S*2b B*4d 2b1a 5d5c+ 4b3a 4d1a+ B*5a P*2b 3b2b S*4b 5a4b 5c4b 3a4b 1a2b S*7f 6e5e P*5d 6d5d G*4e 5e4e
go
	// uct_nodeからPVを辿り、詰み探索をしていない局面を見つけたら
	// df-pnで詰探索を1回行う。
	// 詰み探索を行う局面がないか、詰探索を1回したならば、returnする。
	void PvMateSearcher::SearchInner(Position& pos, Node* uct_node)
	{
		// 停止
		if (stop) return;

		// 未展開の場合、終了する
		if (!uct_node->IsEvaled() || !uct_node->child) {
			std::this_thread::yield();
			return;
		}

		ChildNode* uct_child = uct_node->child.get();

		// このnodeがroot nodeでかつ詰み探索がまだであるなら、探索する。
		// (dlshogiにはない処理だが、ふかうら王ではPV lineのmate searchを1本化したいのでこうする)

		bool root = pos.game_ply() == dl_searcher->pos_root.game_ply();

		Node* node;
		if (root)
			node = uct_node;
		else
		{
			// 訪問回数が最大の子ノードを選択
			const auto next_index = select_max_child_node(uct_node);

			// 詰みの場合、終了する
			if (uct_child[next_index].IsWin() || uct_child[next_index].IsLose()) {
				std::this_thread::yield();
				return;
			}

			// まだ子Nodeが生成されていないか?
			if (!uct_node->child_nodes || !uct_node->child_nodes[next_index])
				return;

			// 選んだ手を着手
			StateInfo st;
			const auto move = uct_child[next_index].move;
			pos.do_move(move, st);

			// 停止
			if (stop) return;

			node = uct_node->child_nodes[next_index].get();
		}

		// いまから詰み探索済みフラグをチェックするのでlockが必要
		mtx_searched.lock();

		// このchild nodeは詰み探索済みであるか?
		if (!node->dfpn_checked)
		{
			// 詰み探索まだ。

			// いったん詰み探索をしたことにする。(他のスレッドがこの局面を重複して探索しないように。)
			uct_node->dfpn_checked = true;

			mtx_searched.unlock();

			// 詰みの場合、ノードを更新
			Move move = dfpn.mate_dfpn(pos, node_limit);
			if (move) {
				// 詰みを発見した。

				// rootで詰みを発見したのでメッセージを出力しておく。
				if (root)
				{
					sync_cout << "info string found the mate by df-pn , move = " << to_usi_string(move) << sync_endl;

					// moveの指し手をSetLose()する。
					for (int i = 0; i < node->child_num; ++i)
						if (uct_child[i].move == move)
						{
							uct_child[i].SetLose();
							break;
						}
				}
				else
				{
					// SetWinしておけばPV line上なので次のUctSearchで更新されるはず。
					// ここがrootなら、これでrootは詰みを発見するので自動的に探索が停止する。
					// ゆえに、rootであるかの判定等は不要である。
					uct_child[next_index].SetWin();
					// ⇨ moveの指し手を指したら、子ノードの局面に即詰みがあるということなので
					//   現局面は負けの局面であることに注意。
				}
			}
			else if (stop) {
				// 途中で停止された場合、未探索に戻す。
				std::lock_guard<std::mutex> lock(mtx_searched);
				node->dfpn_checked = false;
			}
			// 探索中にPVが変わっている可能性があるため、ルートに戻る。
		}
		else {
			mtx_searched.unlock();

			// 詰み探索済みであることがわかったので、
			// 子が展開済みの場合、PV上の次の手へ
			if (uct_node->child_nodes && uct_node->child_nodes[next_index])
				// 再帰的に子を辿っていく。
				SearchInner(pos, uct_node->child_nodes[next_index].get());
			else
				std::this_thread::yield();
		}
	}
	// --------------------------------------------------------------------
	//  RootDfpnSearcher : Rootノードでのdf-pn探索用。
	// --------------------------------------------------------------------

	RootDfpnSearcher::RootDfpnSearcher(DlshogiSearcher* dlshogi_searcher)
	{
		this->dlshogi_searcher = dlshogi_searcher;
		solver = std::make_unique<Mate::Dfpn::MateDfpnSolver>(Mate::Dfpn::DfpnSolverType::Node48bitOrdering);
	}

	// 詰み探索用のメモリを確保する。
	// 確保するメモリ量ではなくノード数を指定するので注意。
	void RootDfpnSearcher::alloc(u32 nodes_limit)
	{
		// 子ノードを展開するから、探索ノード数の8倍ぐらいのメモリを要する
		solver->alloc_by_nodes_limit((size_t)(nodes_limit * 8));
	}

	// 引き分けになる手数の設定
	// max_game_ply = 引き分けになるgame ply。この値になった時点で不詰扱い。
	void RootDfpnSearcher::set_max_game_ply(int max_game_ply)
	{
		solver->set_max_game_ply(max_game_ply);
	}

	// df-pn探索する。
	// この関数を呼び出すとsearching = trueになり、探索が終了するとsearching = falseになる。
	// nodes_limit   = 探索ノード数上限
	// Threads.stop == trueになるとdfpn探索を終了する。
	void RootDfpnSearcher::search(const Position& rootPos , u32 nodes_limit)
	{
		searching = true;
		mate_move = MOVE_NONE;
		mate_ponder_move = MOVE_NONE;

		Move move = solver->mate_dfpn(rootPos,nodes_limit);
		if (is_ok(move))
		{
			// 解けたのであれば、それを出力してやる。
			// PV抑制するならそれ考慮したほうがいいかも…。
			auto mate_pv = solver->get_pv();
			std::stringstream ss;
			ss << "info string solved by df-pn : mate = " << USI::move(move) << " , mate_nodes_searched = " << solver->get_nodes_searched() << std::endl;
			ss << "info score mate " << mate_pv.size() << " pv" << USI::move(mate_pv);
			this->pv = ss.str();

			mate_move = move;
			if (mate_pv.size() >= 2)
				mate_ponder_move = mate_pv[1]; // ponder moveも設定しておいてやる。

			// 探索の中断を申し入れる。
			dlshogi_searcher->search_limits.interruption = true;
		}
		// デバッグメッセージONであるなら状況も出力してやる。
		else if (dlshogi_searcher->search_options.debug_message)
		{
			// 不詰が証明された
			if (move == MOVE_NULL)
			{
				if (solver->get_nodes_searched() > 1) /* いくらか探索したのであれば */
					sync_cout << "info string df-pn solver : no mate has been proven. mate_nodes_searched = " << solver->get_nodes_searched() << sync_endl;
			}
			else if (solver->get_nodes_searched() >= nodes_limit)
			{
					sync_cout << "info string df-pn solver : exceeded RootMateSearchNodesLimit. mate_nodes_searched = " << solver->get_nodes_searched() << sync_endl;
			}
			else if (solver->is_out_of_memory())
			{
					sync_cout << "info string df-pn solver : out_of_memory. mate_nodes_searched = " << solver->get_nodes_searched() << sync_endl;
			}
		}

		searching = false;
	}
		// root局面での詰み探索用。
		std::unique_ptr<RootDfpnSearcher> root_dfpn_searcher;


	// あとで
	class RootDfpnSearcher
	{
	public:
		RootDfpnSearcher(DlshogiSearcher* dlshogi_searcher);

		// 詰み探索用のメモリを確保する。
		// 確保するメモリ量ではなくノード数を指定するので注意。
		void alloc(u32 nodes_limit);

		// df-pn探索する。
		// この関数を呼び出すとsearching = trueになり、探索が終了するとsearching = falseになる。
		// nodes_limit   = 探索ノード数上限
		void search(const Position& rootPos, u32 nodes_limit);

		// 引き分けになる手数の設定
		// max_game_ply = 引き分けになるgame ply。この値になった時点で不詰扱い。
		void set_max_game_ply(int max_game_ply);

		// 探索中であるかのフラグ
		std::atomic<bool> searching;

		// 解けた時の詰みになる指し手とponder move(相手の指し手の予想手)
		std::atomic<Move> mate_move , mate_ponder_move;

		// 解けた時のPV
		std::string pv;

	private:
		// df-pn探索を行うsolver
		std::unique_ptr<Mate::Dfpn::MateDfpnSolver> solver;

		DlshogiSearcher* dlshogi_searcher;
	};

		root_dfpn_searcher   = std::make_unique<RootDfpnSearcher>(this);
		// ---------------------
		//     root nodeでのdf-pn
		// ---------------------

		// root nodeでのdf-pn solverが詰みを見つけているならその読み筋を出力して、それを返す。
		if (root_dfpn_searcher->mate_move)
		{
			// 詰み筋を表示してやる。
			if (!search_limits.silent)
				sync_cout << root_dfpn_searcher->pv << sync_endl;

			ponderMove = root_dfpn_searcher->mate_ponder_move;
			return root_dfpn_searcher->mate_move;
		}



	// root nodeでの詰め将棋ルーチンの呼び出しに関する条件を設定し、メモリを確保する。
	void DlshogiSearcher::InitMateSearcher()
	{
		// -- root nodeでdf-pn solverを呼び出す時。

		// メモリを確保(探索ノード数を設定してそれに応じたメモリを確保する)
		root_dfpn_searcher->alloc           (search_options.root_mate_search_nodes_limit);

		// 引き分けになる手数の設定
		root_dfpn_searcher->set_max_game_ply(search_options.max_moves_to_draw);
	}



		// 詰み探索中で、探索し続ければ解けるのかも。
		if (root_dfpn_searcher->searching)
			return;


		else if (thread_id == s + 1)
			root_dfpn_searcher->search(rootPos, search_options.root_mate_search_nodes_limit); // df-pnの探索ノード数制限



		// root nodeでの詰め将棋ルーチンの呼び出しに関する条件を設定し、メモリを確保する。
		void InitMateSearcher();



		// root nodeで詰み探索するならそのためのメモリを確保する。
		if (dfpn_thread_num)
			InitMateSearcher();
	// root nodeでの詰み探索用のスレッド数
	const int dfpn_thread_num = (search_options.root_mate_search_nodes_limit > 0) ? 1 : 0;

2024/4/29

  • ふかうら王にPV lineの詰み探索導入。
    • エンジンオプション追加。
      • PV_Mate_Search_Threads : PV lineの詰み探索のスレッド数
      • PV_Mate_Search_Nodes : PV lineの詰み探索の1局面の最大ノード数

### PV_Mate_Search_Threads, PV_Mate_Search_Nodes

PV line(最善応手列上の局面)に対して、専用スレッドを用意して詰み探索を行います。

- PV_Mate_Search_Threads : PV lineの詰み探索のスレッド数
- PV_Mate_Search_Nodes : PV lineの詰み探索の1局面の最大ノード数
	⚠ 事前にこのノード数の分だけメモリを確保するので、あまり大きな数値にするとメモリを大量に消費する。

PV_Mate_Search_Threadsに0を指定するとこの機能は無効化されます。

2024/4/22

  • やねうら王のKPPTとKPP_KKPT用のbuildで、数値のみからなるVERSIONをmakeする時に指定するとうまく文字列が埋め込まれないバグ修正。

2024/4/16

is_ok(Move)のコメント修正。(thanks > Lefu777さん)

2024/4/5

  • emulation版のMSB32/64 で-1をし忘れている #273 // Lefu777さんのプルリク。

2024/4/2

  • BURNING_BRIDGES_hd2-Final

engine1 = YaneuraOuNNUE_V820c_1024.exe , eval = tanuki20240131 engine2 = YaneuraOuNNUE_V820c.exe , eval = BURNING_BRIDGES_hd2-Final T2,b1000,1608 - 82 - 1310(55.11% R35.61[24.97,46.24]) winrate black , white = 51.51% , 48.49% ⇨ たぶん公開されてるNNUEよりは+R15ぐらいでtanuki-最新モデルよりは-R35ぐらい..

V820c

engine1 = YaneuraOuNNUE_V820b_1024.exe , eval = tanuki20240131 engine2 = YaneuraOuNNUE_V820c_1024.exe , eval = tanuki20240131 T2,b1000,1464 - 179 - 1357(51.9% R13.18[2.42,23.95]) winrate black , white = 51.65% , 48.35%

⇨ stat bonusいじるの影響がでかすぎる。  これrollbackする。他の枝刈りと絡みすぎる。

V820b

// 親nodeでのfutility margin // 重要度 ★★★☆☆ // 元の値 = Stockfish 14 : 112 , Stockfish 16 : 127 , step = 10 // [PARAM] min:100,max:400,step:10,interval:1,time_rate:1,fixed PARAM_DEFINE PARAM_FUTILITY_AT_PARENT_NODE_ALPHA = 127;

engine1 = YaneuraOuNNUE_V820a_1024.exe , eval = tanuki20240131 engine2 = YaneuraOuNNUE_V820b_1024.exe , eval = tanuki20240131 T2,b1000,1426 - 147 - 1427(49.98% R-0.12[-10.82,10.58]) winrate black , white = 52.47% , 47.53% ⇨ 計測できない差だが、探索パラメーターひとつ減ったのでこれは採用する。

V820a : ここまで。

  • ふかうら王、hash 128bitでコンパイルエラーになっていたの修正。

  • README.mdに第二回電竜戦HWTを戦績として追加。

  • 定跡のBookDepthLimitでのfilterについて、コメント追加。

2024/3/18

  • MSB64 を使うことで香車の効きの最適化 (#272)

    engine1 = YaneuraOuNNUE_V811_1024.exe , eval = tanuki20240131 engine2 = YaneuraOuNNUE_V820a_1024.exe , eval = tanuki20240131 T2,b1000,1439 - 170 - 1391(50.85% R5.89[-4.85,16.64]) winrate black , white = 51.66% , 48.34%

改造前よりR5.89 低かった。と言うか、3000局では計測できない差であった。 指し手生成が5%速くなったところで、NPS 1%ぐらいしか高速化しないのでこの差は計測できない。

仕方ないので、depth固定でのbench時に同じ探索ノード数になるかをチェックする。

bench 1024 1 24 default depth

=========================== Total time (ms) : 24567 Nodes searched : 23135171 Nodes_searched/second : 941717

⇓ 変更後

Total time (ms) : 25275 Nodes searched : 23135171 Nodes_searched/second : 915338

// NPS下がっているがサーマルスロットリングか。 ノード数は変わってないので良しとする。

bench 1024 1 25 default depth

=========================== Total time (ms) : 72002 Nodes searched : 72640562 Nodes_searched/second : 1008868

⇓変更後

Total time (ms) : 71779 Nodes searched : 72640562 Nodes_searched/second : 1012002

// 今度は速くなった。ともかくノード数変わってないので良しとする。

NPSはサーマルスロットリングがあって正確に計測できないが、速くなっていたとして0.4%程度。 たぶん遅くはなってなさそう。

2024/2/20

  • ふかうら王、hash 128bitでコンパイルエラーになっていたの修正。

2024/2/19

  • ふかうら王V8.00リリース
  • やねうら王Wikiのふかうら王のインストール手順、更新。

2024/2/14

・GitHubのやねうら王のレーティング差も計測する。

engine1 = YaneuraOuNNUE1024_2024.01.21-cl4_avx2.exe , eval = tanuki20240131 engine2 = YaneuraOuNNUE1024_2024.02.16-cl4_avx2.exe , eval = tanuki20240131 T2,b1000,1353 - 130 - 1517(47.14% R-19.88[-30.56,-9.19]) winrate black , white = 51.01% , 48.99%

+R20ぐらいか。そうか…。

V8.10w

  • V8.10uのrevert。

engine1 = YaneuraOuNNUE_V810_1024.exe , eval = tanuki20240131 engine2 = YaneuraOuNNUE_V810w_1024.exe , eval = tanuki20240131 T2,b1000,1356 - 119 - 1525(47.07% R-20.4[-31.07,-9.74]) winrate black , white = 50.68% , 49.32% T2,b2000,1331 - 166 - 1333(49.96% R-0.26[-11.33,10.81]) winrate black , white = 53.49% , 46.51% ⇨ トータルでぜんぜんつよなってない。なんでなのだ…。

V8.10v

engine1 = YaneuraOuNNUE_V810u_1024.exe , eval = tanuki20240131 engine2 = YaneuraOuNNUE_V810v_1024.exe , eval = tanuki20240131 T2,b1000,1434 - 170 - 1396(50.67% R4.67[-6.08,15.41]) winrate black , white = 52.76% , 47.24% ⇨ 計測できる差にならんな.. T2,b2000,1376 - 167 - 1457(48.57% R-9.94[-20.68,0.8]) winrate black , white = 51.82% , 48.18% ⇨ 悪くなさそうなので良しとする。

engine1 = YaneuraOuNNUE_V810v_1024.exe , eval = tanuki20240131 engine2 = YaneuraOuNNUE1024_2024.02.16-cl4_avx2.exe , eval = tanuki20240131 T2,b1000,1801 - 128 - 1071(62.71% R90.29[79.27,101.31]) winrate black , white = 51.11% , 48.89% ⇨ 大差か..

・GitHubの配布用実行ファイルのビルドをするためのDocker環境用意する。 ⇨ 別に要らんかった。MSYS2なのでDocker化するのあまり効率よくなさそう。

  • ふかうら王、logitの計算の時に、policyが負の値を返すとアンダーフローする可能性があったの修正。
    • これ、アンダーフローしたところで微小なものはどうせ0と変わらないのでアンダーフローしても探索に影響はないのだが…。
    • Softmaxまわりのコメント追加。

V8.10u

			// Quiet ttMove extensions (~1 Elo)
			// 駒を取らない置換表の指し手に関する延長

			// PV nodeでquietなttは良い指し手のはずだから延長するというもの。

			else if (PvNode
				&& move == ttMove
				&& move == ss->killers[0]
				&& (*contHist[0])(movedPiece, to_sq(move)) >= 4194)
				extension = 1;

engine1 = YaneuraOuNNUE_V810t_1024.exe , eval = tanuki20240131 engine2 = YaneuraOuNNUE_V810u_1024.exe , eval = tanuki20240131 T2,b1000,1452 - 170 - 1378(51.31% R9.09[-1.66,19.83]) winrate black , white = 51.73% , 48.27% T2,b2000,1253 - 190 - 1157(51.99% R13.85[2.2,25.5]) winrate black , white = 53.44% , 46.56% ⇨ これは、よわなってる可能性がある。 ⇨ 長い時間で計測する。

engine1 = YaneuraOuNNUE_V810_1024.exe , eval = tanuki20240131 engine2 = YaneuraOuNNUE_V810u_1024.exe , eval = tanuki20240131 T2,b2000,1332 - 193 - 1475(47.45% R-17.72[-28.51,-6.92]) winrate black , white = 51.87% , 48.13%

V8.10t

engine1 = YaneuraOuNNUE_V810r_1024.exe , eval = tanuki20240131 engine2 = YaneuraOuNNUE_V810t_1024.exe , eval = tanuki20240131 T2,b1000,1381 - 185 - 1434(49.06% R-6.54[-17.31,4.23]) winrate black , white = 51.44% , 48.56%

engine1 = YaneuraOuNNUE_V810_1024.exe , eval = tanuki20240131 engine2 = YaneuraOuNNUE_V810t_1024.exe , eval = tanuki20240131 T2,b1000,1386 - 160 - 1454(48.8% R-8.32[-19.05,2.4]) winrate black , white = 51.13% , 48.87% // V810からあんまつよなってない…。

V8.10s

			// Check extensions (~1 Elo)
			// 王手延長

			//  注意 : 王手延長に関して、Stockfishのコード、ここに持ってくる時には気をつけること!
			// → 将棋では王手はわりと続くのでそのまま持ってくるとやりすぎの可能性が高い。
			// 
			// ※ Stockfish 14では depth > 6 だったのが、Stockfish 15でdepth > 9に変更されたが				
			//  それでもまだやりすぎの感はある。やねうら王では、延長の条件をさらに絞る。
			/*
			else if (givesCheck
				&& depth > 9
				// !!重要!!
				// この条件、やねうら王では独自に追加している。
				// → 王手延長は、開き王手と駒損しない王手に限定する。
				//  将棋だと王手でどんどん延長させる局面があり、探索が終わらなくなる。
				&& (pos.is_discovery_check_on_king(~us, move) || pos.see_ge(move))
				)
				extension = 1;
			*/
			// ⇨ Stockfishで削除されたが、王手延長自体は何らかあった方が良い可能性はあるので条件を調整してはどうか。
			// Remove check extension : https://github.com/official-stockfish/Stockfish/commit/96837bc4396d205536cdaabfc17e4885a48b0588

V8.10r

  • Simplify opponent movecount reduction : Simplify opponent movecount reduction
		// Decrease reduction if opponent's move count is high (~1 Elo)
		// 相手の(1手前の)move countが大きければ、reductionを減らす。
		// 相手の指し手をたくさん読んでいるのにこちらだけreductionするとバランスが悪いから。

		if ((ss - 1)->moveCount > 7)
			r--;

⇨ 削除

engine1 = YaneuraOuNNUE_V810q_1024.exe , eval = tanuki20240131 engine2 = YaneuraOuNNUE_V810r_1024.exe , eval = tanuki20240131 T2,b1000,1391 - 155 - 1454(48.89% R-7.69[-18.41,3.02]) winrate black , white = 53.64% , 46.36%

V8.10q

engine1 = YaneuraOuNNUE_V810p_1024.exe , eval = tanuki20240131 engine2 = YaneuraOuNNUE_V810q_1024.exe , eval = tanuki20240131 T2,b1000,1439 - 149 - 1412(50.47% R3.29[-7.41,13.99]) winrate black , white = 54.3% , 45.7%

V8.10p

engine1 = YaneuraOuNNUE_V810o_1024.exe , eval = tanuki20240131 engine2 = YaneuraOuNNUE_V810p_1024.exe , eval = tanuki20240131 T2,b1000,1443 - 151 - 1406(50.65% R4.51[-6.19,15.22]) winrate black , white = 52.97% , 47.03% ⇨ あんまいいことない可能性がある?

V8.10o

engine1 = YaneuraOuNNUE_V810m_1024.exe , eval = tanuki20240131 engine2 = YaneuraOuNNUE_V810o_1024.exe , eval = tanuki20240131 T2,b1000,1430 - 131 - 1439(49.84% R-1.09[-11.76,9.58]) winrate black , white = 51.59% , 48.41%

V8.10m

  		if (lmrDepth < PARAM_PRUNING_BY_HISTORY_DEPTH && history < -3645 * depth)
  			continue;

⇓ -3645 ⇨ -4195 に変更。

engine1 = YaneuraOuNNUE_V810l_1024.exe , eval = tanuki20240131 engine2 = YaneuraOuNNUE_V810m_1024.exe , eval = tanuki20240131 T2,b1000,1434 - 157 - 1409(50.44% R3.06[-7.66,13.77]) winrate black , white = 51.67% , 48.33% ⇨ 計測できない差

V8.10l

パラメーターついでに変更しておく。

  		lmrDepth += history / 7836;

  		lmrDepth += history / 6992;

engine1 = YaneuraOuNNUE_V810k_1024.exe , eval = tanuki20240131 engine2 = YaneuraOuNNUE_V810l_1024.exe , eval = tanuki20240131 T2,b1000,1441 - 132 - 1427(50.24% R1.7[-8.97,12.37]) winrate black , white = 53.77% , 46.23%

V8.10k

engine1 = YaneuraOuNNUE_V810j_1024.exe , eval = tanuki20240131 engine2 = YaneuraOuNNUE_V810k_1024.exe , eval = tanuki20240131 T2,b1000,1380 - 149 - 1471(48.4% R-11.09[-21.8,-0.39]) winrate black , white = 51.91% , 48.09% T2,b2000,1393 - 224 - 1383(50.18% R1.25[-9.59,12.1]) winrate black , white = 53.6% , 46.4% ⇨ 長い時間で言うほどつよなってない

V8.10j

engine1 = YaneuraOuNNUE_V810i_1024.exe , eval = tanuki20240131 engine2 = YaneuraOuNNUE_V810j_1024.exe , eval = tanuki20240131 T2,b1000,1319 - 123 - 1318(50.02% R0.13[-11.0,11.26]) winrate black , white = 50.63% , 49.37% ⇨ 計測不能な差

V8.10i

engine1 = YaneuraOuNNUE_V810h2_1024.exe , eval = tanuki20240131 engine2 = YaneuraOuNNUE_V810i_1024.exe , eval = tanuki20240131 T2,b1000,1325 - 119 - 1416(48.34% R-11.54[-22.46,-0.62]) winrate black , white = 52.06% , 47.94% ⇨ つよくなってるっぽい。PV絡みだからsingular extensionと同じ理屈で見かけが強いだけかも?

V8.10h2 SUPER_SORTあり + PARAM_MOVEPICKER_SORT_ALPHA1 = 3500でテスト

engine1 = YaneuraOuNNUE_V810g_1024.exe , eval = tanuki20240131 engine2 = YaneuraOuNNUE_V810h2_1024.exe , eval = tanuki20240131 T2,b1000,1380 - 127 - 1393(49.77% R-1.63[-12.48,9.22]) winrate black , white = 52.98% , 47.02% ⇨ 強くはなっていないが悪くはなさそうなのでこれでよしとする。

V8.10h

PARAM_MOVEPICKER_SORT_TH2 /1960/ ⇨ 廃止

// move pickerでsortする係数 (super sort使用時)
// 重要度 ★★★★★
// 元の値 = 3130 , step = 1
// [PARAM] min:0,max:6000,step:500,interval:1,time_rate:1,fixed
PARAM_DEFINE PARAM_MOVEPICKER_SORT_ALPHA1 = 3330;

// move pickerでsortする係数 (super sort使用しない時)
// 重要度 ★★★★★
// 元の値 = 3130 , step = 1
// [PARAM] min:0,max:6000,step:500,interval:1,time_rate:1,fixed
PARAM_DEFINE PARAM_MOVEPICKER_SORT_ALPHA2 = 3500;

こういう定数にした。

まず3330でテスト。

V8.10g

  • V8.10cを修正した。

V8.10f

engine1 = YaneuraOuNNUE_V810e_1024.exe , eval = tanuki20240131 engine2 = YaneuraOuNNUE_V810g_1024.exe , eval = tanuki20240131 T2,b1000,1397 - 123 - 1480(48.56% R-10.03[-20.68,0.63]) winrate black , white = 53.01% , 46.99% T2,b2000,1423 - 172 - 1405(50.32% R2.21[-8.53,12.96]) winrate black , white = 52.05% , 47.95%

V8.10e

engine1 = YaneuraOuNNUE_V810d_1024.exe , eval = tanuki20240131 engine2 = YaneuraOuNNUE_V810e_1024.exe , eval = tanuki20240131 T2,b1000,1346 - 128 - 1316(50.56% R3.92[-7.16,14.99]) winrate black , white = 52.25% , 47.75% ⇨ 差が小さすぎて計測不能

V8.10d

engine1 = YaneuraOuNNUE_V810c_1024.exe , eval = tanuki20240131 engine2 = YaneuraOuNNUE_V810d_1024.exe , eval = tanuki20240131 T2,b1000,1339 - 110 - 1331(50.15% R1.04[-10.02,12.1]) winrate black , white = 53.11% , 46.89% ⇨ 差が小さすぎて計測不能。

V8.10c

engine1 = YaneuraOuNNUE_V810b_1024.exe , eval = tanuki20240131 engine2 = YaneuraOuNNUE_V810c_1024.exe , eval = tanuki20240131 T2,b1000,140 - 6 - 74(65.42% R110.76[69.79,151.73]) winrate black , white = 50.47% , 49.53% ⇨ しまった。returnする条件、間違えてた。V8.10gで修正した。

engine1 = YaneuraOuNNUE_V810b_1024.exe , eval = tanuki20240131 engine2 = YaneuraOuNNUE_V810g_1024.exe , eval = tanuki20240131 T2,b1000,1446 - 145 - 1409(50.65% R4.5[-6.19,15.2]) winrate black , white = 52.54% , 47.46% ⇨ うーん、よくない。いっかいrollbackしてやりなおす。

修正したので計測しなおし。

engine1 = YaneuraOuNNUE_V810b_1024.exe , eval = tanuki20240131 engine2 = YaneuraOuNNUE_V810c_1024.exe , eval = tanuki20240131 T2,b1000,1320 - 125 - 1305(50.29% R1.99[-9.17,13.14]) winrate black , white = 52.61% , 47.39% ⇨ 差が小さすぎて計測不能。

V8.10b

engine1 = YaneuraOuNNUE_V810a_1024.exe , eval = tanuki20240131 engine2 = YaneuraOuNNUE_V810b_1024.exe , eval = tanuki20240131 T2,b1000,1414 - 128 - 1458(49.23% R-5.32[-15.99,5.34]) winrate black , white = 52.96% , 47.04% ⇨ OK

V8.10a

engine1 = YaneuraOuNNUE_V810_1024.exe , eval = tanuki20240131 engine2 = YaneuraOuNNUE_V810a_1024.exe , eval = tanuki20240131 T2,b1000,1398 - 123 - 1479(48.59% R-9.78[-20.44,0.87]) winrate black , white = 49.25% , 50.75% ⇨ OK

  • 評価関数の比較計測

FV_SCALE = 20 YaneuraOuNNUE_V810_1024.exe , Li YaneuraOuNNUE_V810_1024.exe , tanuki20240122 YaneuraOuNNUE_V810_1024.exe , tanuki20240131

engine1 = YaneuraOuNNUE_V810_1024.exe , eval = li engine2 = YaneuraOuNNUE_V810_1024.exe , eval = tanuki20240122 T2,b1000,442 - 20 - 538(45.1% R-34.14[-52.48,-15.81]) winrate black , white = 51.33% , 48.67% T2,b2000,426 - 41 - 533(44.42% R-38.93[-57.49,-20.37]) winrate black , white = 52.35% , 47.65%

engine1 = YaneuraOuNNUE_V810_1024.exe , eval = li engine2 = YaneuraOuNNUE_V810_1024.exe , eval = tanuki20240131 T2,b1000,405 - 19 - 576(41.28% R-61.19[-79.71,-42.67]) winrate black , white = 49.64% , 50.36% T2,b2000,386 - 46 - 568(40.46% R-67.1[-85.94,-48.27]) winrate black , white = 52.41% , 47.59%

engine1 = YaneuraOuNNUE_V810.exe , eval = hao engine2 = YaneuraOuNNUE_V810_1024.exe , eval = tanuki20240122 T2,b1000,489 - 30 - 481(50.41% R2.87[-15.48,21.21]) winrate black , white = 50.41% , 49.59%

engine1 = YaneuraOuNNUE_V810.exe , eval = hao engine2 = YaneuraOuNNUE_V810_1024.exe , eval = tanuki20240131 T2,b1000,413 - 26 - 561(42.4% R-53.21[-71.72,-34.69]) winrate black , white = 49.49% , 50.51% ⇨ 20240131は確かに強いので、定跡掘るの、これに差し替える。また、以降の探索部の調整はこれを基準に行う。

engine1 = YaneuraOuNNUE_V810_1024.exe , eval = tanuki20240122 engine2 = YaneuraOuNNUE_V810_1024.exe , eval = tanuki20240131 T2,b4000,423 - 45 - 532(44.29% R-39.83[-58.43,-21.22]) winrate black , white = 56.44% , 43.56% ⇨ 確かに長い時間で強い。

2024/2/8

  • ふかうら王、Softmaxのmaxの初期値、0.0から-INFに変更。

    • モデルによってはPolicy(logit)が負の値を返すことはありえるので。
  • ふかうら王、持ち時間制御その9

以下のところ1/4 ⇨ 1/8に。

					// 経過時間がoptimum /8 を超えてるのに残りmaximum時間をすべて用いても訪問数が逆転しない。
					// ただしこの時、eval_diffが0.1なら50%というように、eval_diffの値に応じてrest_optimum_poを減らして考える。
					if (   elapsed >= optimum / 8
  • ふかうら王の Softmax_Temparatureの上限値変更。500 → 10000

2024/2/6

  • ふかうら王、持ち時間制御その8
    • 合流局面、まだ落ちるケースがあったの修正。

検証用局面

multipv 2
DebugMessage true
isready
usinewgame
position startpos moves 7g7f 8c8d 2g2f 8d8e 2f2e 4a3b 8h7g 3c3d 7i8h 2b7g+ 8h7g 3a2b 3g3f 9c9d 9g9f 6c6d 1g1f 1c1d 6i7h 7a6b 2i3g 5a4b 3i3h 2b3c 4g4f 7c7d 3h4g 8a7c 5i6h 6a7b 4i4h 8b8a 4g5f 6b6c 6g6f 6c5d 3g4e 3c2b 7f7e 7d7e 2e2d 2c2d 2h2d 4c4d P*7d 4d4e 4f4e 2b2c 2d2g P*2d 4e4d P*4e 7d7c+
go btime 170000 wtime 199000 binc 4000 winc 4000
  • ふかうら王、持ち時間制御その7
    • 指し手はkCheckIntervalMsだけ早く指すように修正。

    • 合流局面まわり

検証用局面

multipv 2
DebugMessage true
isready
usinewgame
position startpos moves 7g7f 4a3b 2g2f 8c8d 2f2e 8d8e 8h7g 3c3d 7i7h 2b7g+ 7h7g 3a2b 1g1f 1c1d 3g3f 5a4b 6i7h 2b3c 5i6h 7a7b 3i3h 6c6d 9g9f 7b6c 9f9e 7c7d 4g4f 8a7c 3h4g 4c4d 2i3g 6a5b 4i5h 6c5d 2h2i 4b3a 5h4h 5b6b 6h7i 8b8a 7i8h 7d7e 7f7e 7c6e 7g6f 3a2b 4h5h 5d4c 7e7d 9c9d 9e9d 8e8f 8g8f 9a9d 9i9d 8a8f P*8g 8f7f 7h6h P*8f 8g8f 7f8f P*8g B*9i 8h9i 8f8g+ L*8h 8g9f P*9g 9f9d 9i9h P*8f B*5a P*9f 9g9f L*8d B*6i 6b6c 2e2d 2c2d P*2c 2b2c 6f7e 6c7d 7e8d 7d8d P*2e 2d2e 3g2e P*2d 2e3c+ 2a3c L*2h N*2e S*9e 8d9e 5a9e+ 9d7d P*7e P*9g 9h9g 7d7e P*7g S*9i 6h7h 9i8h 7h8h L*8b G*7f 7e7a 6g6f 6e7g 7f7g P*7f 7g8f 8b8f 9e8f S*7g 8h7g 7f7g+ 8f7g P*7f 7g9e P*9d 9e6h P*8h 9g8h G*7g 8i7g 7f7g+ 6h7g N*8e P*7f 8e7g+ 8h7g P*7e S*8g 7e7f 8g7f G*8e S*6g 8e7f 6g7f P*7e 7f6g 6d6e 6f6e 4c5d G*8g 5d6e 7g6h 7e7f L*7i S*6f G*7h B*9h 6g6f 6e6f S*8h P*8f 8g8f 9h8i+ 8f8g S*7g 6h5i 7a6b P*6e 4d4e 8h7g 7f7g+ 8g7g 6f7g 7h7g 8i7i S*6h 7i8h N*4d L*4a 4d3b+ 6b3b P*8i 8h8i 6i7h 8i7h 7g7h N*6f 6h6g P*7g 7h7g 6f5h+ 5i5h 4e4f 4g4f 4a4f 5h6h S*8h 7g8g B*4d S*6f S*7i 2i7i
go btime 10000 wtime 10000 byoyomi 10000

8h7iと8h7i+とがある。同玉で合流する。 → 不成だと取る一ではなかったか…。

  • ふかうら王、memcpyにstd::つけ忘れてたの修正。

  • ふかうら王、持ち時間制御その6 fix

    • ひとつ前のcommitで配列のアクセス違反になるケースがあったの修正。

検証用局面

multipv 2
DebugMessage true
isready
usinewgame
position startpos moves 7g7f 8c8d 2g2f 4a3b 2f2e 8d8e 8h7g 3c3d 7i7h 2b7g+ 7h7g 3a2b 1g1f 1c1d 3g3f 6c6d 2i3g 7a6b 5i6h 5a4b 6i7h 2b3c 3i3h 6b6c 4g4f 9c9d 9g9f 7c7d 6g6f 6c5d 3h4g 6a6b 3g4e 3c4d 2e2d
go btime 10000 wtime 10000 byoyomi 10000

V779d

  • ふかうら王、持ち時間制御その6
    • 合流判定追加

合流判定用のテスト局面。

multipv 2
DebugMessage true
isready
usinewgame
position startpos moves 2g2f 8c8d 7g7f 4a3b 2f2e 8d8e 8h7g 3c3d 7i7h 2b7g+ 7h7g 3a2b 4g4f 5a4b 9g9f B*4g B*3h 4g7d+ 3h7d 7c7d 6i7h 7a7b 1g1f 2b3c 3i4h 1c1d 4h4g 9c9d 4i4h 8a7c 6g6f 6c6d 3g3f 6d6e 2i3g 6e6f 4g5f 7b6c 7g6f 6c5d 2h2i 8e8f 8g8f 6a6b 5f4e 5d6e 6f6e 7c6e 4e5f S*3h 4h3h 6e5g+ S*6e B*6f P*5h 5g5f 6e5f 6f9i+ N*5d 4b3a 5d6b+ 9i8i G*7i 8i7i 7h7i 8b6b B*7c 6b6c 7c5a+ G*4a P*6d 6c6d 5a4a 3a4a B*5e 6d6c G*7c 6c6a S*6b 6a8a 2e2d 2c2d 7c8b P*6a 6b5c+ L*5a 3g4e S*4d 4e3c+ 2a3c 8b8a 4d5e S*2c 5a5c 2c3b 4a3b 5f5e B*7g P*6h B*4a G*2c 3b2c R*2a S*2b S*3a N*6g 5i4i S*1c 3a2b 1c2b S*3a S*1c
go btime 100000 wtime 100000 byoyomi 10000

→ 千日手なのでnode作られてなかった…。

multipv 2
DebugMessage true
isready
usinewgame
position startpos moves 7g7f 4a3b 2g2f 8c8d 2f2e 8d8e 8h7g 3c3d 7i7h 2b7g+ 7h7g 3a2b 1g1f 1c1d 3i3h 5a4b 9g9f 7a7b 3g3f 6c6d 5i6h 7c7d 6i7h 8a7c 3f3e 7c6e 7g6f 8e8f 8g8f 8b8f P*8h 7d7e B*5e 7b6c 3h3g B*3c 5e3c+ 2b3c 3e3d 3c3d 6f6e 6d6e B*7g 8f8a N*7c 8a7a 7c6a+ 7a6a 7g1a+ 2a3c 1a1b 6e6f 6g6f P*6g 6h5h 3c4e 3g4f N*5d P*3c 3b3c P*3e 5d6f 5h4h P*3f L*3h S*3g 2i3g 3f3g+ 3h3g
go btime 100000 wtime 100000 byoyomi 10000

→ 合流はしているのだが、桂不成だと同玉を強制できないので評価値と訪問回数に差がある。

  • ふかうら王、MultiPVの時に、各指し手の訪問回数をnodesとして出力するように変更。

  • ふかうら王、持ち時間制御その5

やねうらお — 今日 05:36
MCTSタイプの探索部、持ち時間をうまく制御すると、わりと強くなる。

基本的な考え方は、その1手に関して、思考時間を増やしても指し手に変化がないと思われるなら、早めに切り上げるということだ。

つまり、探索をmax time(その1手で与えられている最大時間)まで行ったに現在の指し手から変化しそうかどうかを確率的に予測できるとよろしい。

また、別の考え方として、現在の指し手が安定しているかという指し手の安定性がある。

MCTSでは訪問回数が一番多い指し手を選ぶが、そのときにbestの指し手のwinrate(期待勝率)とsecondの指し手のwinrateの関係が best winrate > second winrateになってて欲しいのだが、これが逆転しているとまずい。

その場合は、max timeまで使ってでもbest winrate > second winrateになるかを検証すべきである。

安定性という観点からは、best winrate > second winrateかつ、best movecount(bestの指し手の訪問回数) > second movecount * 1.5 みたいに、訪問回数に大きな差があることが求められる。

これ守ったほうが強くなるようである。(dlshogiがこの条件入れてたので、真似てみたら強くなった)

あとは序盤は、どの指し手も同じぐらいの期待勝率の局面がわりとあって、そういう局面でわずかな差を追い求めても仕方がない。それなのに、上のような安定性条件を守ろうとすると、持ち時間を使いすぎてしまう。

そこで序盤は、maximum timeを低めに設定することでこれを回避する。(dlshogiは、20手目までは延長しない。ふかうら王で言うと、maximum timeがoptimum time * 1.0 , 20手目以降が optimum time * 2.0 みたいな感じにしてあって、この問題を回避している。)

ここも真似てみたらいい感じになった。20手目を境に突然 2.0倍にするのはあんまりイケてないので、もう少しなめらかにしたいのだが、どれくらいの値がいいのか実験してパラメーター調整すべきだと思う。ただ、これを適切に調整するための計算資源を持ち合わせていないのでいまふかうら王は目分量で調整している。

やねうらお — 今日 05:42
訪問回数に関しては、探索打ち切りチェックの時に
best movecount > second movecount + 残りpo
だと、最大時間まで探索してもbestとsecondの指し手のmovecountの逆転が起きない。

しかしこの条件は強すぎて、時間を無駄に使っているっぽい。

例えば、勝率差 winrate diff = best winrate - second winrate として、この勝率差がある程度大きいなら、早めに切り上げる(残りpoを小さめに見積もる)みたいなことも考えられる。(これが確率的に安全な条件となるのがどれくらいなのかよくわからない。実験して確かめるべきだと思う。)

持ち時間制御、究極的には、現在のgame ply、best,second movecount, winrate等を入力にして、切り上げるかどうかの判定を行うNNを用意すべきなのかも知れない。

やねうらお — 今日 05:44
切り上げるかどうかの判定を行うNNを用意すべきなのかも知れない。

こういうの何を教師として良いのかはよくわからないけども、例えば、探索を延長したことによって元の指し手より 勝率が +r だけ高い指し手が選べたら、それを利得として、元の指し手と同じ指し手しか選べなかったら、持ち時間使った分だけ損だったので その使った時間に応じたマイナスをして…みたいな感じになるのかな。


やねうらお — 今日 05:50
■ 合流を含む時の持ち時間制御

持ち時間制御をやっていて、嫌らしいのは、合流である。

例えば、22銀と22銀成りでどちらも同金ととられるとする。つまりは同じ変化に合流するわけである。このとき、move countもwinrateもほぼ同じになって最大時間まで探索打ち切りがなされない。非常にもったいない。この2つのPVが合流しているなら、このような時間の使い方は無駄でしかない。

そうは言ってもPVが合流するかの判定が難しい。先のほうで合流する場合、途中で変化できる場合もあるからなおのこと難しい。

とは言っても、PVの2手目で即座に合流しているなら、それは判定できて欲しいところである。
// 例えば、PVの2手目、4手目の局面のhash keyが同じなら..だとか..

この場合、bestとsecondとの比較というよりは、third(3番目の指し手)がbestの指し手を上回るかどうかという話にはなる。

このへんをうまくできるアルゴリズムを考え中だ。

2024/2/5

  • ふかうら王、持ち時間制御その4

⇓このコード間違ってた。

		// optimum,maximum時間の残りが全部 1.0が返ってきた時のeval
		WinType second_winrate_maximum_upperbound = (uct_child[second_i].win + rest_maximum_po) /(uct_child[second_i].move_count + rest_maximum_po + delta);

				// 最大残りpoを費やしても1番目と2番目の評価値が逆転しない。
				// これはもうだめぽ。
				if (best_winrate >= second_winrate_maximum_upperbound)
				{
					if (o.debug_message)
						sync_cout << "info string optimum time is over , best_winrate >= second_winrate_maximum_upperbound"
						<< " , best_winrate = "   << best_winrate
						<< " , second_winrate = " << second_winrate
						<< " , second_winrate_maximum_upperbound = " << second_winrate_maximum_upperbound
						<< " , rest_maximum_po = " << rest_maximum_po << sync_endl;

					break;
				}
  • ふかうら王、持ち時間制御その3
    • refactoring
    • 以下の条件、削除
		WinType second_eval_optimum_upperbound = (wc + rest_optimum_po) /(mc + rest_optimum_po + delta);

				// 経過時間がoptimum/5を超えてるのに残りoptimum時間を用いてもevalが逆転しない。
				if (   elapsed_from_ponderhit >= optimum/5
					&& best_eval > second_eval_optimum_upperbound
					)
				{
					if (o.debug_message)
						sync_cout << "info string interrupted by early exit , best_eval > second_eval_optimum_upperbound"
						<< " , best_eval = "   << best_eval
						<< " , second_eval = " << second_eval
						<< " , second_eval_optimum_upperbound = " << second_eval_optimum_upperbound
						<< " , rest_optimum_po = " << rest_optimum_po << sync_endl;

					break;
				}

2024/2/4

  • ふかうら王、持ち時間制御調整2。

⇓削除

		// 最適時間の1/3は必ず使うべき。
		if (elapsed_from_ponderhit < s.time_manager.optimum() * 1/3)
			return ;

⇓こういうの導入した。

		for(int i = 1 ; i < 10 ; ++i)
		{
			// 経過時間がoptimum * i/10 を超えてるのに残りoptimum時間の70%を用いても訪問数が逆転しない。
			// ただし訪問数に(11-i)倍以上の差がある場合に限る。
			if (rest_optimum_po > 0 /* optimum時間が残っている */
				&& elapsed_from_ponderhit >= optimum * i / 10
				&& max_searched > second_searched + rest_optimum_po * 0.7
				&& max_searched > second_searched * (10 - i)
				)
			{
				if (o.debug_message)
					sync_cout << "info string interrupted by early exit"
					<< " , max_searched > second_searched + rest_optimum_po * 0.7 , max_searched > second_searched * (10 - " << i << ")"
					<< " , max_searched = "    << max_searched
					<< " , second_searched = " << second_searched
					<< " , rest_optimum_po = " << rest_optimum_po << sync_endl;

				// 残り時間くりあげて使って、終了すべき。
				s.time_manager.search_end = s.time_manager.round_up(elapsed_from_ponderhit);
				return;
			}
		}

⇓よくなかった

		// optimum/4以上時間を使っている状態で勝率にeval_delta 以上の差がある。
		WinType eval_delta =
			s.game_ply < 20 ? 0.10:
			s.game_ply < 40 ? 0.13:
 			                  0.15;

		if (   elapsed_from_ponderhit >= optimum/4
			&& max_eval >= second_eval + eval_delta
			)
		{
			if (o.debug_message)
				sync_cout << "info string interrupted by early exit , max_eval >= second_eval + " << eval_delta << " ,  max_eval = " << max_eval << " , second_eval = " << second_eval
				<< " , rest_optimum_po = " << rest_optimum_po << sync_endl;

			// 残り時間くりあげて使って、終了すべき。
			s.time_manager.search_end = s.time_manager.round_up(elapsed_from_ponderhit);
			return;
		}

以下の時間の切り上げ、よくなかった。

		// 経過時間がoptimum/4を超えていて、残りoptimum時間に max_evalより+0.02良いevalが返り続けても逆転しない。
		WinType second_eval_optimum_upperbound2 = (wc + rest_optimum_po * std::min(1.0, max_eval + 0.02)) /(mc + rest_optimum_po + delta);

		if (rest_optimum_po > 0 /* optimum時間が残っている */
			&& elapsed_from_ponderhit >= optimum/4
			&& max_eval >= second_eval_optimum_upperbound2
			)
		{
			if (o.debug_message)
				sync_cout << "info string interrupted by early exit , max_eval >= second_eval_optimum_upperbound2 , max_eval = " << max_eval << " , second_eval = " << second_eval
				<< " , second_eval_optimum_upperbound2 = " << second_eval_optimum_upperbound2
				<< " , rest_optimum_po = " << rest_optimum_po << sync_endl;

			// 残り時間くりあげて使って、終了すべき。
			s.time_manager.search_end = s.time_manager.round_up(elapsed_from_ponderhit);
			return;
		}
		// optimum時間を超えていて、残り時間に max_evalより+0.02良いevalが返り続けても逆転しないなら終了。
		WinType second_eval_optimum_upperbound3 = (wc + rest_maximum_po * std::min(1.0, max_eval + 0.02)) /(mc + rest_maximum_po + delta);

		if (rest_optimum_po == 0
			&& max_eval >= second_eval_optimum_upperbound3
			)
		{
			if (o.debug_message)
				sync_cout << "info string optimum time is over , max_eval >= second_eval_optimum_upperbound3 , max_eval = " << max_eval << " , second_eval = " << second_eval
				<< " , second_eval_optimum_upperbound3 = " << second_eval_optimum_upperbound3
				<< " , rest_optimum_po = " << rest_optimum_po << sync_endl;

			// 残り時間くりあげて使って、終了すべき。
			s.time_manager.search_end = s.time_manager.round_up(elapsed_from_ponderhit);
			return;
		}
  • やねうら王、持ち時間制御調整。
  • ふかうら王の持ち時間制御、刷新する。
    • まるごと書き直した。
    • FAST_ALLOCのコード掃除。

⇓これ良くない気がしてきた。

		// optimumを0%、maximumを100%として、何%ぐらい延長して良いのか。

		// 延長度 = evalの差について + 1番目と2番目の訪問回数の比について
		// evalの差について     = 差が0なら0%。差×r
		// 訪問回数の比について = ((2番目の訪問回数/1番目の訪問回数) - k)/(1-k)
		//     2番目の訪問回数/1番目の訪問回数 = k1以下のときに    0%になる
		//     2番目の訪問回数/1番目の訪問回数 = k2以上のときに  100%になる

		// TODO : パラメーターのチューニングすべき。
		const float k1 = 0.70000f;
		const float k2 = 1.00000f;
		const float r = 20.0f; // 勝率0.02の差 = 延長度40%
		const float eval_alpha = 0.02f; // evalの差に下駄履きさせる値。微差は拾い上げる考え。

		float eval_bonus  = std::min(std::max(float((second_eval - max_eval + eval_alpha) * r),0.0f),1.0f);
		float visit_bonus = std::max(float( (double(second_searched) / (double(max_searched) + 1) - k1)/(k2-k1)),0.0f);
		float bonus = std::max(std::min(eval_bonus + visit_bonus , 1.0f),0.0f);
		TimePoint time_limit = (TimePoint)(double(optimum) * (1.0 - bonus) + double(maximum) * bonus);
		if (elapsed_from_ponderhit >= time_limit)
		{
			if (o.debug_message)
				sync_cout << "info string interrupted by bonus limit , eval_bonus = " << eval_bonus << " , visit_bonus = " << visit_bonus
				<< " , time_limit = " << time_limit << " , max_searched = " << max_searched << ", second_searched = " << second_searched << sync_endl;

			// 残り時間くりあげて使って、終了すべき。
			s.time_manager.search_end = s.time_manager.round_up(elapsed_from_ponderhit);
			return;
		}

2024/1/29

  • 比較用にEXCLUDED_MOVE_FORCE_EVALUATE追加。

2024/1/27

  • makebook peta_shockコマンド、思考エンジンオプションのBookDirを無視してbook/を対象フォルダにしていたの修正。

2024/1/22

⇓ これくらいで調整してみるとどうか。

  • V778m 30分+5fに合わせて調整。
	int move_horizon;
	if (time_forfeit)
		move_horizon = MoveHorizon + 40 - std::min(ply , 40);
	else
		move_horizon = MoveHorizon - std::min(ply , 80);
  • V778l
		move_horizon = MoveHorizon + 10 - std::min(ply , 100);
  • V778k
	int move_horizon;
	if (time_forfeit)
		move_horizon = MoveHorizon + 40 - std::min(ply , 40);
	else
		// 40手目まで定跡で進行するとして80までに持ち時間の1/3,120までに1/3,160までに1/3を使うぐらいのペースになるように調整。
		// +20は調整項。+0だと80で1/2ぐらい使ってしまう。
		move_horizon = MoveHorizon + 20 - std::min(ply , 100);
  • V778j
	// 切れ負けであるか?
	bool time_forfeit = limits.inc[us] == 0 && limits.byoyomi[us] == 0;

	// 1. 切れ負けルールの時は、MoveHorizonを + 40して考える。
	// 2. ゲーム開始直後~40手目ぐらいまでは定跡で進むし、そこまで進まなかったとしても勝負どころはそこではないので
	//  ゲーム開始直後~40手目付近のMoveHorizonは少し大きめに考える必要がある。逆に40手目以降、MoveHorizonは40ぐらい減らして考えていいと思う。
	// 3. 切れ負けでないなら、100手時点で残り60手ぐらいのつもりで指していいと思う。(これくらいしないと勝負どころすぎてからの持ち時間が余ってしまう..)
	// (現在の大会のフィッシャールールは15分+inctime5秒とか5分+inctime10秒そんな感じなので、160手目ぐらいで持ち時間使い切って問題ない)
	int move_horizon;
	if (time_forfeit)
		move_horizon = MoveHorizon + 40 - std::min(ply , 40);
	else
		move_horizon = MoveHorizon - std::min(ply , 100);
■ やねうら王の持ち時間制御について

やねうら王の持ち時間制御について書く。

持ち時間制御とは、各局面の思考開始時にOptimalTime(最適時間)を算出して探索部に返すことを言う。

探索部はそのOptimalTimeをもとに今回の思考時間を決定する。反復深化の時の評価値のぶれが少ないなら早めに切り上げることもある。だいたい平均するとOptimalTimeの7割ぐらいの時間で思考を切り上げるようである。

では、OptimalTimeをどうやって決定しているかと言うと、大雑把には次式である。

まず、自分の指し手があと何手あるのかを決定する。

512手ルールなら思考エンジンオプションでMaxMovesToDrawは512に設定されているから、現在の手数(gamePly)とから算出できる。

restply = (MaxMovesToDraw - gamePly + 1) / 2

次にMoveHorizonという概念がある。これは終局までの平均手数を考慮して決めてある。

MoveHorizon = 160 / 2

この手数ぐらいはこのあと指すと思って時間配分をしなさいという定数である。この両者の小さい方の値が残りの自分の指し手の回数だと見積もっている。

restply2 = min(MoveHorizon , restply)

そして、フィッシャールールの時は、inctimeが手番が来るごとに加算されるから、残り時間は、

resttime = 持ち時間 + restply2 * inctime

となる。

つまり、

OptimalTime = resttime / restply2

みたいな感じになる。

ところが、実際は平均的にはOptimalTimeの7割ぐらいしか使わないので、余ってくる。

どういうことかと言うと、restply2 == MoveHorizonという状況において、

resttime = 持ち時間 + MoveHorizon * inctimeだから、
OptimalTime = 持ち時間 + MoveHorizon * inctime / MoveHorizon = 持ち時間/MoveHorizon + inctime

となるが、毎回OptimalTimeの7割しか使わないと仮定すると、この2手先では持ち時間は前回のOptimalTime * 0.7だけ減っていることになって、2手先では、

OptimalTime = (持ち時間 - 前回のOptimalTime * 0.7)/MoveHorizon + inctime

となる。これが定常状態になりうる。つまり、右辺の「前回のOptimalTime」と左辺の「(今回の)OptimalTime」が同じ値になって、(残り)持ち時間もずっと同じになった、そういう状態になりうる。右辺のOptimalTime == 左辺のOptimalTimeとしてこの方程式を解くと、

OptimalTime( 1 + 0.7/MoveHorizon) = 持ち時間/MoveHorizon + inctime

となって、持ち時間が減らないような、均衡がとれた状態になってしまう。

この状態は本意ではなく、フィッシャールールであるなら、200手目ぐらいには時間が秒読みぐらいになってても良いのであるから、あまり良い状態とは言えない。

もともとは、30分+inctime 1秒(切れ負け防止のためだけのフィッシャー)みたいなのを想定して上式のようになっていたのだが、最近の大会では、15分+inctime 5秒みたいなのが多くて、別に200手目ぐらいで残り持ち時間30秒ぐらいになってても良くね? みたいなところはある。

そう考えると、上式のままやるとしたら、フィッシャールールのときにgamePlyが増えていった時にMoveHorizonを減らすという補正をするのがお手軽である。

どれくらい増えてった時にどれくらい減らせば良いのかはわからないが、もう少し調整する必要がありそうだ。
  • ENGINE_VERSION、makefileの方から変更できるようにした。

2024/1/20

  • peta_shock_next2で指定された書き出す個数に局面数が満たない時に落ちてたの修正。
    • その周りでコンパイル時にwarning出てたの修正。

2024/1/19

root(初期局面)での先手の最善手の評価値が47。
先手用の定跡は、先手は最善手を選び、後手は任意(≒ランダム)に選ぶので未解決局面の評価値は47以上となります。

未解決局面763982局面をこの評価値で昇順sortした場合、
10000局面目 .. 評価値70
20000局面目 .. 評価値77
40000局面目 .. 評価値91
80000局面目 .. 評価値112
160000局面目 .. 評価値142
320000局面目 .. 評価値194
640000局面目 .. 評価値292
こんな感じになってました。

例えば、評価値194なのに実は先手悪いみたいな局面があるとして、その局面をさらに掘るためには、⇑のように列挙してsortして、32万局面分掘れば良いということになります。

ただ、普通は上から1万局面ほど掘ると、さらに未解決局面局面がまた出てくるので、それを掘るのを繰り返すので、そうするといつまでも評価値192の先手悪いみたいな局面は掘れないことになります。

そうは言っても評価値 192の局面の方が評価値70の局面よりは良い確率の方がずっと高いはずなので、前者を展開するアルゴリズムにはなかなかしにくい意味はあります。

しかしまあ、終盤の方の評価値 192は信用ならないので、進行度みたいなのを加味して考えないといけないような気がしています。(考え中)
makebook peta_shock_next2 user_book1_20240119080417.db peta_next.sfens 10000 eval_limit 400


makebook peta_shock_next2 user_book1_20240119080417.db peta_next.sfens 2000 eval_limit 400

("startpos moves 2g2f 8c8d 2f2e 8d8e 7g7f 4a3b 8h7g 3c3d 7i6h 2b7g+ 6h7g 3a2b 3g3f 7c7d 3i3h 2b3c 4g4f 8a7c 6i7h 7a6b 3h4g 5a4b 1g1f 6a5a 4i5h", 57)
+		[999]	("startpos moves 2g2f 8c8d 2f2e 8d8e 7g7f 4a3b 8h7g 3c3d 7i6h 2b7g+ 6h7g 3a2b 3i3h 2b3c 6i7h 6c6d 9g9f 1c1d 5i6h 7a6b 4i5h", 36)	std::pair<std::string,short>

16279544局面中
先手番用のleaf node : 763982
後手番用のleaf node : 1610163
やねうらお — 今日 12:19
平方根くらいになりますか。

ちょっと面白いデータが得られたので共有します。

先手用定跡は、先手は最善手の評価値と同じ指し手しか選ばない。後手はすべての指し手を選ぶ。
後手用定跡は、後手は最善手の評価値と同じ指し手しか選ばない。先手はすべての指し手を選ぶ。

この条件で、あと、評価値の絶対値が400を超えたらそれは必勝という扱いにして、それは解決済みの変化であるとします。

このとき、先手用定跡のleaf nodeが何局面ぐらいになるのか、要するに解決していない課題局面みたいなのがどれくらいのオーダーであるのかについてです。

いまペタショック定跡は16279544局面登録されているのですが、この定跡に対して、先手用定跡の未解決局面は、763982。後手用定跡の未解決局面は1610163でした。

わりとありますね…。😟
  • makebook peta_shock_next2コマンド追加。

コマンド例

makebook peta_shock_next2 user_book1_20240119080417.db peta_next.sfens 1000 eval_limit 400 from_startpos

上のコマンド例の1000は、先手がDBの最善手を選んだ時(後手はDBの任意の指し手)の末端の局面が500個、後手がDBの最善手を選んだ時(先手はDBの任意の指し手)の末端の局面が500である。

上のコマンド例の400は、(ペタショック化した時の)評価値の絶対値が400以内の指し手のみを辿ると言う意味である。

⇓追加した内容。

■ ペタショックNextについて

定跡を掘っていく時に、次に掘るべき局面をどうやって決定するかと言うと、ペタショックNext(コマンド名 makebook peta_shock_next)では、定跡上のPVのleaf moveで進めた局面を選出していた。1つ選出して、そのあと、そのleaf moveが無くなったと仮定して親に評価値を伝播しなおして、またroot(初期局面)からのPV leaf moveを調べて…を繰り返していた。

これは評価関数がある程度信頼できるならうまく機能するのだが、root付近で掘れていない枝が大量に残ることとなった。

そこで、leaf moveにガウスノイズを加えると言うのが改善のアイデアだった。これはうまく機能して、root付近で掘れていない枝はある程度潰すことができた。

しかし、PV leafを掘るというのがあまりいいアイデアではなくて、対策すべきは、相手がどの指し手をやってきてもちゃんと勝負の形になる(信頼できる)leafに辿り着けることである。

そのようなleafとは、つまり、プレイヤーが先手であるとしたら、先手 ⇨ 定跡DBの最善手を選ぶ , 後手 ⇨ 定跡DBのいずれかの指し手を選ぶ というのを繰り返して到達できるleaf nodeの集合であって、それらが信頼できることが大切なのである。

定跡の平均分岐がa手だとすると、30手目でのnodeはa ^ 30あるのだが、先手の最善手が1手のみ、後手の平均分岐がa手だとすると、a ^ 15で済む。これは、a ^ 30の平方根ぐらいで、要するに、その局面ぐらいはちゃんと調べておきましょうということである。

定跡局面が1億だとして、その平方根とは1万局面である。(わりと少ない)
これらを選出して優先して展開すべきではないかと思った。

ただし、展開するとしても、優先順位はつけるべきで、後手にとって評価値の良い順にするだとか、遷移確率の高い順にするだとか、何らか工夫は必要である。

…というのを考え中。

2024/1/18

  • peta_shockコマンドの合流チェック、Position::hash_key_after()を使って高速化。

    • 使えるの忘れてた。
  • makebook peta_shockにshrinkモード追加。(最善手と同じ評価値の指し手のみを書き出す)

makebook peta_shock user_book1_20240108165930.db user-shrinked.db shrink

やねうらお — 今日 11:09
ShogiBookTools、sort, merge, shrink(最善手と同じ評価値以外の指し手を削除) などのコマンドを実装した。

10億局面に対して行っても物理メモリは100MBほどしか必要としない。🙆 
これは次世代の定跡メンテナンスツールなのである。

https://github.com/yaneurao/ShogiBookTools/commit/b41bcb958ff3fe3e31045cbd34a53ee32cd5d421

2024/1/17

  • MoveHorizon、40手目までは少し下方修正するようにした。

    // 切れ負けルールの時は、MoveHorizonを + 40して考える。 // 逆に、ゲーム開始直後~40手目ぐらいまでは定跡で進むし、そこまで進まなかったとしても勝負どころはそこではないので // ゲーム開始直後~40手目付近のMoveHorizonは少し大きめに考える必要がある。逆に40手目以降、MoveHorizonは40ぐらい減らして考えていいと思う。 const int move_horizon = (time_forfeit ? MoveHorizon + 40 : MoveHorizon) - std::min(ply , 40);

やねうらお — 今日 22:50
定跡にhitして40手目ぐらいまで進んだ時と、定跡にhitしなくて10手目ぐらいで探索しないといけない時とで同じMoveHorizonなの、やっぱりやかしい気がする(勝負どころはいずれも50手目~100手目付近にあるはずで)

そんなわけで、これを考慮したMoveHorizonになるようになるように調整した。

https://github.com/yaneurao/YaneuraOu/commit/35e968eee224c90d069fc1d037a0f94e4e34ce85
  • MoveToHorizon 160 → 120に変更。
  • SfenPackerでwarning出てたの修正。
やねうらお — 今日 12:35
■ やねうら王のLEARN版について

LEARN版があるのは、TTをスレッドごとに分けないといけないからだ。
TTをなぜスレッドごとに分けないといけないのかと言うと、教師生成の時にTTをスレッド間で共用できないからだ。
教師生成を複数スレッドでやっているのは、教師ファイルがスレッド数だけ書き出されると扱いにくいと思ったからだ。
しかしいまやSSDが普通なのでファイルがいくら分かれようとそれでディスク書き出しが遅くなるわけでもないし、ファイル結合もそんなに時間がかかるわけではないし、1プロセス1スレッドで教師生成して、複数プロセス起動すればいいんじゃないのか、みたいなところはある。
しかしそうすると、NNUEはEvalShareの仕組みがないから、メモリをスレッド数分だけ食うんだよなー。😥 

LEARN版、無くしたいのになー。
  • 電竜戦HWTで持ち時間の使い方が下手すぎたの修正。
やねうらお — 今日 06:52
■ 電竜戦HWTで持ち時間の使い方が下手すぎる件

残り手数(=MTG)を計算する時、自分手番の残り手数だから、2で割らないといけないのだが、それ割ってなかった。😅 
MaxMovesToDrawが256の時はそっちは2で割ってるから(残り)128手でうまく指せていたのだが、MaxMovesToDrawが256の時は256手とMoveToHorizon(=160)のminで160になってて、残り320手に均等に持ち時間配分する感じで指してた。

これだと中盤の難所で時間使わないから弱くなる。

正しくは、このMoveToHorizonも2で割るべきであった。

2024/1/13

mate -6 でてたのに途中で違うことになってたやつ。 ponderではちゃんと読めてたのに、ponder外れて読めてないのか…。時間なかったみたいだし、しゃーないか。

>1:ponderhit
<1:info depth 31 seldepth 10 score mate -10 nodes 2254901 nps 4262572 hashfull 1 time 529 pv G*9d P*3c 9d9c 5c9c P*6a P*7c 2e1g+ R*2f 1f2f
<1:bestmove G*9d ponder P*3c
>T:-0094KI,'* 100000 +0033FU -9493KI +5393RY -0061FU +0073FU -2517NK +0026HI -1626TO
<T:-0094KI,T0
>1:position startpos moves 2g2f 8c8d 2f2e 8d8e 7g7f 4a3b 8h7g 3c3d 7i8h 2b7g+ 8h7g 3a2b 3i3h 2b3c 6i7h 7c7d 3g3f 7a6b 2i3g 5a4b 4g4f 1c1d 9g9f 6b7c 4i4h 7c6d 3h4g 6a5b 2h2i 4c4d 4g5f 7d7e 7f7e 6d7e 2e2d 2c2d P*2e 3d3e 2e2d P*2g 2i2g P*2e 3f3e 3c2d B*5e 8b8d 5i6h 8e8f 7g8f 7e8f 8g8f 8d8f P*8g 8f8d S*7e 8d8e P*7f P*3f 3g4e S*2f 2g2i 3f3g+ 4h5h 2f3e 8g8f 8e8c 5e9a+ 4d4e 9a9b P*8b 7e7d N*6d 6h7g 6d5f 5g5f B*3h 2i3i 3h5f+ 7d8c 8b8c 9b8a P*3h 3i5i 3g4g 5h4g 5f4g R*9b S*5h 5i5h 4g5h 8a6c P*6b N*3d 4b3c 6c5b S*4c 5b6a R*6i G*7i 5h6g 7g8h 6g7h 7i7h 3c3d P*6g P*7g 8i7g G*8i 8h8g 3e3f 9b6b+ 3b3c N*5e 8i7i 7h7i 6i7i+ S*8h 7i1i 5e4c+ 3d3e 4c3c 2d3c 4f4e G*8i G*7h 3e2f 6b6f 8i8h 7h8h N*3d S*4g G*3g 6a8c N*4f 4g3f 3g3f 6f5g S*2g 8f8e 3h3i+ 5g5c 2f1g B*7a 3d2f 7a9c+ 3c3d 8g8f 4f3h+ G*8b 1d1e 7g6e 1e1f 6e7c+ 2g2h+ 4e4d 1g2g 8c6a 3h3g 6a3d 3i3h G*9h 1f1g+ S*6b 1i7i 9c6f 3h3i 8f7e 2f3h+ 3d2e 1g1f 2e5b 2a1c 4d4c+ 1c2e 6f1a L*9d 7e8d 7i7f 8h7g 7f6e S*5e P*8a 8b7b 6e5f 8d8c 5f5i 8c9b 9d9f 9b9a 3i2i L*2b P*4b 4c4b 9f9h+ L*9b G*7d 7c7d 5i7i G*2a S*8b 7b8b 7i4i P*9c 4i4b 5b4b 8a8b S*8a G*9d P*3c
>1:go ponder btime 377000 wtime 12000 binc 10000 winc 10000
<1:info depth 26 seldepth 8 score mate -8 nodes 454184 nps 15139466 hashfull 0 time 30 pv 9d9c 5c9c P*6a R*1c 3f2f 6b6a 9h9i
<T:+0063HI,T0
>1:stop
<1:bestmove 9d9c ponder 5c9c
>1:position startpos moves 2g2f 8c8d 2f2e 8d8e 7g7f 4a3b 8h7g 3c3d 7i8h 2b7g+ 8h7g 3a2b 3i3h 2b3c 6i7h 7c7d 3g3f 7a6b 2i3g 5a4b 4g4f 1c1d 9g9f 6b7c 4i4h 7c6d 3h4g 6a5b 2h2i 4c4d 4g5f 7d7e 7f7e 6d7e 2e2d 2c2d P*2e 3d3e 2e2d P*2g 2i2g P*2e 3f3e 3c2d B*5e 8b8d 5i6h 8e8f 7g8f 7e8f 8g8f 8d8f P*8g 8f8d S*7e 8d8e P*7f P*3f 3g4e S*2f 2g2i 3f3g+ 4h5h 2f3e 8g8f 8e8c 5e9a+ 4d4e 9a9b P*8b 7e7d N*6d 6h7g 6d5f 5g5f B*3h 2i3i 3h5f+ 7d8c 8b8c 9b8a P*3h 3i5i 3g4g 5h4g 5f4g R*9b S*5h 5i5h 4g5h 8a6c P*6b N*3d 4b3c 6c5b S*4c 5b6a R*6i G*7i 5h6g 7g8h 6g7h 7i7h 3c3d P*6g P*7g 8i7g G*8i 8h8g 3e3f 9b6b+ 3b3c N*5e 8i7i 7h7i 6i7i+ S*8h 7i1i 5e4c+ 3d3e 4c3c 2d3c 4f4e G*8i G*7h 3e2f 6b6f 8i8h 7h8h N*3d S*4g G*3g 6a8c N*4f 4g3f 3g3f 6f5g S*2g 8f8e 3h3i+ 5g5c 2f1g B*7a 3d2f 7a9c+ 3c3d 8g8f 4f3h+ G*8b 1d1e 7g6e 1e1f 6e7c+ 2g2h+ 4e4d 1g2g 8c6a 3h3g 6a3d 3i3h G*9h 1f1g+ S*6b 1i7i 9c6f 3h3i 8f7e 2f3h+ 3d2e 1g1f 2e5b 2a1c 4d4c+ 1c2e 6f1a L*9d 7e8d 7i7f 8h7g 7f6e S*5e P*8a 8b7b 6e5f 8d8c 5f5i 8c9b 9d9f 9b9a 3i2i L*2b P*4b 4c4b 9f9h+ L*9b G*7d 7c7d 5i7i G*2a S*8b 7b8b 7i4i P*9c 4i4b 5b4b 8a8b S*8a G*9d R*6c
>1:go btime 387000 wtime 12000 binc 10000 winc 10000
<1:info depth 10 seldepth 14 score cp -2574 nodes 770772 nps 22022057 hashfull 0 time 35 pv 9d9c 6c9c+ P*6a 6b6a+ P*4a 4b4a 1f1e 9i9h
<1:bestmove 9d9c ponder 6c9c+
>T:-9493KI,'* 2574 +6393RY -0061FU +6261NG -0041FU +4241UM -1615TO +9998KY
<T:-9493KI,T0
>1:position startpos moves 2g2f 8c8d 2f2e 8d8e 7g7f 4a3b 8h7g 3c3d 7i8h 2b7g+ 8h7g 3a2b 3i3h 2b3c 6i7h 7c7d 3g3f 7a6b 2i3g 5a4b 4g4f 1c1d 9g9f 6b7c 4i4h 7c6d 3h4g 6a5b 2h2i 4c4d 4g5f 7d7e 7f7e 6d7e 2e2d 2c2d P*2e 3d3e 2e2d P*2g 2i2g P*2e 3f3e 3c2d B*5e 8b8d 5i6h 8e8f 7g8f 7e8f 8g8f 8d8f P*8g 8f8d S*7e 8d8e P*7f P*3f 3g4e S*2f 2g2i 3f3g+ 4h5h 2f3e 8g8f 8e8c 5e9a+ 4d4e 9a9b P*8b 7e7d N*6d 6h7g 6d5f 5g5f B*3h 2i3i 3h5f+ 7d8c 8b8c 9b8a P*3h 3i5i 3g4g 5h4g 5f4g R*9b S*5h 5i5h 4g5h 8a6c P*6b N*3d 4b3c 6c5b S*4c 5b6a R*6i G*7i 5h6g 7g8h 6g7h 7i7h 3c3d P*6g P*7g 8i7g G*8i 8h8g 3e3f 9b6b+ 3b3c N*5e 8i7i 7h7i 6i7i+ S*8h 7i1i 5e4c+ 3d3e 4c3c 2d3c 4f4e G*8i G*7h 3e2f 6b6f 8i8h 7h8h N*3d S*4g G*3g 6a8c N*4f 4g3f 3g3f 6f5g S*2g 8f8e 3h3i+ 5g5c 2f1g B*7a 3d2f 7a9c+ 3c3d 8g8f 4f3h+ G*8b 1d1e 7g6e 1e1f 6e7c+ 2g2h+ 4e4d 1g2g 8c6a 3h3g 6a3d 3i3h G*9h 1f1g+ S*6b 1i7i 9c6f 3h3i 8f7e 2f3h+ 3d2e 1g1f 2e5b 2a1c 4d4c+ 1c2e 6f1a L*9d 7e8d 7i7f 8h7g 7f6e S*5e P*8a 8b7b 6e5f 8d8c 5f5i 8c9b 9d9f 9b9a 3i2i L*2b P*4b 4c4b 9f9h+ L*9b G*7d 7c7d 5i7i G*2a S*8b 7b8b 7i4i P*9c 4i4b 5b4b 8a8b S*8a G*9d R*6c 9d9c 6c9c+
>1:go ponder btime 387000 wtime 22000 binc 10000 winc 10000
<1:info depth 21 seldepth 6 score mate -6 nodes 120572 nps 4637384 hashfull 0 time 26 pv P*6a P*7c 8b8c G*1g 2e1g+
<T:+6393RY,T0
>1:ponderhit
<1:bestmove P*6a ponder P*7c
■ やねうら王標準定跡バイナリフォーマットについて

しばらく考えていたのだが、Pythonで書き出す場合、いまどきのSSDは速いので書き出しがボトルネックになってるわけではなさそうなのだ。PackedSfenに変換したりするとそっちの方が遅くなりそうな感じである。

ストレージ、確かにもったいないけど、7zipで圧縮した場合、旧形式の方はテキストで冗長性が高いので1/8ぐらいのサイズになるようである。

それに対して、PackedSfenはハフマン符号化なので、圧縮がほとんど利かない。これでは、旧形式の1/3に収まったとしても圧縮後のサイズで負けてしまう。

そう考えると、バイナリフォーマットみたいな新しい形式に対して諸々のツールセットを実装する手間みたいなものを考えると全く得をしていないとも言えると思う。

いまどきSSDも安いので、23GB(1億局面時)ぐらいどうってことないから、いまの形式のまま行こうと思う。10億局面掘ったところで230GB。7zipで圧縮したら40GB程度。圧縮がかからない80GBよりずっといいと思う。


やねうらお — 今日 05:46
私の実装上はbook_moveのvalueの更新がなくなったら、ループを抜けるようにしています。

ペタショック定跡(現時点で1200万局面程度)、その処理入れてもMAX_PLYまでループ抜けないんですよねー。ループが複雑に絡み合ってて、なんかそういうことになってるんだと思うんですけど、どういう現象なのか詳しいことはよくわからないです。😅 

GitHub Sponsorsで支援してくださった方へのNews Letterでペタショック定跡を配布しようと思っているので、よかったら調べてみてくださいな。

やねうらお — 今日 05:53
あと、出次数が0のnodeはそれを親として持つノードは存在しないのでこのループに入る前に削除していけるので、そうやって削除して、それによって出次数が0になったnodeも再帰的に削除して、そうすると、元ある定跡ノード数の1/10程度になります。

なので、このループ自体はMAX_PLY回回ったところで、そこまで時間はかからないです。(1000万局面の定跡DBに対して1分程度)

どちらかと言うと合流チェックにその10倍ぐらいの時間を要してますね…。1局面ずつ全合法手生成して、1手進めてその局面のhash keyが一致する局面があるかをC++のunordered_mapで調べてるだけなんですけど、1000万局面×平均合法手100通り = 10億回それをやる時間が5分ぐらいかかりますね。😢

2024/1/12

やねうらお — 今日 14:25 ■ 千日手スコアを0以外に変更する必要性があるのかについて

千日手スコアが0の場合、初期局面で先手+70ぐらいになる。(少なくとも結論は千日手ではなさそうだから)

千日手スコアが先手にとって-50だと、それを回避する手順にしないといけないから、これよりは小さな値となる。先手+40とか30とか。

千日手スコアが先手にとって-100だとおそらく先手は+0にすらできない。マイナスの値となる。

定跡手順の研究には良いのかも知れないけども、大会でこういう定跡が使えるかと言うとちょっと疑問が残るところではある。先手なのに最善の変化が+0の定跡、大会で使いたいかと言うとそれはちょっと…。

いま、ペタショック定跡は1100万局面ほど掘ったところだが、後手だからと言ってそんなに千日手になるわけではなさそう。

floodgateの対局を見ている感じだと定跡を抜けたあとに千日手になることはあるが、定跡レベルで千日手になることはほぼないっぽい。相手が千日手にならない変化を選ぶからというのが大きそう。相手にとっては、探索中はその周辺の局面は評価値0±30ぐらいに見えているので、上振れした評価値(+30)のノードを見つけて、そこを目指そうとするからである。

そんなわけで、千日手スコア0にしているからと言って、実戦で後手が定跡レベルで千日手にしてしまうことはわりと稀で、大会で千日手にしたくないからと言って千日手スコアを変更して定跡を生成する意味があまりなさそうである。

千日手スコアを0から変更しないなら、定跡はflipした局面も共通で良いし、ペタショック化も、先後で異なる局面を書き出さなくていいから処理が単純になり、省メモリで済むし、定跡のバイナリフォーマットもなくてもいいかな、ぐらいの感じになる。いいこと尽くめである。

http://wdoor.c.u-tokyo.ac.jp/shogi/view/show-player.cgi?event=LATEST&filter=floodgate&show_self_play=1&user=YO810Li-peta14M-3700X%2B36f1d21a348e2eb1501be421594d59bc

2024/1/10

peta_shock 10020882局面、公開するか? ⇨ 公開できる準備だけ整えておく。

やねうらお — 今日 20:18
■ ペタショック化の時に千日手スコアを0以外に変更する件

書き出す局面数が2倍になるかと思ったのだけど、ほとんどの局面では指し手と評価値は変わらないと思うので、その場合、片側の局面を書き出すだけで良く(やねうら王のFlippedBookオプションがtrueなら、どちらかが書き出されていれば定跡にhitするので)、つまりは、1.1倍ぐらいで済むのではないかと思った。
  • peta_shock化、DrawStateの掃除
	// 千日手の状態
	// 最終的には書き出した定跡DBのdepthに反映させる。
	// depth
	//  +1000 : 先手は千日手を打開できない。
	//  +2000 : 後手は千日手を打開できない。
	//  +3000 : 先手・後手ともに千日手を打開できない。
	struct DrawState
	{
		DrawState(u8 state):state(state){}

		// bit0 : 先手は千日手を打開する権利を持っていない。
		// bit1 : 後手は千日手を打開する権利を持っていない。

		// つまり、
		//   00b : 先後千日手を打開する権利を持っている。
		//   01b : 先手は(後手が千日手を選んだ時に)千日手を打開できない。(後手は打開できる)
		//   10b : 後手は(先手が千日手を選んだ時に)千日手を打開できない。(先手は打開できる)
		//   11b : 先手・後手ともに千日手を打開できない。

		u8 state;

		/*
			後手番で、11bと00bとの指し手があった時に 後手は00bの指し手を選択するが、親には01b を伝播する。
			この時、01bの指し手を選択すると、これがループである可能性がある。

			A→B→C→D→A
				  D→E
			となっている場合、後手が誤ったほうを選択するとループになる。

			だから、bestの選出は、後手は
			  00b > 01b > 10b > 11b
			先手は、
			  00b > 10b > 01b > 11b
			の順番でなければならない。
		*/

		// 比較オペレーター
		bool operator==(const DrawState& v) const { return state==v.state;}
		bool operator!=(const DrawState& v) const { return !(*this==v); }

		// 手番cにおいて評価値が同じ時に this のほうが yより勝るか。
		bool is_superior(DrawState y, Color c) const
		{
			// 先手にとってのbestを選出する順番
			constexpr int black_[] = {1,3,2,4};
			// 後手にとってのbestを選出する順番
			constexpr int white_[] = {1,2,3,4};

			if (c==BLACK)
				return black_[this->state] < black_[y.state];
			else
				return white_[this->state] < white_[y.state];
		}

		// 手番cの時、評価値が同じでDrawStateだけ異なる指し手がある時に
		// このnodeのDrawStateを求める。
		// 
		// 例)
		//   後手番で評価値が同じである 00bと11bの指し手があるとして
		//   後手はどちらかを選べるので、後手には千日手の権利があることになる。(先手はそれを決める権利がない)
		//   ゆえに、このとき、このnodeの draw_stateは、01b となる。
		//
		//   つまり、
		//     手番側のbitは、0と1があるなら0(bit and)
		//     非手番側のbitは、0と1があるなら1(bit or)
		//   をすればいいということである。
		void select(DrawState y, Color c)
		{
			*this = select_static(*this, y , c);
		}

		// ↑のstatic版
		static DrawState select_static(DrawState x , DrawState y , Color c)
		{
			u8 our_bit  =  c == BLACK ? 1 : 2;
			u8 them_bit = ~c == BLACK ? 1 : 2;
			
			return ((x.state & our_bit ) & (y.state & our_bit ))
				 | ((x.state & them_bit) | (y.state & them_bit));
		}

		// depthに変換する。
		// stateを1000倍しておく。
		u16 to_depth() const
		{
			return state * 1000;
		}
	};
やねうらお — 今日 19:11
千日手の価値を適切に設定できているのであれば、どれを選んでも同じと見なしていいのかなと思っています。

そうかもですなー。私が最初に実験してた時は、角換わりで評価値が0の局面がわりとあって、そっちにいく指し手を選んで欲しかったので、DrawStateが必要だと思ったんですけど、まあ、千日手自体の価値を変更して(後手は)+200とかにするなら、+200を選ぼうと、評価値+200を選ぼうと、同じ意味ではありますなー。🙄 

このDrawStateの実装、わりと面倒でややこしいので、効果に乏しいから削除しますかね…。🙆‍♂️
  • USE_SFEN_PACKERのシンボルが定義されていない時にPackedSfenをclass定義しないように。

  • PackedSfenの駒落ち、cshogiの出力と完全にバイナリ互換になるように調整した。

    • UnitTestにPackedSfenのバイナリチェック追加。
  • PackedSfenの駒落ち対応。

  • PackedSfenの駒落ちのUnitTest追加。

  • is_promoted_piece ⇨ is_non_promotable_piece とrename

  • is_promoted_piece新たに実装。

  • UnitTest通らなくなっていたの修正。

    • Position::max_repetition_plyをstatic memberに変更。
    • UnitTestの時のランダムプレイヤーによる対局、固定乱数になっていなかったの修正。

2024/1/9

やねうらお — 今日 22:20
テラショックコマンド、お掃除した。

他の定跡コマンドも、Pythonで書けるものはPythonで書いた実装にして、速度と省メモリが問われるものは最新の技術で実装しなおして…。🤔 

まあいいか、とりあえず、今回はこれだけ。

https://github.com/yaneurao/YaneuraOu/commit/d5adb2db9a7bda92b2c0e8d3a2e145d1fef2b1e2
  • テラショック定跡コマンドの削除

    • いまやペタショック定跡コマンドが完全上位互換なので…。
    • 例えば、次に掘る局面は makebook peta_shock_nextで求めて、makebook thinkでその局面について思考させる、という動作を繰り返すbatファイルを書けば、それだけで自動定跡掘りができる。
  • テラショック定跡のドキュメント(定跡の作成.mdから削除)

## makebook build_tree

> makebook build_tree read_book.db write_book.db

このコマンドは、テラショック化コマンドと呼ばれるものです。

read_book.dbには、thinkコマンドで実戦で出現した局面に評価値がついているものとして、leaf node(末端の局面)についている評価値を使ってMinimax探索のようなことをして、それぞれの局面での最善手write_book.dbに書き出す機能です。

⚠ IgnoreBookPlyオプションの値は反映されます。これをtrueにしている場合、手数無視で定跡にhitするものとしてtreeを生成します。(write_book.dbに書き出されます) IgnoreBookPlyはtrueにしたほうが良いと思います。

> makebook build_tree read_book.db write_book.db black_contempt 50 white_contempt 150

build_treeで千日手に至るときにそのとき、千日手を評価値いくらにみなすかというのを先手・後手個別に設定できます。

👉 例えば、black_contempt = 50を指定すると先手の千日手時のスコアを-50とみなします。

## makebook extend_tree

> makebook extend_tree read_book.db read_sfen.txt write_sfen.txt

定跡ツリーの延長を行います。

上の例では、read_book.dbの定跡に対して、read_sfen.txtのsfenの局面から開始して定跡DBの末端の局面まで行き、延長する枝をwrite_sfen.txtに書き出します。

延長する枝を選ぶアルゴリズム)

read_sfen.txtの局面から、評価値が先手で-50以上、後手で-150以上の値がついている枝だけを延長していきます。

💡 ここで言う「枝」とは指し手とそこに続く局面のことです。定跡を木のような構造だと見立てているので、このような表現になっています。

💡 ここで書き出されたsfenファイルを、makebook thinkコマンドで思考させるのを繰り返すと定跡ツリーが成長していきます。

read_sfen.txtにはこの延長を行う開始局面を複数指定できます。(わからなければ、"startpos"とだけ書いたテキストを用意すれば、平手の開始局面からになるのでそれで問題ないはずです。)

"startpos moves.."の形で複数の局面を指定します。"sfen ..."や、"sfen ... moves ..."のような形式で指定することもできます。

🌙 このコマンドにIgnoreBookPlyオプションの値は反映されます。

> makebook extend_tree read_book.db read_sfen.txt write_sfen.txt black_eval_limit -200 white_eval_limit -300

extend_treeのときに展開する枝の評価値の下限を先手/後手個別に指定できます。

- black_eval_limit 延長する時の、先手の指し手の評価値の下限
- white_eval_limit 延長する時の、後手の指し手の評価値の下限

> makebook extend_tree read_book.db read_sfen.txt write_sfen.txt extend_range -1 1

extend_rangeを指定すると、その範囲のleaf node(末端の局面)の候補手だけを延長対象とします。

(MultiPVで思考させ複数の指し手が登録されている場合、このextend_rangeの範囲内の候補手で進めた局面だけが延長されます)

extend_range -1 1 と指定したならば、leaf nodeでextend_treeの引き分けの評価値(-1,0,+1)の局面だけを延長します。makebook thinkのときの引き分けの評価値を0にしておけば、千日手局面が延長されます。

👉 引き分けの評価値がついているけども、定跡ツリー上ではまだ千日手のように循環していない場合、延長することによって、そこまでの手順に循環(合流)するので、この延長をさせたいことがあります。

extend_rangeとblack_eval_limit,white_eval_limitの併用は可能です。(leaf以外の局面で、そこを辿るかどうかをblack_eval_limit/white_eval_limitで制限しつつ、leafで延長するところをextend_rangeで制限する)

## makebook endless_extend_tree

> makebook endless_extend_tree book/user_book1.db book/kadai_sfen.txt book/think_sfen.txt depth 8 startmoves 1 moves 32 loop 100 black_eval_limit -50 white_eval_limit -150 nodes 1000000000

makebook extend_treeとthinkを合体させたコマンド。

think_sfen.txtのところは、思考対象局面を書き出す一時ファイル。

- このときbook/kadai_sfen.txtに "startpos" とだけ書いておけば、平手の初期局面が課題局面になるので、そこから自動的に定跡が掘られていきます。
- loopの回数だけ繰り返されます。
- max_game_ply(最大手数)の指定も可能。
- 検討用の局面をbook/kadai_sfen.txtに入れて、そこから定跡を掘るのに使えるかと思います。

例)
思考エンジンを起動して、
	Hash 4096
	Threads 8
	makebook endless_extend_tree book/user_book1.db book/kadai_sfen.txt book/think_sfen.txt depth 36 startmoves 1 moves 70 loop 100 black_eval_limit -50 white_eval_limit -150 nodes 1000000000
みたいな感じで。
🌙 このコマンドにIgnoreBookPlyオプションの値は反映されます。

> makebook endless_extend_tree book/user_book1.db book/kadai_sfen.txt book/think_sfen.txt depth 8 startmoves 1 moves 32 loop 100 black_eval_limit -50 white_eval_limit -150 nodes 1000000000 extend_range -1 1

endless_extendの千日手局面だけを延長したい時。

## makebook s_tera

> makebook s_tera

スーパーテラショック定跡の生成コマンドです。

- スーパーテラショック定跡についてはこちらの記事をご覧ください。👉 [スーパーテラショック定跡が76歩に34歩を全否定](https://yaneuraou.yaneu.com/2021/11/05/super-tera-shock-book/)

⚠ これは、ふかうら王のみ使えるコマンドです。

|パラメーター|意味|
|-|-|
|read_book 【ファイル名】| 前回このコマンドで書きだした定跡ファイル名(デフォルトでは "book/read_book.db")|
|write_book 【ファイル名】| 今回書き出す定跡ファイル名  (デフォルトでは "book/write_book.db")|
| root_sfens_name | 探索開始局面集のファイル名。デフォルトでは"book/root_sfens.txt"。<br/>このファイルがなければ平手の初期局面から。<br/>平手の初期局面をrootとすると後手番の定跡があまり生成されないので(先手の初手は26歩か34歩を主な読み筋としてしまうので)初期局面から1手指した局面をこのファイルとして与えたほうがいいかも?<br/>あと駒落ちの定跡を生成するときは同様に駒落ちの初期局面と、そこから1手指した局面ぐらいをこのファイルで指定すると良いかも。|
|kif_sfens_name | 棋譜のファイル名。この棋譜上の局面かつPVまわりを思考させることもできる。|
|book_save_interval| この局面数だけ思考するごとに定跡ファイルを書き出す。<br/>"book/read_book.db.000001"のようなファイルに通しナンバー付きで書き出していく。</br>このコマンドが終了するときにも書き出すので、数時間に1度保存される程度のペースでも良いと思う。|
|nodes_limit| 1局面について思考するnode数。30knps出るPCで30000を指定すると1局面1秒。|
|think_limit| この局面数思考したらこのコマンドを終了する。|
|search_delta |PVのbest_valueとの差がsearch_deltaのleaf nodeを拾い上げて思考する。<br/>このパラメーターに0を指定すると、その処理はskipされる。|
|search_delta_on_kif | PVのbest_valueとの差がsearch_delta_on_kifのleaf nodeでかつ、<br/>kif_sfens_nameで指定した棋譜上の局面について思考する。<br/>このパラメーターに0を指定すると、その処理はskipされる。|

それぞれのファイルフォーマット

入力)
	book/root_sfens.txt

	// 探索開始局面
	例)
	sfen lnsgkgsnl/1r5b1/ppppppppp/9/9/9/PPPPPPPPP/1B5R1/LNSGKGSNL b - 1
	↑のようなsfen形式か、
	startpos moves 7g7f ...
	のようなUSIプロトコルの"position"コマンドとして送られる形式でも可。
	// この場合、それぞれの局面が探索開始対象となる。

	book/kif_sfens.txt
	読み込ませたい棋譜。この棋譜上の局面かつ、PVまわりを思考する。
	例)
	startpos moves 7g7f ...
	// このファイルはなければなくとも良い。

	book/read_book.db
	前回出力された出力ファイル(book/write_book.db)をread_book.dbとrenameしたもの。
	// 配置するとその続きから掘ってくれる。
	(初回はこのファイルは不要)

出力)
	book/write_book.db
	→ このあと、このファイルをテラショック化コマンドでテラショック定跡化して使う。

	例)
	> makebook build_tree book/write_book.db book/user_book1.db

注意点)

	高速化のために、局面をSFEN文字列ではなく、局面のhash値で一致しているかどうかをチェックするので
	局面のhash値は衝突しないように128bit以上のhashを用いる。(べき)

	→ 具体的には、config.hでHASH_KEY_BITSのdefineを128にしてビルドする。
## makebook stera_convert

> makebook stera_convert

スーパーテラショック定跡コマンド("makebook stera")で生成した定跡を超高速にテラショック化するコマンドです。

💡 テラショック化とは、定跡ツリー上のleaf node(末端の局面)の評価値を用いてMinimax探索のようなことをして、一番評価値の良いleaf nodeに到達するような定跡に変換すること。

book/read_book.db
を読み込んで
book/write_tera_book.db
を書き出す。

前者は、スーパーテラショック定跡コマンドで生成された定跡。
(デフォルトでは book/write_book.db として書き出されている)


やねうらお — 今日 20:33 ■ 定跡コマンドの大掃除について

・ペタショック定跡コマンドがテラショック定跡コマンドの完全上位互換になった(と思う)ので、テラショック定跡コマンドを削除する。 ・定跡のマージなどのコマンドはあるが、設計自体が古いので、もうちょっと自由なことができるように実装しなおす。(かも) ⇨ 1億局面ぐらいの局面を扱いたいので、従来のものだと遅い&メモリ消費しすぎるので…。 ・やねうら王に棋譜を与えて、その局面について思考させるコマンド(定跡を作る手助けをするコマンド)があるが、あれ、いまどきはPythonのスクリプト等でやるべきで、やねうら王側にそんなコード残しておくとメンテナンスの手間とかが馬鹿にならない気がしているので削除するかも。



- ExtMoveからMove16へのcastがambiguousでコンパイルエラーになっていたの修正。

ペタショック化コマンド、局面をメモリ上に保持しないことにした。(ファイルに書き出す。定跡DBはsortされていることを前提として良いので、ノードの合流によって局面の増加・減少はないから、書き出した順でSFEN文字列を読み直してそれをそのまま最終的なファイルにSFEN文字列として書き出して良い。)

これで1億局面のペタショック化(定跡ファイル25GB)が35GB程度のメモリで出来るようになった。1億局面まではこれで凌げそう。そこから先はわからん…が、1億局面掘るのに1年ぐらいかかるので、とりあえず、これでしばらく困らなさそう。

https://github.com/yaneurao/YaneuraOu/commit/f5c0e65e363815f8777499918af9d18111efa764 https://github.com/yaneurao/YaneuraOu/commit/5e74e989e37c76b1eb6ac0b87b9b445587a0b418 https://github.com/yaneurao/YaneuraOu/commit/cec894ec03834497441266e43fa3eed2e087d0eb

やねうらお — 今日 05:09 あとは局面のhash keyからBookNode配列へのindexをunordered_mapで持ってて、これがメモリもったいない。こんなの、局面の合流チェックで使うだけなので、配列に持って、hash keyでsortして二分探索すればメモリは節約できる…のだが、O(log N)で、1億局面で27回ぐらいメモリに離散的なアクセスしないといけなくて、合流チェックが27倍ぐらい遅くなりかねない。

いま、1000万局面のペタショック化で合流チェックに5分ぐらいかかっているので、1億局面で50分として、それが27倍になったら、たまらん気もする。😢

そんなわけで、これでもうしばらくいじらんとく。


- peta_shockコマンド、packed_sfenをメモリに持つのやめてテンポラリファイルに書き出し、省メモリ化する。
- 構造体、Move → Move16に変更した。Valueはintからs16に変更。

やねうらお — 今日 04:45 ペタショック化コマンド、局面をメモリ上に保持しないことにした。(ファイルに書き出す。定跡DBはsortされていることを前提として良いので、ノードの合流によって局面の増加・減少はないから、書き出した順でSFEN文字列を読み直してそれをそのまま最終的なファイルにSFEN文字列として書き出して良い。)

これで1億局面のペタショック化(定跡ファイル25GB)が35GB程度のメモリで出来るようになった。1億局面まではこれで凌げそう。そこから先はわからん…が、1億局面掘るのに1年ぐらいかかるので、とりあえず、これでしばらく困らなさそう。

https://github.com/yaneurao/YaneuraOu/commit/f5c0e65e363815f8777499918af9d18111efa764 https://github.com/yaneurao/YaneuraOu/commit/5e74e989e37c76b1eb6ac0b87b9b445587a0b418 https://github.com/yaneurao/YaneuraOu/commit/cec894ec03834497441266e43fa3eed2e087d0eb


sfen文字列の末尾に以前は'0'が書き出されていたが、これを削って出力するようにしたら一致してるかわからなくなったので"sfen"で始まる行の末尾の" 0"を削除するスクリプトを書いた。

def process_file(input_file_path, output_file_path): with open(input_file_path, 'r') as file: lines = file.readlines()

with open(output_file_path, 'w') as file:
    for line in lines:
        # Check if the line starts with "sfen" and ends with " 0"
        if line.startswith("sfen") and line.endswith(" 0\n"):
            # Remove the last 2 characters (" 0")
            line = line[:-3] + "\n"
        file.write(line)

process_file(r'C:\Users\yaneen\doc\VSCodeProject\YaneuraOu\YaneuraOu-Dev\build\NNUE\book\user_book2_1.db', r'C:\Users\yaneen\doc\VSCodeProject\YaneuraOu\YaneuraOu-Dev\build\NNUE\book\user_book2_1a.db')

peta_shock化。

修正後
8630821局面で変換元DBファイル1986MB、変換時の使用メモリ4405MB。hash_key_to_index解放後、3867MB。変換時間、16分20秒。
⇨ なぜかわからんがメモリ余分に食って、時間余計かかるようになっただけなので、やめるか…。
⇨ 指し手重複して登録してるくさいなー。だとして、メモリ使用量減るどころか増えてるので良くない改造か?
⇨ WriteLineのあとFlush()呼び出さないから、buffer食ったままになってたのか?
// 3783MBになった。32*8630821=263MB。4055-263=3792。ぴったり合った。これが原因だったらしい。Step IIで3115MB。hash_key_to_index解放後2575MB。
⇨ これでうまく処理できてたら、採用。readもbufferingオフにすべきではないか。
→ Move16に変更して、Valueをintからs16にした。
 3450MB。Step IIで

// 修正前のコードにしたが、4055.4MB。なんか増えてるな…。これでまだバグっておるのか…。hash_key_to_index解放後、3510MB。

修正前
8294952局面で変換元DBファイル1909MB、変換時の使用メモリ3193MB。hash_key_to_index解放後、2759MB。変換時間、15分30秒。



USI_Hash 0
SkipLoadingEval true
makebook peta_shock user_book1_20240108165930.db user_book2.db

write book/user_book2.db
[ PetaShock Result ]
converged_moves  : 1395360
retro_counter1   : 1118115
retro_counter2   : 153545
write book nodes : 1271660

- peta_shockコマンド、定跡がsortされているを前提とする。
  - sortされてなかったらmakebook sortコマンドを使ってsortしてから適用すること。


- peta_shock_next、packed_sfenを用いるのをやめる。

USI_Hash 0
SkipLoadingEval true
makebook peta_shock_next user_book1_20240108165930.db sfens.txt 1000
⇨ 変更前とぴったり一致した。これで完璧。



# 2024/1/8


- peta_shockコマンド、合流数の表示が0になっていたの修正。
- 一つ前のcommitでコンパイルエラーになっていたの修正。


// 対応するhash keyがbook_nodesに存在するかを高速に判定するために // std::vector のようなbitによる存在チェックを行うためのclass。 class ExistHash { public: ~ExistHash() { release(); }

void reserve(size_t size_mb)
{
	release();

	// hash_size[MB]だけ割り当てる。
	size_t hash_size = 1024 * 1024 * size_mb;
	hash = new int8_t[hash_size];
	Tools::memclear("exist hash", hash, hash_size);
	exist_hash_num = hash_size * 8;
}

void release()
{
	if (hash)
		delete[] hash;
}

void set(size_t n)
{
	size_t index = n % exist_hash_num;
	hash[index/8] |= 1 << (index & 7);
}

bool get(size_t n)
{
	size_t index = n % exist_hash_num;
	return bool(hash[index/8] & (1 << (index & 7)));
}

// 実際に確保したメモリ
int8_t* hash = nullptr;

// 確保されている個数
size_t exist_hash_num = 0;

};

⇨ これよくないアイデアであったか…。


・peta_shockコマンド、合流処理の高速化。

USI_Hash 0
SkipLoadingEval true
makebook peta_shock user_book1_20240108165930.db user_book1.db hash 1024
  修正前 time 1:35
  修正後 time 2:08
⇨ 遅なった🥲 std::vector<bool>がいかんかったっぽい。int8_tの配列にした。
 修正後 time 1:30
 8GB割当 time 2:15
⇨ さらに遅なった。メモリのfetch間に合ってなさそう。
⇨ しかも合流チェックのコード、間違っていた。
⇨ これ直したら、合流チェック、めっちゃ時間かかるようになった。flipするからなー。やっぱりflipするの筋が悪いのでは…。
⇨ てかhash_key_to_indexにwhite_hash登録してなかったのか?⇨してた
⇨ 読み込み部、高速化しそう。⇨ した
⇨ 単に合流カウンターのインクリメントがおかしかっただけか。

USI_Hash 0
SkipLoadingEval true
makebook peta_shock user_book1_20240105221623.db user_book1.db

USI_Hash 0
SkipLoadingEval true
makebook peta_shock user_book1_20240108165930.db user_book1.db

⇓
write book/user_book1.db
[ PetaShock Result ]
converged_moves  : 9103450
retro_counter1   : 7288654
retro_counter2   : 1002151
write book nodes : 8290805

8294952局面で変換元DBファイル1909MB、変換時の使用メモリ3193MB。hash_key_to_index解放後、2759MB。変換時間、15分30秒。

packed sfen = 32*8.3MB = 265.6MB
hash_key_to_index 434MB/8.3M = 52.28 byte / entry
⇨ これ、hash key(8byte) + index(4byte)でいいなら12byteしか食わないはずなのになー。8+8だとしても、3.25倍食ってるな…。
52-12 = 40byte節約になると…。40 * 8.3M = 332MB節約になるか。わりと大きいか…。

packed sfenを節約するのが先かも知れん。sort前提なら、これ要らんもんなー。
両方削れたとして使用メモリ2596MB。1億局面で31.31GBかー。まあ、そこまではいけるか。
いまのままだとしても、38.5GBいるだけだしなー。そんな変わらんのちゃう?
どうせ1億局面ほどしか掘れんしなー。

1億局面、変換だけで150分かかるから、3時間だなー。
並列化できれば別だけど…。合流チェックは並列化できるか…。
局面の登録も、並列化できるかも知れんが、読み込んだ順は維持しないといけないから、自明ではないな…。


- peta_shockコマンド、sort済みBook DBに対する高速化、省メモリ化。
  - memory_saving要らないので削除。
    // # NOE:258,SORTED
    のように定跡DBの2行目に書いてあれば、sort済みなので出力の時のsortを端折れる。
    またNOEはNum Of Entriesの意味で、DB上のSfen局面の数。
    これが事前に指定されていれば、固定サイズのメモリ確保で済むので処理が端折れる。


peta shock化、sort済みの定跡に対してはpacked sfenをファイルに書き出して、かつ、hash key to indexを事前に配列に確保して2分探索すれば、後者はN*(8+4) bytesで済むし、局面のpacked sfenなしで良ければ1局面120byteぐらいで済むから、つまりは、N * 132bytesでいいのかな。N=100Mで13.2GB..。そこまで減ってないか?

packed_sfen要らんようなるだけだと、N=100Mで3.2GBの節約にしかならんからか。
これファイルに書き出すだけ野暮かも知れない。
hash key to indexだけどうにかした方がいいか。


# 2024/1/7

V7.78e
- V7.78bにrollbackして、かつ、コメント掃除した。

  engine1 = YaneuraOuNNUE_V778a.exe , eval = hao
  engine2 = YaneuraOuNNUE_V778e.exe , eval = hao
  T2,b1000,296 - 22 - 302(49.5% R-3.49[-26.84,19.87]) winrate black , white = 51.0% , 49.0%
  T2,b2000,133 - 15 - 142(48.36% R-11.37[-45.8,23.05]) winrate black , white = 49.45% , 50.55%
  ⇨ 問題なさげ。

V7.78d
- if (!evaluated)
  自体削除してみる。

  engine1 = YaneuraOuNNUE_V778a.exe , eval = hao
  engine2 = YaneuraOuNNUE_V778d.exe , eval = hao
  T2,b1000,647 - 62 - 571(53.12% R21.71[5.31,38.11]) winrate black , white = 52.79% , 47.21%
  T2,b2000,314 - 26 - 280(52.86% R19.91[-3.56,43.38]) winrate black , white = 48.82% , 51.18%
  ⇨ やっぱ、よくないなー。

V7.78c
- Eval::evaluate_with_no_return(pos)が何らかバグってるんじゃ…。
  - 遅延evaluate()に変更してみるか。

V7.78b
- Eval::evaluate_with_no_return(pos);
    コメントアウトし忘れていた。

    engine1 = YaneuraOuNNUE_V778a.exe , eval = hao
    engine2 = YaneuraOuNNUE_V778b.exe , eval = hao
    T2,b1000,3313 - 293 - 3304(50.07% R0.47[-6.55,7.5]) winrate black , white = 52.73% , 47.27%
    T2,b2000,1555 - 191 - 1544(50.18% R1.23[-9.03,11.5]) winrate black , white = 52.92% , 47.08%

V7.78a
- 1手詰めを特殊な条件で見逃すことがあるのを修正。(圧倒的勝勢で次善手を指すので勝敗には影響なさそうなのだが)

    engine1 = YaneuraOuNNUE_V778.exe , eval = hao
    engine2 = YaneuraOuNNUE_V778a.exe , eval = hao
    T2,b1000,2714 - 249 - 2617(50.91% R6.32[-1.51,14.15]) winrate black , white = 52.13% , 47.87%
    T2,b2000,1248 - 152 - 1230(50.36% R2.52[-8.95,14.0]) winrate black , white = 50.85% , 49.15%
    ⇨ 勝敗に影響なさそうなので良しとする。

現象が再現できるUSI入力

isready position startpos moves 7g7f 8b5b 2g2f 5a6b 3i3h 6b7b 5i6h 7b8b 2f2e 3c3d 9g9f 5c5d 6h7h 5d5e 2e2d 2c2d 2h2d 4a3b 2d2h P2c 9f9e 7a7b 6g6f 5e5f 5g5f 5b5f 7h6g 5f5a 7i6h 6c6d 6g7h 3a4b 4g4f 4b5c 3h4g 2b4d 4i5h 5c5d 5h6g 7c7d 2h5h 7b6c 4f4e 4d3c 8h7g 6a7b 4g4f P5e 7h8h 8a7c 6i7h 4c4d 4e4d 3c4d P4e 4d3c 3g3f 6d6e 6f6e 7c8e 7g8f 5d6e P6f 6e5f P5b 5a5b 6g5f 5e5f P5e G4g 5h5f 4g4f 5f4f 5b5e P5f 5e5a 4e4d S6i 7h7i 6i5h+ 4f4e 8c8d 4d4c+ 3c6f S7g 8e7g+ 6h7g 6f5g+ 4c3b S6g G7h P6f N6d 5a6a 6d7b+ 6c7b 4e4c+ 6g7h+ 7i7h N7c 8f5c+ G6b G5b 6b5c 5b6a 7b6a 4c5c G7b G6c 7b6c 5c6c G6b R4b P5b 4b5b+ B7a 6c6b 6a6b G7b 8b8c 5b6b 5g7i 7h7i R*9h go

position startpos moves 7g7f 8b5b 2g2f 5a6b 3i3h 6b7b 5i6h 7b8b 2f2e 3c3d 9g9f 5c5d 6h7h 5d5e 2e2d 2c2d 2h2d 4a3b 2d2h P2c 9f9e 7a7b 6g6f 5e5f 5g5f 5b5f 7h6g 5f5a 7i6h 6c6d 6g7h 3a4b 4g4f 4b5c 3h4g 2b4d 4i5h 5c5d 5h6g 7c7d 2h5h 7b6c 4f4e 4d3c 8h7g 6a7b 4g4f P5e 7h8h 8a7c 6i7h 4c4d 4e4d 3c4d P4e 4d3c 3g3f 6d6e 6f6e 7c8e 7g8f 5d6e P6f 6e5f P5b 5a5b 6g5f 5e5f P5e G4g 5h5f 4g4f 5f4f 5b5e P5f 5e5a 4e4d S6i 7h7i 6i5h+ 4f4e 8c8d 4d4c+ 3c6f S7g 8e7g+ 6h7g 6f5g+ 4c3b S6g G7h P6f N6d 5a6a 6d7b+ 6c7b 4e4c+ 6g7h+ 7i7h N7c 8f5c+ G6b G5b 6b5c 5b6a 7b6a 4c5c G7b G6c 7b6c 5c6c G6b R4b P5b 4b5b+ B7a 6c6b 6a6b G7b 8b8c 5b6b 5g7i 7h7i R*9h 9i9h go

position startpos moves 7g7f 8b5b 2g2f 5a6b 3i3h 6b7b 5i6h 7b8b 2f2e 3c3d 9g9f 5c5d 6h7h 5d5e 2e2d 2c2d 2h2d 4a3b 2d2h P2c 9f9e 7a7b 6g6f 5e5f 5g5f 5b5f 7h6g 5f5a 7i6h 6c6d 6g7h 3a4b 4g4f 4b5c 3h4g 2b4d 4i5h 5c5d 5h6g 7c7d 2h5h 7b6c 4f4e 4d3c 8h7g 6a7b 4g4f P5e 7h8h 8a7c 6i7h 4c4d 4e4d 3c4d P4e 4d3c 3g3f 6d6e 6f6e 7c8e 7g8f 5d6e P6f 6e5f P5b 5a5b 6g5f 5e5f P5e G4g 5h5f 4g4f 5f4f 5b5e P5f 5e5a 4e4d S6i 7h7i 6i5h+ 4f4e 8c8d 4d4c+ 3c6f S7g 8e7g+ 6h7g 6f5g+ 4c3b S6g G7h P6f N6d 5a6a 6d7b+ 6c7b 4e4c+ 6g7h+ 7i7h N7c 8f5c+ G6b G5b 6b5c 5b6a 7b6a 4c5c G7b G6c 7b6c 5c6c G6b R4b P5b 4b5b+ B7a 6c6b 6a6b G7b 8b8c 5b6b 5g7i 7h7i R*9h 9i9h 8d8e go

position startpos moves 7g7f 8b5b 2g2f 5a6b 3i3h 6b7b 5i6h 7b8b 2f2e 3c3d 9g9f 5c5d 6h7h 5d5e 2e2d 2c2d 2h2d 4a3b 2d2h P2c 9f9e 7a7b 6g6f 5e5f 5g5f 5b5f 7h6g 5f5a 7i6h 6c6d 6g7h 3a4b 4g4f 4b5c 3h4g 2b4d 4i5h 5c5d 5h6g 7c7d 2h5h 7b6c 4f4e 4d3c 8h7g 6a7b 4g4f P5e 7h8h 8a7c 6i7h 4c4d 4e4d 3c4d P4e 4d3c 3g3f 6d6e 6f6e 7c8e 7g8f 5d6e P6f 6e5f P5b 5a5b 6g5f 5e5f P5e G4g 5h5f 4g4f 5f4f 5b5e P5f 5e5a 4e4d S6i 7h7i 6i5h+ 4f4e 8c8d 4d4c+ 3c6f S7g 8e7g+ 6h7g 6f5g+ 4c3b S6g G7h P6f N6d 5a6a 6d7b+ 6c7b 4e4c+ 6g7h+ 7i7h N7c 8f5c+ G6b G5b 6b5c 5b6a 7b6a 4c5c G7b G6c 7b6c 5c6c G6b R4b P5b 4b5b+ B7a 6c6b 6a6b G7b 8b8c 5b6b 5g7i 7h7i R*9h 9i9h 8d8e 7b7c go

position startpos moves 7g7f 8b5b 2g2f 5a6b 3i3h 6b7b 5i6h 7b8b 2f2e 3c3d 9g9f 5c5d 6h7h 5d5e 2e2d 2c2d 2h2d 4a3b 2d2h P2c 9f9e 7a7b 6g6f 5e5f 5g5f 5b5f 7h6g 5f5a 7i6h 6c6d 6g7h 3a4b 4g4f 4b5c 3h4g 2b4d 4i5h 5c5d 5h6g 7c7d 2h5h 7b6c 4f4e 4d3c 8h7g 6a7b 4g4f P5e 7h8h 8a7c 6i7h 4c4d 4e4d 3c4d P4e 4d3c 3g3f 6d6e 6f6e 7c8e 7g8f 5d6e P6f 6e5f P5b 5a5b 6g5f 5e5f P5e G4g 5h5f 4g4f 5f4f 5b5e P5f 5e5a 4e4d S6i 7h7i 6i5h+ 4f4e 8c8d 4d4c+ 3c6f S7g 8e7g+ 6h7g 6f5g+ 4c3b S6g G7h P6f N6d 5a6a 6d7b+ 6c7b 4e4c+ 6g7h+ 7i7h N7c 8f5c+ G6b G5b 6b5c 5b6a 7b6a 4c5c G7b G6c 7b6c 5c6c G6b R4b P5b 4b5b+ B7a 6c6b 6a6b G7b 8b8c 5b6b 5g7i 7h7i R*9h 9i9h 8d8e 7b7c 8c8d go btime 555000 wtime 30000 binc 10000 winc 10000


# 2024/1/6

engine1 = YaneuraOuNNUE_V777j4_1024.exe , eval = Suisho_DR4_kishin
engine2 = YaneuraOuNNUE_V777j4_1024.exe , eval = Li
T2,b1000,331 - 24 - 305(52.04% R14.21[-8.45,36.87]) winrate black , white = 52.04% , 47.96%
T2,b1000,323 - 23 - 314(50.71% R4.91[-17.72,27.54]) winrate black , white = 49.61% , 50.39%
T2,b2000,134 - 17 - 159(45.73% R-29.72[-63.17,3.74]) winrate black , white = 53.58% , 46.42%
T2,b2000,144 - 18 - 148(49.32% R-4.76[-38.15,28.64]) winrate black , white = 51.03% , 48.97%


# 2024/1/2

- makebook peta_shock_nextコマンドにfrom_startposと指定して、"startpos moves ..."の形式で次に掘るべき局面を出力する機能を追加。


Clone this wiki locally