コンピュータ将棋の開発中に遭遇したバグ

Q.バグ状況
A.バグの原因
という感じで、ここ2ヶ月に遭遇した大きいバグを書いていく(ていうかQ&Aじゃねえ)。
Q.連続して同じ局面を読ませると違法手を指す。
A.局面をセットする前にゼロクリアするのだが、その処理を誤って削除していた。
Q.置換表の使用エントリ数を示すカウンタが、実際より1だけずれている。
A.棋譜の局面を登録するときにインクリメントしてなかった。
Q.デバッグのためにpopcntを使って様子を見ていたが、うまくいかない。
A.popcnt関数自体が間違っていた。古いBitboardの仕様を引きずっていた。
Q.探索中、無限ループに入ってしまう。
A.if (d <= 20) return qsearch<TN>(a, b, d); の return を書き忘れていた。
Q.通常探索と静止探索を分離したが、なぜか動かない。まさか静止探索で200以上の指し手を生成しないだろうし。
A.王手されているときは王手放置を含む全ての指し手を生成していた。
Q.負けの局面で勝ちを返す。静止探索を真面目に考えないとダメか。
A.二歩は生成していないのに探索で二歩する。二歩チェックせずにKiller Moveを使っていた。
Q.評価値がおかしい。
A.静止探索で、生成した取る手が全て王手放置だったとき-∞を返していた。
Q.落ちる。
A.置換表からPVを得るとき、千日手の変化で無限ループしていた。
Q.静止探索が詰みを返さない?
A.指す手がないときは常に評価関数を呼んでいた(王手されているときは詰みを返せる)。
Q.NPSが、読んでいるうちにぐんぐん下がる。
A.深さ2の探索でデバッグ用の出力をしていた。
Q.ありえない手を指す。
A.Fail-Lowした手をbestmoveで返していた。
Q.変な手を指す。
A.置換表の評価値で、自分より残り深さが大きい(つまり自分と異なる)ものを探索に使うのはまずい。
Q.何か変。
A.手番を設定する前に手番を使って分岐していた。
Q.実際に指すことはないが、読み筋に連続王手の千日手が現れる。
A.置換表を見ずに静止探索へ入り、その手を置換表に登録するから。バグではない。
Q.t15(15bitめを取り出した変数)が消えてしまう!?
A.1<<15を誤ってt15と書いていたので、そもそも15bitめをセットしていなかった。
Q.探索終了後、14bitめを消す処理で消しきれないことがある。
A.打ち歩詰めというリターンパスを忘れていた。
Q.(置換表絡みのバグを疑っていて)そもそも置換表の評価値を使わなくてもダメだ。
A.前回の指し手を記録する変数を、指し手ループ内で毎回戻す必要があった。
Q.高速1手詰めが変。
A._mm_andnot_si128だと思ってコピペしてたものが_mm_and_si128(&演算子にし忘れ)だった。
Q.手生成。
A.Bishopと書くべきところを、Horseと書いていた。
Q.相手の持ち駒が減るだけのケースで手を返さない。
A.手を置換表に登録せず-∞を返すから。1手詰めの局面もそうだった。
Q.Triangular PV-Tableの四角形版(簡単)を作った。静止探索の手順がおかしい。
A.手が更新されないケースのために、qsearchの頭で0を入れとくのね(忠実に真似ればいいだけ)。
Q.学習中、頻繁に落ちる。
A.search<TN, 学習用>内でsearch<TN, 通常探索>を呼んでいた。
Q.反則負け。
A.Fail-lowで負けを読み切ったとき、bestmoveが書き込まれていなかった。
Q.学習が少しおかしい。
A.学習対象の局面に、王手の情報が入っていなかった。通常探索に近い前処理が必要。
Q.学習が少しおかしい。
A.評価関数を一手毎に変えるなら置換表の評価値を使ってはいけなかった。
Q.王手回避の生成漏れ(合駒)があるようだ。
A.探索開始時にchecker(王手している駒の一つを入れる変数)を設定していなかった。
Q.角が増える。
A.静止探索を含めると探索深さが16に達することがありPV-Tableが死んだ。
Q.駒割(全部10の倍数)だけなのに評価値49って何だよお!
A.αが10の倍数でないことはあり、Futilityでαをそのまま返している。バグではない。
Q.持ち駒が減るだけの手を指す。
A.学習時に置換表をクリアするのを試していたのが残ったままだった。
Q.checkerを取るときに玉で取る手を生成していない?
A.checkerを取る手と玉を動かす手は重複しているが、両王手で玉を動かす手しか生成しないときも重複を除いていた。
Q.千日手模様でbestmoveを返さないことがある。
A.スコアが経路依存で置換表に書けないとき、以前のスコアを消していなかった。
Q.負けの局面で負けじゃない評価値を返した。
A.その手以外は全て負けと読み切ったため、読みの浅い評価値を返した。バグではない。
Q.簡単なはずなのに打ち歩詰めを回避できていない。
A.宣言済みのsにスコアを代入していたつもりが、int sと書いて別の変数に入れていた。
Q.学習時に落ちる。
A.intの配列にuint16_tの配列をmemcpyしていた。
Q.同じコードをコンパイルしてるのに読み筋が変わる。
A.局面編集をすると棋譜が消去される。将棋所の使い方が悪かった。
Q.連続王手の回数が棋譜から反映されていない。
A.棋譜を読み終わったとき、棋譜を使わなかった頃の名残りで局面を読み込んで上書きしていた。
Q.久々の反則負け!
A.sfenを読むコードは昔書いたままで、そこから指し手を得る最近書いたコードが間違っていた。
Q.稲庭将棋と指したら千日手を避けなかった。
A.置換表の世代をn手目にはn回インクリメントしていて、O(n^2)で増えていた。
Q.稲庭将棋と指したら807手目を指せずに落ちた。
A.sfenを受け取るバッファが不足していた。
Q.std::ifstreamでファイルを開けない。
A.openしてcloseする前にopenしたら失敗するのを知らなかった。
Q.詰みを読み切ったが違う手を指して負けた。
A.おそらく短い詰みを選ぼうとして評価値がオーバーフローした。
Q.金打ちの合駒があるのに、合駒しない。
A.開き王手かを、打つ場合(不必要、想定外)もチェックしていた。
Q.駒割の学習が全然ダメだ。
A.学習のための初期化関数を呼び忘れていた。学習用の局面に手番を入れていなかった。王手回避専用の手生成をするようになったのを忘れていた。
Q.駒割の学習がやっぱり全然ダメだ。
A.教師より良く評価してしまった手で学習するつもりが、1手指した局面では符号が逆になることに気づいていなかった。致命的なバグだが、恐ろしいことに2駒関係はそこそこ学習できていた。
Q.歩をすり抜けて馬が移動した。詰みがある局面だし、王手関係かなあ。
A.Killerの合法手チェックで、%と&の書き間違い。
Q.定跡を抜けた次の手で落ちる。
A.探索前に局面を保存しておくが、定跡中は保存しておらず、それを前提にしたデバッグ用関数で落ちた。
Q.2番めにいい手の評価値がうまく求まらない?
A.評価値を更新したときしか見ていなかった。2番めの手に更新の機会がないのが理想。
Q.落ちる。持ち駒が飛7角7金15となっている。メモリが塗り潰されたか。
A.置換表の持ち駒が壊れてハッシュ手で持っていない金を打っていた。|を||と書いていた。