TadaoYamaokaの開発日記

個人開発しているスマホアプリや将棋AIの開発ネタを中心に書いていきます。

Unityで将棋アプリの開発 その10(AIの戦型指定)

作成している将棋アプリにAIの戦型指定を実装した。

実装方法

先日実装した定跡の戦型予想を使って、指定した戦型が定跡の候補手に含まれる場合、その手の採用率と戦型の確率に応じて、次の手を選択する。
そうすることで、指定した戦型になるように誘導できる。

振り飛車を指定すると、振り飛車を指してくれる。
f:id:TadaoYamaoka:20220314224801p:plain

課題

人間側も定跡にある手を指さないと指定した戦型にはならないので、人間側も定跡にある手を指してもらう必要がある。
少し試したところ、振り飛車の登録されている定跡が短く指定した戦型にならない場合があった。
floodagteの棋譜から定跡を作ると振り飛車の定跡の短いという課題がある。
これについては別途検討したい。

UI

戦型を指定するために、簡易的な対局条件の指定画面を作った。
f:id:TadaoYamaoka:20220314224005p:plain

戦型のドロップダウンをタップ(クリック)すると、戦型を選択できる。
選べる戦型は、暫定で適当に登録している。
f:id:TadaoYamaoka:20220314224143p:plain

戦型分類の定義は、より細かく分類されているので、集約している。
例えば、振り飛車の場合、四間飛車三間飛車中飛車、向かい飛車、振り飛車片銀冠、振り飛車銀冠、振り飛車穴熊振り飛車銀冠穴熊が「振り飛車」になる。
(右四間飛車振り飛車にならないのは、りゅうおうのおしごと13巻で予習済みである)

まとめ

AIの戦型を指定できるようにした。
戦型の誘導には、先日実装した定跡の戦型予想を使用しているが、floodagateの棋譜から定跡を作ると振り飛車の定跡が少なくなるため、定跡が外れやすくなる課題が見つかった。
振り飛車の定跡をどう作成するかは別途検討したい。

次は、棋力測定モードを実装したい。

Unityで将棋アプリの開発 その9(定跡と戦型予想)

作成している将棋アプリに定跡検索と戦型予想を実装した。

定跡ボタン(ノートのアイコン)を押すと、現在の局面の定跡の候補手と、各手を指した後にどの戦型になるかの確率を表示する。

候補手は、出現率と勝率も合わせて表示する。

f:id:TadaoYamaoka:20220309193506p:plain

定跡の元データ

定跡の元データには、floodateの2018年以降のR3500以上のソフト間の棋譜を使用した。

ある程度レーティングの高いソフトの指し手を登録した方がよいのでレーティングでフィルタを行ったが、R3300は技巧2の棋譜が多いためそれよりも少し大きいR3500とした。

なお、floodgateのレーティングの分布は以下のようになっている。
f:id:TadaoYamaoka:20220309193808p:plain

戦型予想

floodgateの棋譜から、定跡の各手を指した後に、どの戦型になったかの統計情報を取得して、それを確率として表示する。

戦型の分類には、Mizarさんが公開されているjsonの定義ファイルを使用させてもらった。
相掛かりの上限手数が20手までだと該当棋譜が減るため、24手までに修正して使用した。

戦型の分類は、対局中にも行い、手を指した後の戦型を表示する。
f:id:TadaoYamaoka:20220309194420p:plain

まとめ

将棋アプリに定跡と戦型予測を実装した。
戦型予測は、他のアプリにはユニークな機能で、自分自身が欲しいと思っていた機能なので、このアプリを使う理由になってくれればよいと思っている。

また、戦型予測を使って、AIに指定した戦型になるように指させることができる。
相手が振り飛車の練習をしたいといった場合に便利になると思う。
次は、AIに指定した戦型を指させる機能を実装したい。

Unityで将棋アプリの開発 その8(ヒントの実装)

作成している将棋アプリに、ヒント機能を実装した。

ヒントボタン(電球アイコン)を押すと、下の図のように候補手の上位3つを矢印で表示する。

f:id:TadaoYamaoka:20220305103329p:plain

複数候補手表示

下側のテキストボックスには候補手を10まで表示する(表示領域は5行までだがスクロールできる)。
テキストボックスには、勝率と読み筋も表示する。

探索がMCTSの場合、候補手が1つでも複数でも計算時間は変わらないので、短時間の探索でも複数表示できる。
これは、既にあるスマホの将棋アプリとの差別化になると思う。

なお、MCTSだと2番目の候補手の勝率が1番目より高くなることがあるが、わかりにくいのでその場合上位の手の勝率に合わせるようにして、勝率が逆転しないようにした。

リアルタイム更新

ヒントの候補手の探索にある程度時間がかかるため、プレイヤーの手番ではバックグラウンドで常に計算するようにして(探索結果は形勢グラフでも使う)、ボタンが押されたら、その時点での結果を表示し、一定間隔置きに最新に更新するようにした。
PCの将棋GUIの検討機能に近い使い勝手になる。

精度

ヒントの精度は、スマホでCPUで計算しているため、PCで最新ソフトで検討するほどにはならないが、初級者が使うには十分な精度になる。

ぴよ将棋の最高レベルのぴよ帝と対局させてみたが、ぴよ帝は1手10秒くらいかかるが、Pixel5でプレイアウト256で1手8秒くらい探索して、後手番で勝つことを確認しているので、スマホで動く将棋アプリとしては最高レベルの強さがある。

ヒントの検討では、512プレイアウトまで探索するようにしている。
途中結果を表示するので、最後まで待たなくても途中で操作できる。

実装

矢印の描画

Unityで矢印を描画するのにかなり苦労した。

Unityの標準では矢印を描画する機能はないため、やり方をネットで調べていくつか試したが綺麗な表示ができず、最終的にメッシュにテクスチャを貼る方法で実装した。

AndroidAPIで開発する場合は、drawLinesでペンとブラシを使えば簡単に実装できるが、Unityでは2Dの高度な描画APIは用意されていない。

2D向けにはスプライト機能があるが、これで任意の長さの矢印を描画しようとすると画像を引き延ばすことになるので、矢印の先端の三角の部分まで引き延ばされてしまう。

先端の三角の部分と線の部分を分けて組み合わせることもできるが、画像の透過を行っている場合、接合部分をドット単位で綺麗につなげることができないため、色が濃い部分ができてしまう。

Line Rendererを使って、矢印のアウトラインを描画して、内側にメッシュを生成して描画することを考えたが、Line Rendererはアンチエイリアシングが効かず斜めの線がギザギザになってしまうので使えなかった。

結局、メッシュにテクスチャを貼る板ポリゴンとして、矢印を描画することにした。
テクスチャにはアンチエイリアシングが効くので綺麗に描画できる。

矢印を伸ばす場合は、UV座標をそのままにして、線部分のメッシュの頂点の座標を長さに合わせて変更すればよい。
メッシュの頂点座標とUV座標は、テクスチャの矢印形の少し外側にしておくと伸縮した場合にも崩れない。

向きは、メッシュを自体を回転させることで実現できる。

矢印を引きたい2点間のx方向とy方向の差を使うと

arrow.transform.rotation = Quaternion.FromToRotation(Vector3.up, new Vector3(dx, dy, 0));

のようにして、向きを指定できる。
この際、dx=0でdyが負の場合、y軸方向にも回転してしまうため、その場合だけ、Quaternion.Euler(new Vector3(0, 0, 180))にする必要があった。

矢印のプレハブ化

矢印の表示のたびに、メッシュを動的に生成するのは処理が煩雑なため、生成したメッシュをプレハブ化した。

メッシュを生成するコードを書いて、一度実行してアセットとして保存しておき、保存したメッシュをMesh Fillerに設定して、プレハブ化すると矢印の生成のコードがシンプルになる。
一度保存したらメッシュ生成のコードはコメントアウトしておく。
アセットの保存は、以下のサイトの方法を参考にした。
Unityでメッシュをスクリプトから保存する方法 | 3DCG school

まとめ

将棋アプリにヒント機能を実装した。
矢印の描画にかなり苦労したが、メッシュにテクスチャを貼る方法で見た目のクオリティはイメージ通りすることができた。

次は、定跡機能を実装したい。

Unityで将棋アプリの開発 その7(待ったの実装)

作成中の将棋アプリに、ボタンを追加して、「待った」ができるようにした。

UIデザイン

頻度の高い機能をゲーム画面から直接押せるボタンとして、それ以外はメニューを開いてから選べるようにする。
「待った」「ヒント」「定跡」を直接押せるボタンとして、「待った」した後は「進む」と「戻る」ができるようにする。

ボタンのデザインは、マテリアルデザインのアイコンを使用した。
https://fonts.google.com/icons

待ったは、日本では手のマークがよく使われると思うが、海外では通じなさそうなので、lishogiを参考に、リバートのアイコンにした。
定跡はノートのアイコンにしたが、少しわかりにくそうなので変えるかもしれない。

ボタンの下に文字を合わせて表示するか悩んだが、スマホだとデザインがごちゃごちゃするので、アイコンのみとした。
分かりやすさとのトレードオフなので、文字を使わない場合はアイコンを分かりやすくするのが重要だと思う。

いったんボタンを配置してみてデザインは、以下のようになった。
f:id:TadaoYamaoka:20220302000049p:plain

ボタン処理の実装

UnityでCanvasにボタンを追加して、背景にイメージを使用するように設定して、Clickアクションに待った処理を実装したメソッドを設定した。

はじめ、ボタンが全く反応しなくて、試行錯誤していたが、盤の座標に使っている文字のWidthとHeightの範囲がボタンと被っていたのが原因だった。
文字が表示されている領域とは関係なくWidthとHeightの範囲が被ると駄目なようだ。

待った処理のメソッドに、2手戻す処理を実装して、待った後は「進む」と「戻る」ができるようにした。

はじめAI思考中に待ったを無効化する処理を実装したが、無効化でボタンの色が変わると視覚的に邪魔なので、AIが思考中にも待ったは先に押せるようにして、思考終了後にすぐに2手戻すようにした。

まとめ

インターフェースにボタンを追加して待ったができるようにした。
次は、ヒントと定跡を実装したい。

Unityで将棋アプリの開発 その6(駒音)

前回駒のアニメーション処理ができるようになったので、合わせて駒音も鳴らすようにした。

音の素

商用でも利用可能な駒音の素材を探したところ、素材を配布しているサイトがあったので、使わせてもらうことにした。
音素材: 「駒」のカテゴリー | ノタの森
クレジット表記は必要になるようだ。
後で、自分で録音して置き換えることも検討したい。

実装

UnityでAudio Sourceを使って鳴らすだけなので、特に難しいことはなかった。
アニメーションが終わるタイミングで音のピークがあるように鳴らすタイミングを調整したくらいである。

まだUIがないが、とりあえず遊べるようになった。
先手後手の表示と手数の表示もするようにした。
youtu.be

まとめ

アニメーションに合わせて駒音を鳴らすようにした。
AIは1手3秒くらい思考しているが、ストレスを感じないで遊べるようになったと思う。

次は、投了や待ったといったUI周りを実装したい。

Unityで将棋アプリの開発 その5(形勢グラフ)

AIの探索結果を形勢グラフに表示するようにした。

プレイアウト数が64なので誤差は大きいが、リアルタイムで確認できることで初級者には形成判断の練習になればよいと思っている。

f:id:TadaoYamaoka:20220227000023p:plain

強さのレベルの調整のために接待dlshogiの機能を、実装したいと考えているので、その場合探索結果の勝率が正しくなくなる。

その場合は、人間の手番でも非同期で思考するようにしたい。


次は、駒の効果音を実装したい。

Unityで将棋アプリの開発 その4(駒のアニメーション)

前回Androidの実機で確認しているときに、着手時に駒をアニメーションさせると、AIが思考中の待ち時間を感じにくくなることに気付いた。
効果を確認するために、駒のアニメーションを実装した。

Unityは、フレームごとの処理をオブジェクトのUpdateに記述するだけでアニメーションを実装できるため、簡単に実装できた。
ゲームエンジンを使わずにスクラッチで同様の処理を実装する場合、結構実装が大変である。

AIの思考中に非同期にアニメーションを行う

前回、コルーチンで非同期を実装したので、駒のプレファブにアニメーション用のスクリプトを追加して、着手時にアニメーションを開始して、Update()でTime.deltaTimeに応じて表示座標を計算するようにした。

    void Update()
    {
        if (_isMoving)
        {
            _elapsed += Time.deltaTime;
            if (_elapsed > timeToMove)
            {
                _isMoving = false;
                this.transform.position = _position;
                return;
            }
            this.transform.position = _position - _velocity * (timeToMove - _elapsed);
        }
    }

    public void MoveFrom(Vector3 position)
    {
        _isMoving = true;
        _position = this.transform.position;
        _velocity = (this.transform.position - position) / timeToMove;
        _elapsed = Time.deltaTime;
        this.transform.position = position;
    }

これだけで、AIの思考中にアニメーションさせることができた。
Unityを使うと、表示周りの動的な処理が簡単である。

効果

プレイアウト64で3秒くらい思考させた場合にも、体感2秒くらいになった気がする(実際気のせいなのだが)。
f:id:TadaoYamaoka:20220226133129g:plain

効果音も付ければ、さらに効果が高まるのではないかと思う。

まとめ

着手時の駒のアニメーション処理を実装した。
Unityを使うと非同期のアニメーション処理を簡単に実装することができた。

だいぶ遊べるようになってきた。
次は形勢グラフの処理を実装したい。