Hayanagi は、C++17 で実装した USI プロトコル対応の最小構成将棋エンジンです。
通常対局用の探索に加えて、詰将棋用の詰み探索(TsumeSearch)を備え、
将棋 GUI ShogiBoardQ に静的ライブラリとして組み込まれています。
- 現在のバージョン: 1.0.1(変更履歴)
- USI の
id nameはHayanagi 1.0.1。./build/hayanagi --versionでも表示できます - CMake の生成実行ファイル名は
hayanagi、組み込み用の静的ライブラリはhayanagi_tsume - 合法手生成、終局判定、通常探索、詰み探索、
bench/perftをひととおり実装
- CMake 3.16 以上
- C++17 対応コンパイラ
- GCC / Clang を想定(
__builtin_ctzllなどの組み込み関数を使います)
- GCC / Clang を想定(
- Python 3(回帰テストとベンチマークスクリプトのみ。標準ライブラリだけで動きます)
cmake -S . -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build生成物は次のとおりです。
| 生成物 | 内容 |
|---|---|
build/hayanagi |
USI エンジン実行ファイル |
build/libhayanagi_tsume.a |
Position と TsumeSearch の静的ライブラリ(GUI 組み込み用) |
build/hayanagi_tests |
C++ 単体テスト(Hayanagi を最上位でビルドしたときだけ生成) |
コンパイル警告は -Wall -Wextra -Wpedantic -Wshadow -Wconversion -Wno-sign-conversion を有効にしており、GCC と Clang のどちらでも警告なしでビルドできます(Clang の -Wconversion は -Wsign-conversion を含むため、GCC と同じ意味になるよう明示的に無効にしています)。
./build/hayanagi --version # Hayanagi 1.0.1
./build/hayanagi # USI エンジンとして起動usi
isready
usinewgame
position startpos
perft depth 2 divide
bench nodes 10000
go nodes 5000
quit
usi に対しては次のように応答します(Hayanagi 1.0.1 の出力)。
id name Hayanagi 1.0.1
id author OpenAI
option name USI_Ponder type check default false
option name MultiPV type spin default 1 min 1 max 32
option name Threads type spin default 1 min 1 max 128
option name Hash type spin default 16 min 1 max 65536
option name MinimumThinkingTime type spin default 0 min 0 max 600000
option name NetworkDelay type spin default 0 min 0 max 600000
option name NetworkDelay2 type spin default 0 min 0 max 600000
option name SlowMover type spin default 100 min 1 max 1000
option name ResignValue type spin default 99999 min 0 max 99999
option name MaxMovesToDraw type spin default 500 min 0 max 600000
option name EnteringKingRule type combo default CSARule24 var NoEnteringKing var CSARule24 var CSARule24H var CSARule27 var CSARule27H var TryRule
option name GenerateAllLegalMoves type check default true
option name USI_OwnBook type check default true
option name BookDir type string default book
option name BookFile type combo default standard_book.db var no_book var standard_book.db var yaneura_book1.db var yaneura_book2.db var yaneura_book3.db var yaneura_book4.db var user_book1.db var user_book2.db var user_book3.db
option name TsumeMode type check default false
usiok
- 成り
- 打ち駒
- 二歩
- 行き所のない駒の打ち / 移動の禁止
- 打ち歩詰めの禁止
- 王手回避、両王手、ピンされた駒の移動制限を直接生成
- 王手になる手だけの直接生成(
generate_checking_moves)
- 千日手
- 連続王手の千日手
- 持将棋の点数判定
- 手数上限による引き分け判定(
MaxMovesToDraw) - 入玉ルール切り替え(
EnteringKingRule)
usiisreadyusinewgameposition startpos moves ...position sfen ... moves ...go depth Ngo movetime Ngo nodes Ngo ponder ...go wtime ... btime ... byoyomi ...go tsume <attack|defense> depth N movetime M(詰み探索。詳細)setoption name USI_OwnBook value <bool>setoption name BookDir value <path>setoption name BookFile value <file>(no_book/standard_book.db/yaneura_book1.db〜yaneura_book4.db/user_book1.db〜user_book3.db)setoption name USI_Ponder value <bool>setoption name MultiPV value Nsetoption name Threads value Nsetoption name Hash value N(MB 単位)setoption name MinimumThinkingTime value Nsetoption name NetworkDelay value Nsetoption name NetworkDelay2 value Nsetoption name SlowMover value Nsetoption name ResignValue value Nsetoption name MaxMovesToDraw value Nsetoption name EnteringKingRule value <rule>(NoEnteringKing/CSARule24/CSARule24H/CSARule27/CSARule27H/TryRule)setoption name GenerateAllLegalMoves value <bool>setoption name TsumeMode value <bool>(片玉の局面を受け付ける)stopponderhitquitbench depth Nbench nodes Nbench current depth Nbench tsume [movetime M](詰み探索の固定局面ベンチマーク)perft Nperft depth N divide
実装済みの主要オプションは次のとおりです。
USI_OwnBookBookDirBookFileMultiPVThreadsHashUSI_PonderMinimumThinkingTimeNetworkDelayNetworkDelay2SlowMoverResignValueMaxMovesToDrawEnteringKingRuleGenerateAllLegalMovesTsumeMode
USI_OwnBook を有効にすると(デフォルトで有効)、go 時に定跡ファイルを参照し、現局面にヒットすれば探索せずに定跡手を返します。定跡ファイルはやねうら王の DB2016 フォーマット(#YANEURAOU-DB2016 1.00)に対応しています。BookDir で定跡フォルダ(デフォルトは実行ファイルからの相対パス book)、BookFile で定跡ファイル名(デフォルトは standard_book.db)を指定します。BookFile に no_book を指定すると定跡を無効にできます。
MultiPV を有効にすると、info ... multipv N pv ... を順位ごとに出力します。GenerateAllLegalMoves は互換性維持のため残していますが、現在は探索強度を落とさないよう no-op です。
通常探索の info には depth / score cp / nodes / time / nps / pv を出力します。
USI_Ponder を有効にすると、通常探索の bestmove は bestmove <move> ponder <move> を返します。go ponder で始めた探索は ponderhit または stop を受けるまで bestmove を返しません。
- 9x9 盤面、持ち駒、手番を保持します。
startpos/sfenの読み込み、USI 指し手の適用、合法手生成を担当します。- 盤面配列に加えて色別・駒種別ビットボードを持ち、王手検出、ピン判定、合法手生成をビットボード主導で行います。
- 玉の回避手、単王手時の合駒・駒取り制限、ピンされた駒の移動制限を、局面全体の後段フィルタではなく直接生成で処理します。
- 飛び駒の利きはレイのビットボードと最初の遮蔽駒から求め、王手判定は玉と同一線上にある飛び駒だけを調べます。
- 王手生成(
generate_checking_moves)は、移動先に置いた駒が相手玉に利く升と開き王手の候補を先に求め、該当する手だけを列挙します。並び順は全合法手の生成順と同じです。 make_move/unmake_moveは履歴を更新しない軽量な着手・待ったで、詰み探索のように千日手判定が不要な探索で局面のコピーを省きます。has_legal_moveは手を列挙せずに合法手の有無を判定します(王手回避では玉の移動・王手駒の捕獲・合駒の順に調べます)。
- 王手の連続による強制詰みを、深さ優先の反復深化で探索します。詳細は詰み探索を参照してください。
- 反復深化付きの
negamax + alpha-beta探索を行います。 Threads > 1のときは root move 単位で並列探索します。Hashオプションでサイズ変更できる lockless 共有置換表、PVS、quiescence search、SEE ベースの着手順序、killer/history heuristic、LMR、null-move pruning を使って探索効率を上げています。- 短手数の詰みは専用の王手限定探索で先に検出します。
- 評価関数は駒得に加え、駒の前進度、利きの広さ、敵陣進出、手駒価値、玉の安全度を見ます。
- やねうら王の定跡フォーマット(
#YANEURAOU-DB2016 1.00)を読み込みます。 - SFEN 文字列をキーとして定跡手を検索します。
usi/isready/position/go/stop/quitを処理します。- 探索はワーカースレッドで実行し、
stopに反応できる構成にしています。 isready時に定跡ファイルを読み込み、go時に定跡を参照します。benchとperftを内蔵しており、GUI 接続前の自己確認にも使えます。bench tsumeは詰み探索の固定局面を順に解き、結果とノード数・時間を出力します(出力例)。
- 探索は最小構成で、評価関数も簡易です。
- USI には引き分けを返す専用の
bestmoveがないため、千日手・持将棋など現在局面でゲーム終了と判定した場合はinfo string terminal ...を出した上でbestmove resignを返します。 perftはnodesに加えてcaptures/promotions/checks/matesを出力します。benchは各局面と合計についてnodes/time/nps/hashfullを出力し、nodes N指定で固定ノード数ベンチとして使えます。- 詰み探索は深さ優先なので、長手数の詰将棋(おおむね 15 手以上)の解図には向きません。GUI 側では短手数の判定と選別に使い、長手数は外部の詰将棋エンジンに任せる想定です。
src/tsume.h の TsumeSearch は、通常の評価値探索と分けて、王手の連続による強制詰みを探索します。
玉方ではすべての合法応手を調べ、詰む場合は最長抵抗、不詰の場合は逃れる手を返します。
攻方の玉を省略した SFEN も扱えます。
- 深さ優先の反復深化(攻方手番なら深さ 1, 3, 5, …、玉方手番なら 0, 2, 4, …)で、最初に確定した結果を返します。そのため
mateのpliesは最短の詰み手数です。 - 局面は
make_move/unmake_moveで進めて戻し、局面のコピーと履歴の確保をしません。 - 置換表(局面キーは盤面・持駒・手番を含む)には詰みと不詰の証明を深さをまたいで保持します。深さ打ち切りは「その深さまで詰みなし」としてだけ再利用し、不詰の証明とは区別します。
- 置換表は 4 エントリのバケットを持つ固定サイズの表で、64 KB から始めて必要に応じて 2 倍に拡張し、最大 32 MB に収まります。
- 同じ
TsumeSearchを続けて使うと、攻方が同じ間は前回の証明を再利用します(PV の再構成や別詰の数え上げが速くなります)。 TsumeSearchとPositionはグローバルな可変状態を持たないので、スレッドごとに別インスタンスを持てば並行して使えます。- 計測結果は BENCHMARK.md を参照してください。
単独実行時は次の独自 USI 拡張を利用できます(通常 USI の go mate とは別のコマンドです)。
usi
setoption name TsumeMode value true
setoption name USI_OwnBook value false
isready
position sfen 9/9/6R1+R/5k3/9/7+S1/9/9/9 b 2b4g3s4n4l18p 1 moves 3c5c+
go tsume defense depth 4 movetime 5000
応答例:
tsume mate move 4d4e plies 4 nodes ...
attackは現在手番を攻方、defenseは現在手番を玉方として探索します。depthは現在局面からの最大手数(既定 31、上限 63)、movetimeはミリ秒(既定 5000)です。- 応答の状態は
mate/nomate/depthlimit/timeout/cancelledです。不正な局面・コマンドではtsume invalidを返します。 nomateは不詰の証明です。depthlimitは指定手数以内に詰みがないという意味で、それより長い詰みの否定ではありません。timeout/cancelledは未判定です。moveは攻方の詰め手、または玉方の抵抗・逃れの手です。着手がない場合はnoneです。pliesは証明された詰みまでの手数で、mateの場合に有効です。stopで中断結果を返し、position/quitは進行中の探索を破棄します。TsumeModeは既定 false です。通常のpositionでは両玉が必要で、片玉局面での通常のgoは拒否します。
bench tsume [movetime M] は BENCHMARK.md と同じ 8 局面を順に解き、局面ごとの結果と合計を info string で出力します(TsumeMode の設定は不要です)。
info string bench tsume 1/8 mate5-1 depth 5 status mate move 3c5c+ plies 5 nodes 646 time 0 nps 646000
...
info string bench tsume 8/8 nomate5 depth 5 status depthlimit move R*7i plies 0 nodes 1870972 time 207 nps 9038512
info string bench tsume total positions 8 nodes 2019874 time 225 nps 8977217
ShogiBoardQ は Hayanagi をサブモジュールとして取り込み、add_subdirectory で静的ライブラリ hayanagi_tsume をリンクしています。
add_subdirectory(Hayanagi)
target_link_libraries(my_app PRIVATE hayanagi_tsume) # include パス(src/)も伝播する
message(STATUS "Hayanagi ${HAYANAGI_VERSION}") # 組み込み側からバージョンを参照できるhayanagi_tsumeはsrc/position.cppとsrc/tsume.cppだけからなり、Qt などへの依存はありません。- 組み込み時は単体テスト
hayanagi_testsを既定でビルドしません。必要なら-DHAYANAGI_BUILD_TESTS=ONを指定します。 - 主な API(
src/position.h、src/tsume.h):Position::set_sfen(sfen, tsume):tsume=trueで攻方の玉がない局面も受け付け、不正な SFEN ではfalseを返します。Position::generate_legal_moves(): 打ち歩詰めを除いた全合法手。generate_checking_moves(): 合法な王手だけ(打ち駒を含み、成・不成は別の手)。どちらも生成順は安定しており、組み込み側が依存できます。Position::apply_usi_move/do_move/make_move/unmake_move/has_legal_move/to_sfen/move_to_usi。TsumeSearch::solve(position, attacker, max_plies, time_limit_ms, stop):TsumeResult{status, move, plies, nodes}を返します。max_pliesは 63 で丸められ、stopは他スレッドから立てると速やかにCancelledで戻ります。
- 探索結果の意味を変える改良をしたときは、組み込み側で結果をキャッシュしているキー(ShogiBoardQ では Hayanagi の版を含む)を更新する必要があります。
ビルド後、Python 3 の標準ライブラリだけで通常将棋と詰将棋の USI 動作を検証できます。
python3 tests/test_engine.py build/hayanagi
# 変更前の実行ファイルがあれば、通常将棋 4 局面の perft 結果も比較する
python3 tests/test_engine.py build/hayanagi --baseline /path/to/previous/hayanagi平手初期局面の perft、通常探索の合法着手、5 手詰 5 問、別解、不詰、手数超過、
後手攻方、打ち歩詰め、入力不正、時間切れ、中止・局面切替、id name の形式を検証します。
C++ の単体テスト hayanagi_tests は、Hayanagi 自体をビルドしたときだけ既定で生成されます
(add_subdirectory で組み込んだ場合は -DHAYANAGI_BUILD_TESTS=ON で有効化)。
./build/hayanagi_tests
# または
ctest --test-dir buildランダム局面と平手からのランダム対局について、合法手生成・王手生成・has_legal_move・
make_move / unmake_move を単純な参照実装と突き合わせ、詰み探索については
Limit と NoMate の区別、最短手数、PV の再構成、stop と時間切れ、打ち歩詰め、
玉なし局面、持駒の多い局面、同じ探索器の再利用を検証します。
詰み探索のベンチマークは BENCHMARK.md に記録しています。再計測は次のとおりです。
python3 tests/bench_tsume.py build/hayanagi [--baseline /path/to/previous/hayanagi]
# USI から直接実行する場合
./build/hayanagi <<< $'bench tsume\nquit'| パス | 内容 |
|---|---|
src/types.h, src/bitboard.h |
基本型、81 升ビットボード |
src/position.h/.cpp |
局面、SFEN、合法手・王手生成、終局判定 |
src/tsume.h/.cpp |
詰み探索 TsumeSearch |
src/search.h/.cpp |
通常対局用の探索と評価 |
src/book.h/.cpp |
定跡の読み込み |
src/usi_engine.h/.cpp, src/main.cpp |
USI プロトコル処理、bench / perft |
src/version.h |
バージョン定義(HAYANAGI_VERSION) |
tests/test_engine.py |
USI 回帰テスト |
tests/test_*.cpp, tests/test_support.h |
C++ 単体テスト |
tests/bench_tsume.py |
詰み探索ベンチマーク |
BENCHMARK.md |
ベンチマークの計測結果 |
バージョンは src/version.h の HAYANAGI_VERSION が唯一の定義元で、CMake の project(... VERSION)、
USI の id name、--version はすべてここから読みます。リリース時はこの値と本節を更新し、
同じ番号のタグ(v1.0.1 など)を付けます。ShogiBoardQ はタグで指定した版をサブモジュールとして参照します。
| バージョン | タグ | 日付 | 概要 |
|---|---|---|---|
| 1.0.1 | v1.0.1 | 2026-09-25 | Clang での -Wsign-conversion 警告を解消(ShogiBoardQ が参照中) |
| 1.0.0 | v1.0.0 | 2026-09-25 | 詰み探索と局面処理の高速化、ベンチマーク、単体テスト、バージョン情報 |
- Clang では
-Wconversionが-Wsign-conversionを含むため、Qt Creator の Clang コードモデルが Hayanagi のソースに約 490 件の符号変換警告を報告していました(GCC でのビルドは元から警告なしで、ビルド失敗ではありません)。-Wno-sign-conversionを併記して GCC と同じ警告範囲にし、Clang 22 でも警告ゼロでビルドとテストが通ることを確認しています。探索や結果の変更はありません。
最初の明示的なバージョンです。ShogiBoardQ が参照していたコミット 30cfce5 からの主な変更は次のとおりです。
- 詰み探索を高速化しました。局面のコピーをやめて
make_move/unmake_moveで探索し、置換表を深さごとのstd::unordered_mapから、詰み・不詰の証明を深さをまたいで再利用する固定サイズの表に置き換えました。5 手詰の局面で約 15〜18 倍、攻方の持駒が多い不詰局面(深さ 5)で約 24 倍速くなっています(BENCHMARK.md)。 - 局面処理を高速化しました。飛び利きのビットボード計算、王手判定の同一線上判定、王手の直接列挙、
has_legal_moveを追加し、perftも約 2 割速くなっています。perftの結果と通常探索のノード数は変わっていません。 - 玉方の最長抵抗の選択が正確になりました(反復深化の途中結果を深さをまたいで使うため)。同じ探索器を続けて使うと、以前
depthlimitだった局面で不詰の証明nomateを返すことがあります。 - 玉同士が隣接する不正局面で相手玉を取る手を生成しなくなりました。
bench tsume、tests/bench_tsume.py、C++ 単体テストhayanagi_testsを追加しました。-Wshadow -Wconversionを有効にしました。- バージョン情報(
src/version.h、id name Hayanagi 1.0.0、--version)を追加しました。