「接待どうぶつ将棋AI」が爆誕！おもてなし接待AIを作る物語。


# はじめに

AIが将棋のプロ棋士より強くなってから久しい。

一方で**羽生善治**先生は、
「**接待将棋のようなことはAIには難しい**」と喝破している。

では実際に接待AIを作ろうとすると、どのような点が難しく、
どこまでのレベルの「接待」なら出来るのだろうか？

本稿は、「接待将棋」という難問に挑んだ開発日誌的な記録として、
得られた接待AIの考え方にいたるまでの物語である。

参考： [人工知能に「接待将棋」はできない──羽生善治と石山洸が語る将棋とAIの進化](https://wired.jp/2017/02/14/habu-ishiyama/)


# 先に完成品を記載（実際に遊べるURL）

結論から述べると、あるシンプルな実装方針で、
自身ではある程度納得出来る実装を得ることが出来た。
以下がその結果（接待AI）と実際に遊べるアプリだ。
PC/スマホどちらでも対応。

https://doubutu-64e43.web.app/

<a href="https://doubutu-64e43.web.app/" target="_blank" > <img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/193512/e2bc5fdb-8a27-918e-0761-18e28f0d6bd1.png" alt="アプリの画面キャプチャ" width="60%" > </a>

「将棋」は難しすぎるため「どうぶつしょうぎ」で作った。

「どうぶつしょうぎ」は、そのユーザを考えると、
実は地球上で最も「接待」が必要とされているゲームだと思われる。
ルールは適宜ぐぐってくだされ。

接待/最強/雑魚の３モードを用意させていただいた。
「接待の美学」として、プレイヤー側が常に先手である。

ゆえに、後手必勝が解明されている「どうぶつしょうぎ」では
**「最強」相手には絶対勝てない**（ハズ）。
ほんとうにガチの「最強」として実装した。

「雑魚」は、次に負けるかどうかだけ分かる、というランダムAI。
ライオン（王将）のキャッチorトライ、が理解出来ないと、
そもそもルールを解していないことになるため、
どうぶつ将棋を楽しめる最弱の人類とほぼ同等、と言える。

「接待」のモードが本稿で言及しているAIだ。
おそらく上級者ほど、「接待AI」が手ごわく感じられ、
一方で初心者でも十分勝ててしまうだろう。

棋譜解析データの容量が重いため、本来は
サーバ/PCのみで実行すべき作者専用のアプリケーションなのだが、
指し手を返すアルゴリズムを無理やりAPI化して、
PWAを使ってPC/スマホ全対応にして、
幅広い人類（※主にお子様）に使っていただけるようにした。
フロント部はこのように後付けのオマケ、雑な実装なので、
UIはちょっとしょぼい。ご容赦いただきたい。


# 改めて、接待将棋とは何か？

まず、通常の将棋は「勝てば官軍」である。
では、接待将棋とは「絶対負ける将棋」なのだろうか？
否、そうではない。

過日、負けることが難しい、
「世界最弱のオセロAI」が話題になった。
これはこれで面白い試みではあるが、
ルールを逆にしただけの「最強AI」とも言える。
~~（負けることが出来ないために負けた気持ちになる）~~

**「接待」とは「おもてなし」のこと。**
飛車をタダで相手に差し上げるような最悪手ばかり指しても、
なんの接待にもならない。

ある程度競った状態になりながらも、
大部分においては客側が勝つ。
わざと手抜いているようには見えないことが重要だ。

※なお、アプリの実装面においても、
　これが「接待」であると分からせないように、
　「解説OFF」にすると「接待」などの文字が消えた状態で、
　お子様や友人に渡すことが出来るようにしてある。

接待では「相手にそうと悟られない程度に手を抜くこと」
が要求されるため、**普通に勝つよりも難しい技術**なのである。

さらに、相手に応じて強さを変える必要がある。
「上級者相手でもある程度互角に戦った上で負けること」
も求められている。
そうしないと、ただの「弱いAI」になってしまう。

相手のレベルを推し量った上で、程よく強さを加減するAI。
しかも加減していると悟られないほど自然に。
確かに羽生さんのお話の通り、実装は難しそうである。


# なぜ「接待どうぶつしょうぎ」か？

「接待」×「どうぶつしょうぎ」
のテーマを選んだ理由は二つある。

**理由①：完全解析済みであり、その解析結果が使えること**

「どうぶつしょうぎ」については、
2009年に田中哲朗先生により、完全解析されており、
78手で後手の必勝、とのことだ。

ただし、この必勝方法を行うためには、
240万局面以上を暗記する必要があり、
とても人間ワザでは出来ないため、
人間が行うゲームとして、つまらなくなったわけではない。

参考： [「どうぶつしょうぎ」の完全解析](https://www.tanaka.ecc.u-tokyo.ac.jp/ktanaka/dobutsushogi/)

接待AIを作るのは難しいといっても、
完全解析済みのゲームであれば、何とかなる可能性がある。
また、〇×ゲームなどとは比較にならないほど、
人類にとって十分に複雑なゲームであり、
これで得られる知見は他のゲームにも活かすことが出来そうである。

田中先生は完全解析の成果を扱いやすいライセンスで公開してくださっており、
本稿の後段でそのプログラム/データをPythonから扱う実装方法も述べる。

**理由②：「どうぶつしょうぎ」は地球上で最も「接待」が求められるゲーム**

「どうぶつしょうぎ」は主にこども向けに開発されたゲームであり、
アンパンマン、しまじろう、などの
各種大人気キャラによるパッケージも散見される。

「お子様」に対し大人げなく勝ちまくるわけにはいかないし、
かといって、あまりアホな手を指すのも教育上よろしくない。
現在の日本において、これ以上
「接待」適用率の高いゲームは無いと言えるだろう。

また「お子様」の成長は大変早く、その棋力は
超初心者から大人顔負けの上級者まで、まさに千差万別である。
通常では、自分と同じくらいの強さのAI、との対戦が面白いのだが、
適切なAIを選ぶのも難しい。

「接待AI」のように、指す過程で力を調整してくれると都合が良い。

私も**どうぶつしょうぎによる接待業務**を
行ったことがあり、その際には手の抜き方に大変苦慮し、
実際にその**必要性を痛感**している。

# 接待どうぶつ将棋の要件定義

これまでの話をまとめると、
欲しい接待AIは、以下の３要件を同時に満たすことになりそうだ。

* ①基本的には**顧客側が勝つ**
　（※毎回必ず、でなくとも勝率が超えれば良し）

* ②顧客に**手抜きを悟られない**
　（※相手のレベルに合わせた手抜き）

* ③顧客に対して**弱すぎない**
　（※相手のレベルに合わせた強さ）

「接待」＝「おもてなし」＝「面白い、気持ち良い試合」
⇒自分と同程度の強さの相手との接戦を制して勝利！
　を実現差し上げること、の言い換えである。

いくら強い人間でも間違えることもあるし、
初心者がプロと全く同じ手を指すことも多々ある。
相手のレベルに合わせるのは至難のワザであり、
しかも、合わせようとすると「顧客側が勝つ」と両立しなくなってくる。
（最初は定石通りでプロのようだが実は中終盤が弱い人、なんていくらでもいる）

さすが羽生先生もおっしゃる難題だ。


# 接待の方針検討（失敗案）

まず「①顧客側が勝つ」を満たすためには、
AIを弱く弱く作る必要があるが、弱すぎると、
「②手抜きを悟られない」を満たせない。

相手のレベルに合わせるor勝負を接戦にするためには、
盤面の形勢を判断して、
およそ互角になるような手を指す、
という案が最初に思い浮かぶだろう。

勝ってる時は悪い手を指して
負けている時は良い手を指し、盤面の均衡を保ち、
勝負が接戦熱戦になるようにする。

しかし、それでは**勝敗がつかなくなってしまう**。

手が進むにつれて目標とする形勢を50-50⇒60-40⇒70-30などのように
加減していけばよいのだが、
それでは途中からアホになっていくだけで、
本来面白いハズの終盤がつまらない。
最後の方で「手抜きを悟られる」状態になる。
超典型的な「こいつワザと負けるように手を抜いたな～」と
分かるパターンの接待である。

ほかの案としては、
終局までの手数が一番かかるような手を選ぶ ＝ 接戦
という方針も思い浮かぶ。

が、将棋において最強AIが
「出来るだけ長手数にする最良の方法」を取る場合、
まず完膚無きまでに相手のコマを取って
相手の動きを完全封印してから、
（その状態なら任意の手数まで引き伸ばせるので）
予定手数近辺になって、ワザとコマを返して
そのまま自殺頓死する、という、
最悪な動きになってしまうだろう。
これでは話にならない。

通常、ボードゲームのAIというのは、
「N手先読み」＆「盤面評価関数」によって、
MIN-MAXで考えて、評価が最良になるように動かす実装が多い。

相手のレベルに合わせて、という実装を考えると、
通常の評価関数が50-50であることを最良としてそれを目指す、
　（直前の相手が悪手ならこちらも悪手、で評価値を戻す）
決着までが遠いこと＝互角と考えてそれを目指す、
　（どちらが勝ちの決着に対してもそれを引き伸ばすような手）
などが自然な発想となるが、
「接待AI」についてはコレでは実現出来そうにない。
発想の転換が必要だ。


# 接待の方針検討（今回採用案）

結論としては、
囚人のジレンマの「しっぺ返し戦略」をベースとした。

相手がその盤面で「N番目に良い手」を指してきた場合に、
次に接待AIは「N+1番目に良い手」を指すような方針だ。

盤面を50-50に戻そうとしているわけではないため、
緩やかに接待AI側が不利になっていく、というのがミソ。

しかし、単純にこの方針で作ってしまうと、
「王手が最善手で、逃げる手が１手しかない場合」
接待AI側が、最善の受けを選択できず、
王手するだけでゲームが終わることが多数発生する。
（※実際に作ってみてそれに気がついた）

王手でなくても「受けが１手しかない飛車取り」などの
応対が一本道となるやり取りが全滅である。
ある程度の上級者がやる場合には全く興ざめだ。
「③弱すぎない」を満たさなくなってしまう。

では、「N番目に良い手」に対し、
「N番目に良い手」を返せばどうだろうか？（同等の手を許可）

これは盤面の状態によって、
どちらの「N番目」がより有利かが異なるため、
「①顧客側が勝つ」を満たせない可能性が高い。
または終わらなくなってしまう。

状況をみてちゃんと負けに持っていくように、
有利になりすぎたら急遽手抜きをする、と、
「②手抜きを悟られない」が引っかかってくる。

いろいろ考えて試行錯誤した結果、
**「最善手乖離度」を同等以下にする**、
という新概念を思いついた。

# 「最善手乖離度」とは？

まず、盤面の評価値は、田中先生の完全解析の考え方を参考に、
両者とも最善手を指した場合に
・自分が勝つ＝　１００点 マイナス [それまでにかかる手数]
・自分が負け＝ ―１００点 プラス　 [それまでにかかる手数]
という評価とした。
これにより、ある手を指した直後の盤面を評価することで、
最善手～最悪手、まで序列をつけることが可能である。

「最善手乖離度」とは、その盤面における相手の手を評価する指標値で、
「相手の手の評価値 ÷ その盤面の最善手の評価値」
で計算される値である。
負数÷負数のようなものを除外するために、
盤面評価値に＋３００のゲタを加えて計算する。

最善手では常に「１」になり、
最善手から離れれば離れるほど「0.9」「0.8」などと下がる。
勝敗が変わるほどの敗着を打つと「0.5」「0.6」あたりになる。
（100+300の１手勝ちがある状態で、
　-100+300の即負けにするのが最悪なので、その場合で0.5）

今回採用したシンプルな実装方針は、着手可能な手の中から、
「相手の手の最善手乖離度**以下**で最大の手」を打つというだけ。

相手が最善手を打ってくればこちらも最善手を返す。
相手が相当悪い手を打ってくればこちらもそれに近い悪手を返す。
がこの一つのロジックだけで実装可能である。

N番目に良い系だと、そもそも着手可能な選択肢が何個あるのか？
という手の広さへの依存度が高いためNG。
最善手との比較ではなく、選択肢の平均値と比較する案だと、
前述のような正解が１手しかないような状態、で失敗するのでNG。
また、「未満」にすると最善手の最善手返しが含まれなくなってしまうため、
「以下」がベストと考えられる。

相手が最善の「1.0」の場合は接待AIも「1.0」だが、
どこかで多少でも不利な手を指した場合、
「それ以下」でピッタリ同じ数字で戻すような手は、
毎回存在するわけではないため、
緩やかに少しずつ、接待AIが不利になる仕組み。

唯一、完全最強手を連発すると、
その時点の盤面評価値のままで決着してしまう。
（つまり、「接待」側が勝つパターンは、
　選択肢が相当少ない時にワザと王を捨てる、か、
　盤面が不利な状態から最後まで最善手の連続を行う、
　の２パターン）

また、手抜き度合いについても、
常に顧客とほぼ同レベルの手を選択することになるため、
そのレベルの手を実施してくる顧客、から見れば
手抜いているかどうかの判断は不可能である。
（手抜いたと分かるくらいなら、顧客側がもっと良い手を指している）
急激に強くなったり弱くなったりという感が理論上発生しない。

ついでに、
「過去に指した相手の手を蓄積して評価する」ような、
本当に相手の棋力を測る複雑な実装ではないため、
「過去の状態や履歴」を持つ必要がなくなり、
API化がやりやすくなったことは大きな利点である。
（過去の蓄積を評価をしようとすると、
　各クライアントの状態をサーバに保持する必要が生じ、
　ユーザごとの履歴＆盤面管理が必要になってしまう。
　APIごとのユーザ認証が生じてしまう）

結論：接待AIの基本実装はこの考え方だけ！

**相手の直前手の最善手乖離度を計算し、**
**AIはその最善手乖離度以下の最良の手を返す**

※ただし「完全解析」と同様に一発負けになる手だけは除外。

以下のアプリを改めて遊んでみて欲しい。

https://doubutu-64e43.web.app/

超初心者のお子様から、上級者の大人まで、
誰に対してもそこそこの歯ごたえを残しながら、
明らかに人間が勝つ勝率になることを実感いただけると思う。


# 「解析」＆「最強」というオマケ

副産物的に面白いと思ったので、
このAIの「評価結果/最善手乖離度」を直接ログ的に
アプリ上に出力している。

さっき自分が指した手が、
どの程度良い手だったのか、
正解は何だったのか？が都度分かるため、
どうぶつ将棋力の向上にも役立つだろう。

また「完全解析」結果を検索しているため、
「最強」AIは本当に強い。
~~「最強」にボコられるのも、~~
~~マゾな人には「接待」かもしれない。~~

ただし、途中でAIを切り替えた場合、
完全解析に含まれないようなレア局面が生じると、
常に最善手を返せるわけでは無い。

# 実装した内容/使用技術

ここでは、どのような開発をしたのか？の
実装面の話を記載する。
※一部のコードや、実装上のポイントは別途後述

まず、田中先生の完全解析の結果と、
その検索用のプログラムをダウンロード（２GBくらい）して
動かせるようにした。(C言語)

次に、私がPythonで動かしたかったので、
Pythonのsubprocess経由でそのプログラムを動かし、
完全解析データをPythonから扱えるようにした。

大いなる誤算があったのは、
「完全解析の結果」は本当にあらゆる局面の
応対が掲載されているわけではないこと。

「完全解析」とは、
二人零和有限確定完全情報ゲームにおいて
理論上、先手必勝or後手必勝or引き分け決まっているが、
そこまでの道のりを全て求めた、という意味であり、
例えば、「王手見逃し」など
通常あり得ないような手/局面は解析に含まないのだ。

完全情報ゲームの一般的理論については、
@drken 先生の素晴らしい解説をご参照されたし。
https://qiita.com/drken/items/4e1bcf8413af16cb62da

「完全解析」データさえあれば、極端な話、
コマの動きをこちらでプログラミングしなくても、
盤面の遷移だけで、ゲームが作れてしまうかも？と思っていたのは
「完全」という名称だけでイメージしていた誤算であった。

結局「トライ」の判定なども含めて
Pythonで全てルールを再実装する必要があった。

ルールの実装後、AI
（※ここで言うAIとは、盤面に対して手を選択する仕組み、程度の意味）
を実装し、相互に戦わせて遊べるようにした。
このへんで、接待AIの方針を試行錯誤。

毎回２GBの問い合わせ＆結果パース処理を実施していると
遅くてしょうがないため、
問い合わせ結果をキャッシュするSqliteのDBを作り、
過去に問い合わせた結果はキャッシュから返すようにした。
（※現在公開しているアプリも、キャッシュを使っているが、
　　過去に経験していない局面が出た場合、
　　手が返ってくるまでに１５秒程度かかるかもしれない）

自身でもCUIで検討を続けるのは辛いものが生じてきて、
GUI化のためAI部分をAPI化し、
指した手＆その盤面、を入力として与えると、
AI側が指す手を返す、という構造に改造した。
（※なお「千日手」は本来引き分けであり、CUI版では実装済みだが、
　　現在公開しているアプリでは、極力履歴を持たない方針で、
　　実装対象外とさせていただいている）

APIのサーバとしては「Flask」を採用し、
GCP(Goocle Cloud Platform)の完全無料インスタンス上で作った。
最小構成のワリに重めの処理をやっているので、
アクセスが多いとサーバが落ちるかもしれない。

クライアント側は、スマホでも遊べるように
「Monaca」を使って「PWA」で実装することにした。
JavaScriptからAPIをコールしている。

アプリ内で選んだコマの移動可能範囲を色付け表示する際に
毎回通信するのもよろしくないことや、
移動後の盤面をサーバからのレスポンスを待たずに描画するため、
結局JavaScript側でもどうぶつ将棋のルールをほぼ実装するハメになった。

PWAのデプロイ先としては、Googleの
「Firebase」を採用した。

ここでハマったのは、HTTPS通信関連。
Firebaseは自動でHTTPS通信になり、
Monacaからも簡単にデプロイできるクチがあるため
PWAをデプロイする際に便利なのだが、
APIサーバ側を、素のHTTPにしていたので、
PCから実行してOKであっても、
スマホから実行すると、
「せっかくHTTPSなのに途中でHTTPに変えるなよ」的な制約で動かない。
（※スマホ側のデバッグモードが無いためエラー原因を意訳）

APIサーバ側にいわゆる「オレオレ証明書」を入れて
見た目HTTPS化しても事態が改善せず、
「letsencript」を使って本当にHTTPSにした。

letsencript使用時に必要になるため、無料のドメインも取得した。
ここまでやる予定は全くなかったので
予想をはるかに超える手間になってしまった。

接待は改めて大変な仕事である（違


# 実装上のポイント

PWA+Firebaseや、GCP+Flaskは
私の過去記事でも言及しているため、
主にどうぶつしょうぎ完全解析の動かし方と
Flaskのhttps化のノウハウに絞ってポイントを書いておく。

◆Monaca + PWA + Firebase について
https://qiita.com/youwht/items/6c7712bfc7fd088223a2

◆GCP + Flask + 無料ドメイン について
https://qiita.com/youwht/items/9851c2ac9024633fc04e


##  どうぶつしょうぎ完全解析をPythonから動かす方法

前提として、今回は
Windows10に、WSL2(β)でubuntuを導入して動かした。
ちなみに、colaboratory上で動かせることも確認済み。

コードとデータを田中先生のサイトからダウンロードする。

```shell
wget https://dell.tanaka.ecc.u-tokyo.ac.jp/~ktanaka/dobutsushogi/dobutsu-src-20150109.tar.gz
wget https://dell.tanaka.ecc.u-tokyo.ac.jp/~ktanaka/dobutsushogi/dobutsu-dat.tar.gz
```

正しいURLや利用状況については、以下のサイトで確認すること。
https://dell.tanaka.ecc.u-tokyo.ac.jp/~ktanaka/dobutsushogi/

ダウンロードしてきたファイルを展開する

```shell
tar -xzf dobutsu-src-20150109.tar.gz
tar -xzf dobutsu-dat.tar.gz
```

展開できたか以下で確認

```shell
du -sh dobutsu/*
cat "dobutsu/Makefile"
```

以下でmakeする。

```shell
make -f Makefile
```

っとその前に、コレでエラーになる場合、
gccとmakeをインストールする必要がある。
（colaboratoryで実施するならば最初から入っているので不要）

```shell
sudo apt-get install build-essential g++
sudo apt install make
```

棋譜データファイル等を置く場所を作り、棋譜データファイルを作成する。
田中先生のプログラムにおいては、
棋譜データファイルフォーマットが厳格に定義されており、
ちょっとでも棋譜の書き方を誤っているとエラーになるので注意。

```shell
mkdir data
# 初期配置状態の棋譜ファイルサンプルをダウンロードしてくる例
wget -P "data/" "https://www.tanaka.ecc.u-tokyo.ac.jp/ktanaka/dobutsushogi/init.txt"
# リダイレクトで出力して棋譜ファイルを作成する例
echo -e " . -LI . \n-KI-ZO . \n . +ZO+KI\n . +LI . \n100100\n-" > "data/B3B2HI-C4C3KI.txt"
```

上記のように、ダウンロードまたはechoで作成した棋譜ファイルを指定して、
「checkState」というプログラムを実行する。

```shell
./checkState data/init.txt
./checkState data/B3B2HI-C4C3KI.txt
```

これで、初期盤面に対する終局までの完全解析結果が表示されるハズ。すごい。

で、この結果をPythonプログラム側から扱えるようにするために、
以下のようにsubprocessによって、
Pythonからコマンドを叩いて結果を受け取れるようにする。

ここでのポイントは、「checkState」の結果文字列は標準出力ではなく
標準エラー出力側に出ているようなので、それを取得すること。

```py
import subprocess
import os
#checkState本体
def EXEC_checkState(banmen_path):
    # ./checkState data/init.txt
    # 上記コマンドの実行の代わり
    # stderror側の文字列に結果が返ってくるようなので、, stderr=subprocess.STDOUT を追加している
    checkState_result = subprocess.check_output(['./checkState', banmen_path], stderr=subprocess.STDOUT)
    
    #print(type(checkState_result)) ⇒ <class 'bytes'>
    #check_outputの戻り値はバイナリ文字列になってるのでdecodeで文字列化する必要がある
    checkState_result_str = checkState_result.decode('utf8')

    return checkState_result_str
```

このノウハウを使えば、
「接待AI」という謎なテーマに行かなくても、
完全解析の結果に対してPythonでなんらかのデータ分析や、
機械学習を行うことが出来るようになるでしょう。


## Flask + letsencriptがちょっと苦戦した話

まず、証明書を作るためのコマンドをインストールする

```shell
 sudo apt install certbot
```

letsencriptは以下のパスにアクセスしてくることで
ttp://(ドメイン名)/.well-known/acme-challenge/(乱数的なファイル名)
そのサイトの存在を確認しようとしてくる。
この受け皿をFlask上のtemplateとして作る

```py:letsencript関連部分のコード
from flask import Flask, request, jsonify, render_template, redirect

#Flaskの初期化
app = Flask(__name__)

@app.route('/.well-known/acme-challenge/<filename>')
def well_known(filename):
    return render_template('.well-known/acme-challenge/'+ filename)

if __name__ == '__main__':
    ##HTTP通信で起動
    #※letsencriptは主にこちらで動作（※ポート指定なしのデフォルト80番ポートで通信が来る）
    #app.run(host='0.0.0.0', port=80, debug=True)
```

以下のコマンドでFlaskへのアクセスを試してもらう。

```shell
sudo certbot certonly --webroot -w (Flaskの起動しているフォルダの絶対パス)
```

成功すれば、こんな感じのログが出て、.pemファイルが生成される。
エラーが起こる場合は、
ドメインの名前解決、ポートに関する公開設定(GCP側のFW設定）、
Flaskの公開設定、templateフォルダ有無、などがよくある原因か。

```shell
 - Congratulations! Your certificate and chain have been saved at:
   /etc/letsencrypt/live/※※※/fullchain.pem
   Your key file has been saved at:
   /etc/letsencrypt/live/※※※/privkey.pem
```

以下のようにSSLの証明書として生成されたpemファイルを指定した状態で、
FlaskをHTTPS状態で起動すればOK

```py:SSL化関連部分のコード
from flask import Flask, request, jsonify, render_template, redirect
import ssl

app = Flask(__name__)

##SSLの証明書
context = ssl.SSLContext(ssl.PROTOCOL_TLSv1_2)
context.load_cert_chain(
    '※※上記で作成したファイルの置き場所※※/fullchain.pem', 
    '※※上記で作成したファイルの置き場所※※/privkey.pem'
)

if __name__ == '__main__':
    ##HTTPS通信で起動
    #FirebaseはHTTPS前提
    app.run(host='0.0.0.0', port=443, ssl_context=context, debug=True)
```

これでHTTPS通信は出来るようになるが、
letsencrypt側の通信が失敗するようになるため、
証明書の有効期限が切れた時の更新用に、
HTTPでのサーバ立ち上げ用のプログラムも
残しておいたほうがよさそう。
（一方のポートを80番にして、
　両方とも同時に立ち上げておいてもいいかも？）

証明書の自動更新設定とかも未実施だし、
ちょっとイマイチな方法かもしれない。


# 作成した「接待どうぶつしょうぎAI」についての考察

「雑魚」は次の手の勝敗判定以外はランダムという、
王手無視をしない中で最弱クラスのAIであるが、
先手「雑魚」vs後手「接待」の場合、
７割以上「雑魚」が勝つ。
「接待」は上手に負けて差し上げていると思う。

一方で、
先手「最強」vs後手「接待」では、
最強側にランダム性が無いため、
**「接待」が100%勝つ**。

「接待」はヒトコトで言うと、
相手が間違えた手を指した場合にそれより大きく間違える、
という実装で作られているためだ。

「最強」は間違えないために、後手必勝であるこのゲームでは、
「最強」を相手にしたときだけ、「接待」が勝ってしまう。

もちろん途中でワザと負けさせる実装も可能だったが、
最弱よりも弱い「接待」が、
**「最強」だけには勝ってしまう、という方が面白いかなと。**

人間は必ずどこかで間違えるので、「接待」には原則勝つ。

・「最強」は「人間」に勝つ
・「人間」は「雑魚」に勝つ
・「雑魚」は「接待」に勝つ
・「接待」は「最強」に勝つ

この関係が接待AIの斬新性を表していると言えるだろう。

しかし、この接待AIの妙味は「おもてなし」である。
このAIの評価者としては、
**本稿を読まずに使用した「お子様」が最も望ましい**。

~~ってか、本稿のネタを読んだ上で試したら、~~
~~接待要件の一つのの「手抜きを悟られない」に違反している。~~

どうぶつしょうぎのルールは知っているよ、
という人がやってみて、
多少苦戦するような流れも有りつつも、
最後は楽しく勝つことが出来るかどうか？

また、子供でも勝てるAIなのに、
上級者がやってみて、その歯ごたえに驚くかどうか？

作者が実施している限りでは、
ほぼあらゆるレベルの人と互角に戦えて、
ほぼ必ず人側が勝てる、という「接待」を
実現出来た気がするが、皆様にはどうだろうか？

シンプルな考え方の実装（＝最善手乖離度）だけで
このレベルに高度な「接待」を実現することができた。

完全解析済みでない他のゲームにおいても、
評価関数がある程度正しければ、
**同様の方法で「接待AI」が作成可能と見込まれる**。

既に将棋や囲碁のAIは人智を遥かに超えた動きをする。
これらの最強AIを、もっと人を「接待」する方向へも
適用していくべきではないだろうか？


# あとがき

最初に「せったいどうぶつしょうぎ」を
作りたいと思ってから、長い長い月日にわたる脳内検討を経て、
やっと最近このアイデアを思いつき、
ひとつの形になったので嬉しく思う。

最も悩ましい点は、
「接待」という過度に人間的な行為について、
どのような状況を作れば良いとするのか？を、
コンピュータの世界で扱える定義で表現すること、
そしてそれを実現可能な実装方針と繋げること、の二点。
本稿の長文はその試行錯誤の過程を記録するものである。

**「おもてなし」をプログラムで実現する実装案**として、
また、「**お子様への接待**」として、
楽しんでいただければ幸いである。

以上です。
長文お付き合いいただいたかた、ありがとうございました。