将棋と同じにしていると、いろいろ不都合が
将棋関連のアプリ(5五将棋、7七将棋アプリ、禽将棋アプリ)と同じ評価関数でチェスアプリをリリースしましたが、いろいろ不都合が出てきたのでチェスアプリの評価関数に将棋アプリには無い独自の属性を追加していろいろ試しました。最終的には不採用にしたものもあるのですが、どんなことを試行錯誤していたのか紹介します。
1.キャスリングに加点する
チェスアプリをリリースした当初は、ここはキャスリングする一手だろうと思われるテストケースを用意して、その局面でキャスリングの手を選ぶように評価値を加減していました。先読みの過程で、キャスリングしていない局面からキャスリングした直後の局面の評価値に下駄を履かせて、無理やりキャスリングするようにするわけです。
例えば以下のような局面です。
局面1:白番。最善手はキングサイドキャスリング?
局面2:黒番。最善手はクイーンサイドキャスリング?
キャスリングが最善手と思われる局面を適当に用意して、キャスリングの手を選ぶように評価値を加減していたのですが、こういう付け焼き刃なやり方はどこかで歪みが出てくるのが常で、chess.comのコンピュータ対戦テスト中にアプリがかなり優勢な局面で悪手と言えるキャスリングを選んだのをきっかけに採用するのを止めました。
chess.comでコンピュータとの対戦1テスト中に出現した一例が以下の局面です。
局面3:黒番。キャスリングは悪手
この局面でアプリ(黒)がクイーンサイドキャスリングしました。キャスリング自体は悪い手では無いと思いますが、h2のクイーンが只で取られてしまいますこのケースを切っ掛けにキャスリングの評価値に下駄を履かせるのはやめました(Version1.0.1.4)
局面4:局面3からBf3と指したところ。
今では上図のように3の局面からBf3+と指すようになりましたが、キャスリングの評価値に下駄を履かせるのを止めた所為か、対局全体を通して滅多にキャスリングをしないチェスアプリになってしまいました。これは評価関数そのものに問題があるのかもしれません。また、局面1のケースではキングサイドキャスリングせずにQc7と指すようになりました。これがキャスリングよりいい手なのかどうかは分かりません。チェス初心者なので尚更分からないのですがどうも自分のチェスアプリは居玉のまま無理攻めしていくような印象があります。
2.チェスの方が千日手回避し難い
自分の将棋関連のアプリは、駒の損得が発生しない状況だとあまりにも千日手になるケースが多く、対局が楽しめないのでなるべく千日手を避けるようにしています。アプリが不利な状況の時に負けないために戦略的に千日手を狙うというのではなく、予備の手を用意しておいて千日手になりそうな時は予備の手を選ぶという単純な機能です。勝負に負けないためではなく、千日手を回避することを目的にした機能です。
将棋アプリだと予備の手を一つ用意しておくだけでほとんど千日手になることは無いのですが、チェスアプリで同じ機能をつけていてもよく千日手になってしまいます。
例えば以下のようにアプリ側(白)が圧倒的に有利であっても黒が千日手を狙ってくるとリリース当初は簡単に千日手になっていました。
局面5:
予備の手を用意していても、この局面ではルークやビショップが一路違うだけで指し手の意味があまり変わらない手がいっぱい存在する局面なので、Bf4,Bg3,Bh2やRg6,Rf6,Re6などの似たような手を繰り返すだけで、黒のキングがb7近辺を動いているだけで簡単に千日手になってしまいます。アプリ側(白)が圧倒的に有利なのにも拘らずです。
そこで同じ局面が2回出現した時は思い切って駒の重みをある程度ランダムに変更して指し手を変えることにしました(Version1.0.1.4)。
3.Pawnのランク(行)位置2で駒の価値に加点する
2.の千日手回避の目的とも関わるのですが、ポーンの位置によってポーンの駒の価値に加点することにしました(Version1.0.1.4)。
局面5では圧倒的にアプリ側(白)が有利なので、ビショップやルークを動かすよりポーンをゆっくり前に進めてクイーンに成るのが確実な勝ち方だと思います。でも自分のアプリでは3手〜5手読みなのでポーンが一つや二つ前に進んでもポーンの価値は変わらないので、ポーンを前に進める手を選んでくれませんでした。
そこでポーンのランク位置によって先手なら2ランクにあるポーンより8ランクにあるポーンの方が価値があるという評価関数にしました。先手の場合2ランクから8ランクに到るまで放物線を描くように加点しています。2ランクにあるポーンより3ランクに位置するポーンの方が価値が高いのかどうか難しいところですが、8ランクに辿り着いてクイーンに成るまでにも一応変化を与える必要があるためそうしました。その結果、以下のようにポーンを進めるようになりました。
局面6:局面5からポーンを進めた(d4)局面
こういうやり方が必要?なのはアプリの読む手の深さにも関係しています。将棋の場合なら敵陣に歩を打って相手が何か指して次に打った歩を成ればいいので、都合3手以上の読みの深さがあれば「歩」の価値が「と金」の価値となって評価値に反映されますが、チェスの場合は持ち歩を打つという選択がないのでちまちまとポーンを前に進めていく手を読まなければならないので、読みが浅いとポーンがクイーンに成る局面まで評価出来ないことに起因してます。だから巷の深く読んでる強いアプリ(全部Stockfish由来?)は評価関数内でこんなことしてないと思います。
深く読めないのがもどかしい
他にもポーンチェーン3に加点するなんてことも試しましたがボツにしました。
評価関数のパラメータ調整というのでしょうか?いろいろ試行錯誤しましたが、こういう職人技的な作業が必要なのは自分のアプリが深く読めないことが一番の原因です。Javascript製で尚且つデータ構造も人間が理解し易い(所謂高級な)ままなので実行速度が出せません。深く読めば読むほど強くなるわけではないと思います4が、どこまで読めばいいかを語る以前の段階という感じです。Javascriptでこういうアプリを作るのは自分に手枷足枷を課して作っているようにも感じるのですが、アイデアを絞り出して問題解決するのもプログラミングの醍醐味なので、なんとかチェスアプリとして最低限のレベルは確保していたいと思ってます。
アプリのままだと制約が多いので、一度アプリより深く読む設定でCLI上で実行してchess.comのコンピュータ対戦でどのレベルまで勝てるのか試してみようと思ってます。読みを深くするだけで強くなるようであれば、評価関数の出来自体は悪くないということですし、また新たな問題を見つけることが出来るかもしれません。
4.ステイルメイトの局面評価(2021-02-22追記)
以前の記事5に書いているのでここには書いていなかったのですが、「俺氏、将棋が二人零和有限確定完全情報ゲームでないことに気づいてしまうwww」という記事を読んで、将棋の千日手とチェスのステイルメイトは似ている点があることに気づいたので書いておきます。
この記事の趣旨とは別の話になると思うのですが、将棋ソフトの「やねうら王」の思考ルーチンには「引き分け(千日手)を回避する意欲」みたいなパラメータがあるそうです。自分の将棋関連アプリはそこまでやっていないのですが、チェスのステイルメイトを評価する時に例外的な局面評価をする必要があるので、チェスアプリでは特殊な評価をしています。
チェスのステイルメイトは引分けと言ってもレピュテーションドロー(千日手)と違い、劣勢な時はステイルメイトを狙う必要があるけど、優勢であればチェックメイト(詰み)を狙う方がいいので優勢な側はステイルメイトを避けたいところです。つまり引分けと言っても評価値を0にすればいいという話にはならないのです、このことは以前の記事に書きました。
内部的には打ち歩詰めの局面評価と同じ方法を取っているのですが、自分の将棋関連アプリは相手玉を詰ませば最大評価値(勝ち)だけど、打ち歩詰めの場合は評価値に-1を掛けて評価値を反転(最少評価値、負け)にしています。チェスアプリの場合は相手玉をチェックメイトすれば最大評価値(勝ち)だけど、ステイルメイトの場合は評価値に-0.7
を掛けて評価値をやや反転?(劣勢から優勢)するようにしています。この処理は前述の「引き分け(千日手)を回避する意欲」に当たると思います。劣勢な時はステイルメイトの評価値が-0.7
を掛けることによって高くなり(優勢)、優勢な時は-0.7
を掛けることによって低くなる(回避する)方向に働きます。将棋の場合、リーグ戦の状況などによって積極的に千日手(引分け)を選びたい状況があり得ると思いますが、チェスの場合はたった一局の勝負でも劣勢なら常に積極的にステイルメイトを選びたいゲームなので、チェスの場合は必須の機能と言えるかも知れません。巷の将棋AIの多くも元々はチェスのプログラム(Stockfish)をベースにしているらしいので、こういう機能が備わっているのだと思います。
ところでこの-0.7
というマジックナンバーですが、明確な根拠はありません。以前の記事に書いたように、こうしないと優勢なのにも拘らずステイルメイトになる手を選んでしまうケースがあったので、適当な数値を設定してステイルメイトを避けるようにしただけです。この値をどのように決めるべきなのかは今も分かりません