Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

定跡管理

本章は圧縮配置の Native Book v4 と Python API を説明する。

1.7.3 でも Python の公開 API は維持する。 BookDatabasePatch、DB2016 / SBK / YBB の差分保存、YBB の全件診断と同一キー全行の取得、NativeBook::validate_full_and_visit() は Rust 向け API である。 既存の SbkBook.open() および Native Book の検証・構築・検索には Rust core の内部改善が適用される。 変更範囲は 1.7.3 の変更内容、1.7.2 の変更内容 と 1.7.1 の変更内容 を参照。

Native Book v4 と外部定跡の reader を利用できる。形式の詳細は バイナリ仕様 と 定跡アーキテクチャ を参照。

Native Book v4

NativeBook はファイルの必要な部分を読む reader、NativeBookEditor は排他ロックを持つ編集用 writer、NativeBookBuilder は新規ファイルを構築する builder である。検証と保存は Rust core が行う。

from rsshogi.core import Board, Move
from rsshogi.book import (
    NativeBook, NativeBookEditor, NativeBookMetadata,
    NativeBound, NativeCandidate, NativeScore,
)

board = Board()
editor = NativeBookEditor.create("example.book", NativeBookMetadata("定跡集", depth_kind=1))
editor.upsert_candidate(
    board,
    NativeCandidate(Move.from_usi("7g7f"), NativeScore.cp(25, NativeBound.Exact),
                    depth=24),
    comment="本線",
)
editor.set_position_comment(board, "初期局面")
editor.save()
editor.close()

book = NativeBook.open("example.book")
book.validate_full()
entry = book.get(board)
record = book.record(board)

新規構築では NativeBookBuilder(path, metadata=None, block_positions=64) の block_positions をキーワードで指定できる。 範囲は 1..=16384、既定と推奨値は 64 である。 保存値は book.block_positions で取得し、editor.compact() と salvage_prefix() でも元の値を維持する。

保存先が既に存在する場合は、上書きせず FileExistsError(OSError のサブクラス)を送出する。 構築開始後に保存先が作られた場合も、finish() は FileExistsError を送出する。 不正なブロック局面数や重複局面は ValueError となる。

from rsshogi.book import NativeBookBuilder, NativeBookEntry, NativeBookPosition

builder = NativeBookBuilder("custom-blocks.book", block_positions=256)
builder.push(NativeBookPosition(NativeBookEntry(board, [NativeCandidate(Move.from_usi("7g7f"))])))
builder.finish()
assert NativeBook.open("custom-blocks.book").block_positions == 256

評価は NativeScore.unknown()、cp(value, bound)、mate(plies, bound)、mate_unknown(winning) で区別する。視点は指す前の手番側で、詰み距離は正なら勝ち、負なら負け。kind、value、bound、winning で値を取得できる。

NativeCandidate(mv, score=None, depth=None, display_order=0) の depth は任意の u32 値であり、None は未設定、0 は設定済みの値を表す。 NativeBookMetadata(title=None, description=None, depth_kind=0) は文書全体の深さの意味を指定し、0 は未指定、1 は探索深さ、2 は定跡内の後退解析距離である。 その他の u32 値も保存、再 open と compaction で保持する。 メタデータは book.metadata() または editor.metadata() で取得し、editor.set_metadata(...) で置換する。

NativeBookEntry(board, candidates) は候補手を検証し、raw 指し手順に整列する。表示順は display_order が別に保持する。NativeBookPosition(entry, comment, move_comments) は所有 snapshot で、move_comments は raw 指し手整数 (int(move)) から本文への辞書である。候補手ゼロの局面にもコメントを残せる。

upsert_candidate(..., comment=None) は既存の候補手コメントを保持し、空文字は削除する。reorder_candidate は表示順位を変更する。save() は変更がなければ False を返し、追記しない。close() はロックを解放し、未保存の変更を破棄する。reader は open 時点の snapshot を保持するため、保存後の変更を読むには開き直す。

open は完全検証を意味しない。由来不明のファイルは validate_full() で検証する。get() は検索データ、record() はコメント込みの局面を返す。position_comment(board) と move_comment(board, mv) は指定した本文だけを取得し、同じ局面のほかのコメント本文を読まない。entries() と records() は所有リストを返すため、大規模ファイルの全件展開では必要な RAM に注意する。Rust 側には streaming iterator がある。tail() は未完了 tail の (offset, end) または None、io_stats() は累積の要求 (read_calls, read_bytes) を返す。物理ディスクの I/O 量ではない。

I/O 失敗は OSError、writer 競合は BlockingIOError、不正データは ValueError。NativeBookLogError は offset、valid_end、sequence、reason を持つ ValueError である。NativeBookSaveUncertainError は保存結果不明を表す RuntimeError で、editor の再 open が必要になる。

recover_tail(path) は正常な開始ヘッダーを持つ未完了 tail を別ファイルへ保全してから復旧し、保全先を返す。未検証 tail や破損には使わない。salvage_prefix(path, output) は原本を変更せず、完全検証した本体とログの prefix を新規ファイルへ救出する。報告には原本長、検証済み末尾、sequence、異常 offset、出力先が含まれる。救出は本体全件の検証と再構築を伴い、時間と別ファイル分の空き容量が必要である。

旧 MemoryBook、StaticBook、BookBuilder とハッシュキー API は 1.5.0 の開発版で削除した。旧 v2 単独から局面を復元できないため、元の棋譜や外部形式から再生成する。

やねうら王形式定跡(YaneuraOuBook)

#YANEURAOU-DB2016 1.00 ヘッダーを持つテキスト定跡を読み取り専用で検索する。 既定の open(path) は先頭 10,000 局面行までの整列を検査する。 lookup_sfen() と lookup_position() は全件の整列検査完了を必要とする。 診断が sorted でも complete が False なら validate_full() を実行し、未整列なら lookup_sfen_by_scan() を使う。 局面索引やファイル全体の候補手をメモリへ展開しない。

メソッド戻り値説明
YaneuraOuBook.open(path)YaneuraOuBook文字列のファイルパスから開く。
diagnostics()YaneuraOuBookDiagnostics整列状態と検査範囲を返す。
validate_full()YaneuraOuBookDiagnostics全件を検査し、診断情報を返す。
lookup_sfen(sfen)YaneuraOuBookEntry | None正規化 SFEN で検索する。手数は検索キーに含めない。
lookup_position(board)YaneuraOuBookEntry | NoneBoard の局面で検索する。
lookup_sfen_by_scan(sfen)YaneuraOuBookEntry | Noneファイルを走査して検索する。
クラスプロパティ型と意味
YaneuraOuBookEntrysfenstr。手数を 1 に正規化した SFEN。
YaneuraOuBookEntrymin_plyint。元の手数。省略時は 0。
YaneuraOuBookEntrycommentstr。局面コメント。
YaneuraOuBookEntrymoveslist[YaneuraOuBookMove]。len(entry) で候補手数を取得する。
YaneuraOuBookMovemv / ponderMove。to_usi() で文字列に変換する。応手なしは Move.MOVE_NONE。
YaneuraOuBookMovescore / depth / countint | None。評価値、探索深さ、採択回数。
YaneuraOuBookMovecommentstr。候補手コメント。
YaneuraOuBookDiagnosticskindstr。sorted、unsorted、invalid_header、unvalidated。
YaneuraOuBookDiagnosticschecked_rowsint | None。検査済みの局面行数。
YaneuraOuBookDiagnosticscompletebool | None。整列済みの場合に全件検査の完了状態を返す。
YaneuraOuBookDiagnosticsline_numberint | None。未整列を検出した行番号。
YaneuraOuBookDiagnosticsprevious_sfen / current_sfenstr | None。未整列を検出した前後の SFEN。
from rsshogi.book import YaneuraOuBook
from rsshogi.core import Board

book = YaneuraOuBook.open("user_book1.db")
book.validate_full()
print(book.diagnostics().kind)

board = Board()
entry = book.lookup_position(board)
if entry is not None:
    print(entry.min_ply, len(entry))
    for move in entry.moves:
        print(move.mv.to_usi(), move.ponder.to_usi(), move.score, move.depth, move.count)

SBK 形式定跡(SbkBook)

SBK の局面状態と候補手を読み取り専用で扱う。 state_id はファイル内の識別子、state_index は物理的な格納順を表す。 next_state_id を物理インデックスとして扱わず、遷移先の取得には child_entry() を使う。

メソッドまたはプロパティ戻り値説明
SbkBook.open(path)SbkBook文字列のファイルパスから開く。
SbkBook.open_with_control(path, callback)SbkBook進捗付きで開く。callback は必須。
author / descriptionstr | None作成者と説明。
diagnostics()SbkDiagnostics重複局面と未解決遷移先の件数を返す。
lookup_sfen(sfen) / lookup_position(board)SbkEntry | None局面で検索する。
lookup_state_id(state_id)SbkEntry | Noneファイル内の識別子で検索する。
lookup_state_index(state_index)SbkEntry | None0 始まりの物理インデックスで検索する。
child_entry(parent, move_index)SbkEntry | None候補手の遷移先を取得する。範囲外の候補手番号や未解決の遷移先は None。
len(book)int局面状態数。
クラスプロパティ型と意味
SbkEntrystate_index / state_idint / int | None。物理インデックスと識別子。
SbkEntrysfen / commentstr / str | None。局面とコメント。
SbkEntrygames / won_black / won_whiteint | None。対局数、先手勝数、後手勝数。
SbkEntrymoves / evalslist[SbkMove] / list[SbkEval]。候補手と局面評価の履歴。
SbkMovemv / raw_moveMove / int。変換済みの指し手とファイル内の生値。
SbkMoveevaluation / weight / next_state_idint | None。評価値、重み、遷移先識別子。
SbkEvalevaluation_value / depth / sel_depth / nodesint | None。評価値、深さ、選択深さ、ノード数。
SbkEvalvariation / engine_namestr | None。読み筋とエンジン名。
SbkDiagnosticsduplicate_positions / unresolved_next_statesint。重複局面数と未解決遷移先数。
SbkOpenProgressindexed_states / total_statesint。索引作成済み状態数と全状態数。

callback は SbkOpenProgress を受け取る。 None または真値を返すと継続し、False を返すとキャンセルする。 キャンセルと読み込みエラーは ValueError、callback 内の例外は元の例外として送出される。

from rsshogi.book import SbkBook, SbkOpenProgress
from rsshogi.core import Board


def on_progress(progress: SbkOpenProgress) -> None:
    print(f"{progress.indexed_states}/{progress.total_states}")


book = SbkBook.open_with_control("opening.sbk", on_progress)
board = Board()
entry = book.lookup_position(board)
if entry is not None and len(entry) > 0:
    first_move = entry.moves[0]
    print(first_move.mv.to_usi(), first_move.evaluation, first_move.next_state_id)
    next_entry = book.child_entry(entry, 0)

関連項目