SFEN と局面文字列
局面の文字列表現には SFEN を使い、Position::to_sfen と SFEN のパーサが相互変換を担う。
SFEN は盤面、手番、持ち駒、手数を人間可読な形で渡すための形式である。
一方、学習データやバイナリ入出力では固定長 32 バイトの PackedSfen と HuffmanCodedPos を使う。
両者は同じ Position の盤面、手番、持ち駒を符号化するが、名前やバイト列が等しいことを保証する互換形式ではない。
変換の境界
Position::to_packed_sfen と set_packed_sfen は PackedSfen を生成・復元する。
Position::to_huffman_coded_pos と set_huffman_coded_pos は HuffmanCodedPos を生成・復元する。
to_packed_sfen と to_huffman_coded_pos は表現不能な局面で panic する簡便な入口である。
外部入力から作った未検証局面を符号化する場合は、try_to_packed_sfen と try_to_huffman_coded_pos で Result を確認する。
いずれの復元も Result を返し、ビット列の終端超過、不正な駒符号、不正な玉位置をエラーにする。
復元時の ply は局面の手数を呼び出し側が与える。
固定長形式は局面本体を表すものであり、棋譜の指し手列、反復履歴、評価値を含まない。
実装上の原則
ビットの読み書きは BitReader と BitWriter が担当する。
どちらもバイト列を下位ビット側から順に進め、読み取りはバッファ末尾を越えると失敗する。
符号化は玉位置を先に置き、残る駒を盤上または持ち駒へ割り当てる。
復元では駒の在庫数と玉位置を検査してから Position を構築する。
この章では現在の API と検証可能な契約を扱い、バイト単位のレイアウトを外部プロトコルとして固定しない。
SFEN の構造
SFEN は空白で区切られた盤面、手番、持ち駒、手数の四つのフィールドからなる。
盤面は九段を / で区切り、数字は連続した空きマス数、英字は駒、+ は成駒を表す。
手番は b または w、持ち駒がなければ -、手数は正の十進数で表す。
lnsgkgsnl/1r5b1/ppppppppp/9/9/9/PPPPPPPPP/1B5R1/LNSGKGSNL b - 1
この文字列は局面のスナップショットであり、そこへ至る指し手列は表さない。
Position::from_sfen は新しい局面を作り、set_sfen は既存の局面を更新する。
to_sfen は必要に応じて game_ply を受け取り、局面を文字列へ戻す。
パース時の検査
SFEN パーサは、段数、各段の幅、駒記号、成り記号、手番、持ち駒、手数を検査する。
構文が読めても、玉の数や駒在庫などの局面不変条件を別途検査する必要がある場面がある。
入力を受け取る境界では Result のエラーを呼び出し元へ返し、不完全な局面へ黙って補正しない。
出力文字列の同一性は、局面が同一であることの唯一の判定手段ではない。
局面の比較には、必要に応じて盤面、手番、持ち駒、手数、履歴のどこまでを同一視するかを明示する。
テストで扱うケース
SFEN の往復テストは、平手だけでなく持ち駒、成駒、手番、手数を変えた局面を含める。
異常系では、段幅不足、未知の駒記号、壊れた持ち駒、手数の不正値を個別に確認する。
文字列を入力する API の変更では、構文テストと局面構築後のビットボード不変条件の両方を確認する。