USI対応の思考エンジンを作ったときに遭遇したバグ

USIで使える将棋ソフトを作りたいと、ずっと思っていたが、ついに動いた。
とりあえず、駒割とアルファベータのみ。
最後にBonanzaと対戦させた棋譜を載せる。すごい水平線効果。
その単純なプログラムを作るまでに遭遇したバグの全てをここに書く。

遭遇したバグ

for (j = 0; j < 16; i++){ という無限ループ。長時間発見できなかった。
最初にUSIを通して指した手は、Illegal move 8a5aで反則負け。
(8-x) + (8-y) * 16 とするべきところ、なぜ気づかないのか(8-x) + (9-y) * 16。
これでやっと初手の合法手が30手になった。
評価値の差分計算で、評価値の符号が反転されているときを考慮していなかった。
歩を突き抜けて角が成った(▲26歩△88角成)。
歩を0で表していたので、cap &= 15;(駒の先後の情報を除く処理)とした時点で
capは0となり、駒がない、角の利きが止まらない扱いになっていた。
二歩の反則をしてしまう。
同じ段に歩を打たないようにしていたかと思ったが、それはちゃんとしている。
各筋に歩があるかを保存しておく配列の初期化を忘れていた。
しかも、fl[(i&15)+tn*16] をfl[i&15+tn*16] と書いていた。
後手番のとき、評価値の初期化で符号を逆にするのを忘れていた。
手を進めて戻すと駒が消えてしまう現象。
生成した手の中には、取った駒の情報も含まれているのだが、
歩を0で表していたため、駒を取らなかった扱いになってしまう。
なので、持ち駒にならない玉を0に、歩は7に変更した。
makeHand(j + 144, i, j, 0, 0); 手生成、特に駒を打つ手の場合。
144は、まだ盤面の仕様をよく決めていなかったときに書いたものだった。
だが、それを今まで引きずっていた。正しくは176。だいぶよくなってきた。
しかし、まだたまに(内部で保持する盤面から)玉が消える。
玉を取ったときも駒の表現が0なので手を戻せないと思われる。
王手放置を生成して取られる手を読むような手抜き実装なのがいけない。
玉を取る手を生成した瞬間に+30000点を返すようにして対症療法とする。
角の不成を指してきた。成る手のほうが点数が高いはずなのに。
駒割を見ると、「成った金」の価値(現れないのでどうでもいい)のを0にしたつもりが、
その隣の「成った角」の価値を0にしていた。
おそらく、駒の番号を変更したときに間違えたのだろう。
完成度が上がってきたので、USIのgoコマンドで時間を受け取れるようにする。
だが、この辺りから自玉を取る反則手が頻発するようになる。
時間をかけて調べると、盤面を読み込んだ時点で後手玉が存在していない。
原因は、USIの時間を読み取る処理で、盤面を上書きしていたことだった。
何にでも使えるバッファだと思ったらそうじゃなかったという恥ずかしいミス。
依然としてタダで成れるのに角成を逃す。
移動先が敵陣にかかっているかの判定でミスってるのはわかったが、
なぜそこが間違っているのか全然わからない。
なぜか、駒の移動量を16で割れば、駒が何段移動したかわかると思い込んでいた。
露骨な間違いも思い込むと全く見えなくなる。
実際は、移動量が15でも16でも1段だけ移動している。

棋譜(PentiumM-1.1GHz 一手1秒)

先手:私が作ったソフト
後手:Bonanza Feliz 0.0 (bonasse32)


▲9六歩 △3四歩 ▲9五歩 △6二銀 ▲8六歩 △8四歩
▲9四歩 △同 歩 ▲8五歩 △同 歩 ▲7六歩 △4四歩
▲7五歩 △8六歩 ▲7四歩 △8七歩成 ▲5五角 △8五飛
▲5六歩 △5四歩 ▲4六角 △4五歩 ▲7三歩成 △4六歩
▲8六歩 △同 飛 ▲6二と △同 金 ▲9四香 △同 香
▲7一銀 △6一金 ▲9二歩 △4七歩成 ▲9一歩成 △7三桂
▲5五歩 △5六香 ▲5八金左 △7八歩 ▲同 銀 △同 と
▲6二銀成 △同 金 ▲4八金上 △5八香成 ▲同 金 △8九飛成
▲7九歩 △同 龍 ▲投了