局面の圧縮
PackedSfen と HuffmanCodedPos はともに data: [u8; 32] を持つ固定長の局面表現である。
固定長にすることで、バッファ中の局面を一定間隔で配置し、コピー、比較、レコード処理を単純にできる。
PackedSfen
PackedSfen は盤上の各マスを空または駒の可変長プレフィックスで読み書きする。
玉は手番の後に 7 ビットずつ置かれ、残る駒は盤上または持ち駒のどちらにあるかを接尾部で表す。
復元は駒種別の在庫数を数え、盤上の玉位置、接頭辞、ビット終端を検査する。
decode_into は PackedSfenSink に復元結果を流すため、Position 以外の受け手も同じデコーダを利用できる。
mirror が真なら、decode_into と set_packed_sfen は盤面マスの筋を反転して受け手へ渡す。
このオプションは手番、駒色、持ち駒の色を変更しない。
from_le_u32_words と to_le_u32_words は 32 バイトを 8 個の little-endian u32 として変換する。
この変換は SBK の語順比較を行う cmp_sbk_words にも使われる。
HuffmanCodedPos
HuffmanCodedPos も手番と両玉の位置を先に書き、その後を駒のハフマン符号で表す。
盤上の駒と持ち駒を区別する符号を読み、在庫を超える駒、無効な符号、盤外の玉位置を拒否する。
huffman_coded_pos_unpack は復元した局面を SFEN 文字列として返す補助 API である。
set_huffman_coded_pos は与えられた ply を使って Position を再構築する。
検証と利用上の注意
符号化後の 32 バイトは、対応するデコーダだけで意味を持つバイナリ値である。
外部からのデータは set_packed_sfen または set_huffman_coded_pos の Result を必ず確認する。
テストは平手、持ち駒、成駒を含む局面の往復、無効な玉位置、無効な符号、カーソル終端を検証する。
保存形式を追加するときは、既存の 32 バイト値を別形式として再解釈せず、形式名と明示的な変換経路を分ける。
ビット I/O の規約
BitWriter::write_one_bit と write_n_bits は、現在のカーソルから下位ビット側へ値を書き込む。
BitReader は同じ順序で読み、要求したビット数がバッファを越えると BitCursorOverflow を返す。
符号の長さが可変でも、エンコード完了時にはカーソルが 256 ビットであることを debug assertion とデコード時の検査で確認する。
固定長バッファを使うからといって、任意の 32 バイト列が有効な局面になるわけではない。
バイト順と比較順
PackedSfen::cmp_bytes は data のバイト列を辞書順に比較する。
cmp_sbk_words は同じ 32 バイトを eight little-endian u32 に直して比較する。
二つの順序は一般に同じではないため、レコード形式が指定する比較規則を選ぶ。
ハッシュキーやソートキーに使う場合も、どちらの順序かを型名または API 名で明示する。
局面復元後の状態
set_packed_sfen と set_huffman_coded_pos は盤面と持ち駒を復元した後、ビットボードを再構築し、状態スタックを現在局面へリセットする。
そのため復元後の Position は局面操作に使えるが、復元前に積まれていた指し手履歴を保持しない。
反復判定のように履歴を必要とする処理へ渡すときは、この境界を意識して新しい探索状態として扱う。