天下一リバーシAI武道会

# はじめに
どうも、y-tetsuです。

以前、自作のリバーシ(オセロ)AIで強豪AIのEdaxさんに挑ませていただいた者です。

https://qiita.com/y-tetsu/items/2d5a199e401aa846891f

私はリバーシAI作りが好きなのですが、実はリバーシAI同士の戦いを眺めるのも好きだったりします。

ということで今回は、Edaxさんに加えて新たな強豪勢として、WZebraさんとEgaroucidさん、そしておまけ枠として自作のゆかいなAI達を交えた、リバーシAI同士が戦う**リバーシの大会**を開いてみたいと思います。

リバーシ好きな人もそうでない人も、是非この大会をご観戦いただき、人間には到底打てないであろう"異次元の手"が飛び交う神々の戦いを、存分に楽しもうではありませんか！

![budokai.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2653390/889bc72d-8751-f82c-9402-c978998553a9.png)

それではただ今より、リバーシAIによる武道の祭典──名付けて「**天下一リバーシAI武道会**」──を開催いたしまーーーす！！！



# ルール
…と、ずいぶん話を大きく膨らませてしまいましたが、さすがに出場者の皆さんに全力を出されてしまうと、武道会館(筆者のPC)が持ちません…。それに、手が凄すぎて人智を超えてしまいます。そこで、ある程度私たち人間にも理解できそうな、以下の範囲で戦っていただきます。

- 先読みの深さは"8手先"までとする
- Book(あらかじめ登録した良い打ち手)は使用しない
- 1つのCPUのみを使用する(相手の手番は何もしない)
- トーナメント方式の1発勝負とする

![dojo.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2653390/ce29cc47-a8e6-653d-65d9-39908f9d57ca.png)

今大会では、**純粋な評価関数**どうしのぶつかり合いを楽しむ事にしましょう。



# 出場者
はい。出場者のみなさん集合して下さーーーーい！

|出場AI名 |参戦スタイル |紹介文 |
|---|---|---|
|Edax      |Console  |言わずと知れた強豪AIの1つ。ただただ強いです。|
|WZebra    |GUI      |こちらも有名な強豪AIの1つ。老舗の名店といったところでしょうか。|
|Egaroucid |Console  |世界一と言われる強豪AIの1つ。最近コンソール版がリリースされていることを知ったため、喜び勇んで召喚させていただきました。|
|Blank8    |自作ライブラリ |筆者自慢のAI。お手合わせいただけるだけで光栄です。わが子のように、暖かく見守りたいと思います。|
|Random    |自作ライブラリ |ランダムに手を選ぶAI。おそらく、リバーシAIを作ったことがある人なら一度は作ったことがあるはず？？ある意味人気No.1。可能性だけなら無限大。|
|Mcs       |自作ライブラリ |Monte Carlo Search(原始モンテカルロ探索)で手を選ぶAI。ランダムを寄せ集めた分だけ、知恵をつけていく妙なやつ。モンテカルロ"木"探索に昇格したい。|
|ChatGPT   |Web API 　　　 |GPT-3.5による会話で手を選ぶAI。プロンプト次第で強くも弱くもなりそう。はたして、実力の程はいかに？流行りに乗って、緊急参戦。|
|Me        |ヒト　　 |Meつまり"私"。折角なので筆者自身も、恥ずかしながら人間代表として参戦させていただきます！|



# トーナメント表
それでは、トーナメント表を発表します！独断と偏見により以下の組み合わせで対戦を進めていただきます。

![tournament.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2653390/d2314e5f-b567-cbde-9c8d-e3cb7fd70533.png)

一体どんな戦いが繰り広げられるのか、そしてだれが優勝するのか、結果が非常に楽しみですね！


# 武道会
## 1回戦 第1試合 : Edax vs Me
Edax選手、堂々の入場です。さすが王者といった風格。対するは人間代表Me選手。どこまで戦えるのか見ものです。

<br>
<br>
<br>
<br>
<br>

![kids_karate.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2653390/57848375-513b-d05a-cfc6-2e043b2d2697.png)

<br>
<br>
<br>
<br>
<br>

### 結果
![Edax_vs_Me.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2653390/fd3df310-e9d6-3db0-3b73-335563294366.png)

あぁ……Me選手、まったく歯が立たちませんでした！覚悟はしていたのでしょうが、最後は一方的な展開でした。Edax選手は悠々と準決勝進出です。Me選手は極めて素人ではありますが、ここまでやられてしまうとは。はたして、Edax選手に勝てる人間はいるのでしょうか？

### 棋譜
```
D3c3C4e3D2c2E2c6F3d1C5b4A3b6B5b3F5f4A6a5A4e1G4g6F2g5F1g1F6h4G3h3E6d7E7d8A7d6A2b2C7c8E8f8F7g8A1b7A8b1B8H8C1H1H6h5H7g7G2H2
```

<br>
<br>
<br>
<br>
<br>

## 1回戦 第2試合 : Random vs Mcs
Random選手、すでに試合は始まっていますが、客席の方に気を取られているようです。相変わらず何を考えているのか全くわかりません。一方のMcs選手はプレイアウト数100で挑むようです。まずは小手調べ、といったところでしょうか。

<br>
<br>
<br>
<br>
<br>

![kids_karate.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2653390/57848375-513b-d05a-cfc6-2e043b2d2697.png)

<br>
<br>
<br>
<br>
<br>

### 結果
![Random_vs_Mcs.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2653390/4a5b2a40-8cc8-cd81-bc2c-0512498f82b5.png)

予想通り、Mcs選手の勝利でした！！しかし、ここまで差が開くとは思いませんでした。

### 棋譜
```
D3c3B3b2C4e3E2b4A2e1C2a1F4c5B5a3F1d1F3d2C6f2F5c7A4b6A7a6C1b1G2h1D6a5G4e7B7e6H2g1D7h3F6h4H5g3G5h6G6f7F8a8G7h7D8c8B8e8G8h8
```

<br>
<br>
<br>
<br>
<br>

## 1回戦 第3試合 : Egaroucid vs ChatGPT
世界一と言われている大本命、Egaroucid選手の登場です。そしてそれを迎え撃つは、急遽参戦いただいた、今話題のChatGPT選手です。

リバーシはご専門ではないChatGPT選手ですが、この大会に興味を持っていただけたため、今回の試合が実現しました。事前に大会への意気込みも含め、伺っております。その時の模様をどうぞ。

![interview_chatgpt.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2653390/d581657d-347f-14c1-4e85-e280b397f27b.png)

リバーシに特化したAIとの対戦に、既に備えられていたんですね！さすがです。並々ならぬ気迫を感じます。対照的に、Egaroucid選手は物静かな様子。懐の深さを感じさせますね。非常に注目の好カードです。

<br>
<br>
<br>
<br>
<br>

![kids_karate.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2653390/57848375-513b-d05a-cfc6-2e043b2d2697.png)

<br>
<br>
<br>
<br>
<br>

### 結果
![Egaroucid_vs_ChatGPT.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2653390/b2d20e16-0050-d6f1-703e-b06238e0ecf6.png)

Egaroucid選手、強い！…と言うべきか、ChatGPT選手、残念！と言うべきか…。

今回はChatGPT選手、GPT-3.5の無料制限枠という事で、**20秒に1手**とかなりの長考派仕様でした。しかも、大会運営側で都度補正しておりましたが、何度かルール上打てない手を打とうとされていましたね…。まだこの大会には早すぎたのかもしれません。GPT-4を越えて5や6に成長したら、また参戦をお願いします！

ちなみに、ChatGPT選手とはWeb APIを通じて会話により手を決めてもらっておりました。その時の会話の内容を一部ご覧ください。
![chatgpt_move2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2653390/29c6cc8f-79d3-a2f3-05a3-6833847ef439.png)

Egaroucid選手の実力は、準決勝で確かめることにしましょう。

### 棋譜
```
E6f4D3c2F3f5F6g2E3c6D6e2E1d1C1f7D2c3B1c4B6b2E7a6B4b3A1d7A4a2A3a5C5b5H1g3F2f1G4h2H3h4H5g5H6g6H7g7G8e8H8G1F8D8C7A7b7B8
```

<br>
<br>
<br>
<br>
<br>

## 1回戦 第4試合 : WZebra vs Blank8
WZebra選手は1997年に開発された、知る人も多い大御所です。Blank8選手はWZebra選手を、勝手ながら師匠と慕い、「[強いオセロプログラムの内部動作](http://www.amy.hi-ho.ne.jp/okuhara/howtoj.htm)」から数々の技を盗み成長してきたようです。4手読みなら勝てる自信があるようですが、今回の8手読み対決ではどうなることでしょう。

<br>
<br>
<br>
<br>
<br>

![kids_karate.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2653390/57848375-513b-d05a-cfc6-2e043b2d2697.png)

<br>
<br>
<br>
<br>
<br>

### 結果
![Wzebra_vs_Blank8.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2653390/25bd17b8-9c7b-07a4-9536-be18921a8eb9.png)

中盤までは戦えていたように思いますが、やはり力の差が出てしまったか。Blank8選手、頑張りましたが敗退です。

WZebra選手はGUIでの参戦ということで、今回運営側が`pyautogui`などを活用し、武道会場とGUIをいわゆる**RPA**で連動させ、対決を実現させました。折角なので、その模様を少しどうぞ。

![wzebra.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2653390/1fba1733-a305-aa4c-7017-9ac06a39e65a.gif)

WZebra選手のターンでは、主に以下を実行しています。
- GUI（左側の盤面の方）を操作し、黒番か白番かを設定する
- 盤面を一旦、新規にクリアする
- 初手からの棋譜を入力し、現在の盤面を設定する
- 「Start game!」ボタンを押して1手打ってもらう
- 棋譜欄をクリップボード経由で取得し、WZebra選手が打った手を大会側へ伝える

### 棋譜
```
D3c5D6e3F4c6F5c3C4b5F2e6E7f7C7f8D2f6D7c8B3e8A6g4G5g3F3h4B6e2F1g6G7b4A5c1E1d1C2g1H3b1H5h8H6h7G2h1H2b2G8a3A1a2A4b7A8b8D8a7
```

<br>
<br>
<br>
<br>
<br>

## 準決勝 第1試合 : Edax vs Mcs-Ⅱ
準決勝第1試合が始まりました。ここからは実力者同士の戦いとなります。

試合開始早々、Mcs選手、おもむろに上着を脱ぎ捨てました。ドスッ、と物凄くいかにもな音が響きました。重いやつを着て、鍛えてたパターンでしょうか。身軽になり、プレイアウト数を10倍の1000まで引き上げました。Edax選手を前に、本気の構えといったところです。

<br>
<br>
<br>
<br>
<br>

![kids_karate.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2653390/57848375-513b-d05a-cfc6-2e043b2d2697.png)

<br>
<br>
<br>
<br>
<br>

### 結果
![Edax_vs_Mcs-Ⅱ.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2653390/cd1ab5ac-fb4c-722f-874e-fc41462f5fa9.png)

蓋を開けてみれば、何でもなかったようです。前評判通り、Edax選手の圧勝です。本気を出したMcs選手をものともせず、完全に封じ込めました！やはり王者！石数を増やそうともせず、最短で相手を仕留める剛腕を見せつけました。これはMcs選手つらい…。

### 棋譜
```
D3e3F4g3G4c3F5g6F6h4H6g7H3c6H5g5F7f8G2h7H8g1E6e7G8E8D8D7F3h2H1f2D6F1E2B7C5B3
```

<br>
<br>
<br>
<br>
<br>

## 準決勝 第2試合 : Egaroucid vs WZebra
準決勝の第2試合目、遂に大物同士の対決が始まりました。

WZebra選手はGUI参戦のせいであまり伝わらないのですが、とにかくスピードの速さが特徴です。ビットボードとアセンブラを駆使したその打ち手はもはや神速。対するEgaroucid選手は、本大会の仕組み上、致し方ない部分ではありますが、毎手モデルの読み込みに時間がかかる様子。立ち上がりの遅さが気にかかります。スピードよりもパワー重視といったところでしょうか。

大注目のカードです、1手たりとも見逃せません！

<br>
<br>
<br>
<br>
<br>

![kids_karate.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2653390/57848375-513b-d05a-cfc6-2e043b2d2697.png)

<br>
<br>
<br>
<br>
<br>

### 結果
![Egaroucid_vs_WZebra.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2653390/f146aa5c-27a6-04f9-a0b9-da79a1771ddc.png)

ものすごい戦いでした。

途中まで、素人目には端っこを取られていて、Egaroucid選手やや不利か？とも思えましたが、最後にEgaroucid選手が大逆転！！パワーがスピードを制しました。

(中盤)
![Egaroucid_vs_WZebra_mid.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2653390/7d1b9489-11fb-b4fc-daea-7f3db338bae3.png)

(ラスト手前)
![Egaroucid_vs_WZebra_last.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2653390/56fb6e26-2189-7618-f815-f0343a5740e7.png)

Egaroucid選手、ラスト一手で勝負をひっくり返し決勝進出です！！これぞ、リバーシの醍醐味といった試合でした。

### 棋譜
```
E6f6F5f4G5e7F7d6E8h6E3f3D3g6G4h3G3h2C6c5D7g8F8d8G7c8C7b5H4h5B4a3B6a6B7f2C4a8F1c2C3d2B2e2D1b3C1e1A2b8A7a5A4h7H8b1A1G1g2H1
```

<br>
<br>
<br>
<br>
<br>

## 3位決定戦 : Mcs-Ⅲ vs WZebra
気になる決勝戦の前に、先に3位決定戦を始めます。共に準決勝で敗れた、Mcs選手とWZebra選手の対戦です。

…ん？…どうしたことでしょう、Mcs選手、少し様子がおかしいですね。Edax選手に受けたダメージが相当効いているのでしょうか、小刻みに震えております。…えっ！？、急激に体が風船のように膨らみ始めました！プレイアウトの数値もいつの間にか倍の2000まで上昇しています。

…そしてなんと！？、近くにいたRandom選手とBlank8選手を飲み込んでしまいました！……その影響でしょうか、序盤の定石打ちが追加されています！そ、そしてさらに最終16手の完全読みまでっ！！…これはまずいですね、ルールを無視した反則行為です。このまま自爆して会場もろとも吹き飛ばすパターンでしょうか！？

WZebra選手、どうかMcs選手の暴走を止めて下さいっ！！！

<br>
<br>
<br>
<br>
<br>

![kids_karate.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2653390/57848375-513b-d05a-cfc6-2e043b2d2697.png)

<br>
<br>
<br>
<br>
<br>

### 結果
![Mcs-Ⅲ_vs_WZebra.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2653390/4c521cc2-0895-36c1-86c8-06e7c6d0c1b0.png)

やはりWZebra選手は本物でした。暴走したMcs選手を前に、落ち着いた様子で的確にさばきました。

ありがとう、WZebra選手！無事に、Random選手とBlank8選手も戻ってきました。Mcs選手も我に返ったようで、今は静かにうつむいております。さすがにもうこれ以上、抵抗する気はないでしょう。

まだ、場内騒然としておりますが決勝戦の前に、ここで一旦会場の整備の時間をいただきます。

### 棋譜
```
F5f6E6f4E3c5C4d6G6g4B6b5A6f3G3d3E2c3B3f2C6b4F1h3H4h5A3h2G2g5d1E1h1G1d2C2c1B2h6B1a1a2g7G8a4E7c7D8a5B7a7D7h8a8f8F7h7b8c8e8
```

<br>
<br>
<br>
<br>
<br>

![break.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2653390/76f3a759-dac8-c78d-4af6-8bca6281b100.png)

<br>
<br>
<br>
<br>
<br>

## 決勝戦 : Edax vs Egaroucid
…今ようやく、会場の全ての点検が終わり、決勝戦の準備が整いました。まもなく、天下一を決める世紀の一戦が始まろうとしています。

Edax選手とEgaroucid選手による、最強vs最強のぶつかり合い。名勝負の予感しかしません。

試合が始まるまでに、両者の情報を少しお伝えさせていただきます。

Edax選手の評価関数は「[もしオセロの初心者がEdaxの評価関数を読んだら](https://qiita.com/tanaka-a/items/6d6725d5866ebe85fb0b)」によると、盤面を47個ものパターンに分解し、形勢を判断しているそうです。

また、対するEgaroucid選手は、「[オセロAI世界1位になってオセロAIをｶﾝｾﾞﾝﾆﾘｶｲｼﾀ話](https://qiita.com/Nyanyan_Cube/items/195bdc47bb1d7c6f8b24)」によると、43万局、2200万局面ものデータを使い最急降下法による膨大なパラメータの調整の末、Edax選手を越える評価関数を見出した、とのことです。

互いに、学習にかけた時間は計り知れませんね。

さて、いよいよ時間になりました。歴史的な試合を、固唾を呑んで見守りましょう。

それでは決勝戦、はじめっ！！

<br>
<br>
<br>
<br>
<br>

![kids_karate.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2653390/57848375-513b-d05a-cfc6-2e043b2d2697.png)

<br>
<br>
<br>
<br>
<br>

### 結果
![Edax_vs_Egaroucid.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2653390/7e45df47-2593-7925-1185-b1e2fb00150d.png)

ついに、遂に天下一が決定しました。勝ったのは、Egaroucid選手。終始表情を変えませんでしたが、ここにきてようやく安堵の表情。

いつもは勝気なEdax選手ですが、今は心から「Egaroucid…お前がナンバーワンだ！！」と言っているかのようです。

互いに死力を尽くした良い戦いでした。素人目には何がなんだか…。やはり神の領域！！！

それでは試合の模様をじっくり振り返ってみましょう。

(立ち上がりの様子)
![Edax_vs_Egaroucid_1.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2653390/e88dc8a0-75ae-f378-5715-f78b91bd8864.gif)

(序盤)
![Edax_vs_Egaroucid_2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2653390/1c7f55d1-7e5b-b812-d35a-75b06baa3e59.png)

(中盤)
![Edax_vs_Egaroucid_3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2653390/ead72e7d-76af-84ee-09b7-1294f3a12bf2.png)

(終盤)
![Edax_vs_Egaroucid_4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2653390/cb582f0f-c3b0-999a-21f3-01a5d4672ac0.png)

(ラスト1手の様子)
![Edax_vs_Egaroucid_5.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2653390/d175c6cc-027a-e518-f1b7-68b521c494e5.gif)

Edax選手、Egaroucid選手、素晴らしい戦いを本当にありがとうございました！！


### 棋譜
```
D3c5E6f5F6e3C3f3C4b4C6d2F4b5E2g4G5f1F2g3A3h6B3g6D1c2C1e1A4f7G1e7H3d6C7d7D8b6A5e8C8h4H5b2H7b8G8a7B1a1A2a6F8h8G7h2G2h1B7a8
```

<br>
<br>
<br>
<br>
<br>

# 表彰式
出場選手のみなさん、大変お疲れ様でした。数々の激闘の末、本大会の順位が決定しました。これから表彰式を執り行います。

![announcer.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2653390/f4e96e93-dfea-2451-d2c7-9d575a38c0ee.png)

第一回、天下一リバーシAI武道会の優勝者は「Egaroucid」選手です！！

おめでとうございます！！！！！

## 1位 : Egaroucid
![gold.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2653390/89c08cf3-b66c-1ac1-abca-411799b06106.png)

## 2位 : Edax
![silver.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2653390/33699d6e-c38e-85e1-cc01-2f10e494c23a.png)

## 3位 : WZebra
![bronze.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2653390/151a9ff5-ee96-e462-75d0-78cd241c046c.png)

## 最終結果
![result.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2653390/a19e0214-cf61-26b1-e299-15cdff02b971.png)

:::note warn
勝手に順位を付けてしまってすみません。あくまでも私の環境で、たまたま一度戦って出た結果です。また必ずしも対等な条件になっているとは限りません。**本結果はAI本来の強さを示すものではございません。**
:::


# 武道会の設営方法
今回の記事で使用した動作環境を残しておきます。

## 武道会館
必要なPCのスペックは以下です。
- Windows10 64bit
- ディスプレイサイズ 1366x768 以上
- プロセッサ 1.6GHz 以上
- メモリ 4.00GB 以上

## レフェリー
対戦の立ち合い人として、拙作のリバーシライブラリ**reversi**をインストールします。

https://github.com/y-tetsu/reversi

事前に[Python 3.7.6](https://www.python.org/downloads/release/python-376/)をインストールし、以下のコマンドを実行して下さい。
```
$ py -3.7 -m pip install git+https://github.com/y-tetsu/reversi
```

Python3.7以降でも問題なく動くと思いますが、その際はCythonのコンパイルの関係で、[Microsoft Visual C++](https://visualstudio.microsoft.com/ja/downloads/?utm_medium=microsoft&utm_source=docs.microsoft.com&utm_campaign=button+cta&utm_content=download+vs2019+rc)のインストールを併せて実施願います。

## 出場選手

## Edax
### ダウンロード
以下のサイトより、コンソール用のアプリをダウンロードして下さい。

https://github.com/abulmo/edax-reversi/releases

![edax_download.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2653390/7b006e7d-25cf-465f-31fa-e4f12ef832e6.png)

上図の下線で示したファイルをダウンロードして下さい。

以下でも紹介しています。

https://qiita.com/y-tetsu/items/2d5a199e401aa846891f


### AI実行用コード
```edax.py
import subprocess

from reversi import strategies
from reversi.strategies import AbstractStrategy
from reversi.move import Move as m


class Edax(AbstractStrategy):
    def next_move(self, color, board):
        if board.size != 8:
            return strategies.Random.get_next_move(color, board)

        with open('./board.txt', 'w') as f:
            f.write(board.get_board_line_info(color))

        if board._black_score + board._white_score < 56:
            cmd = "./edax-4.4 -solve ./board.txt -l 8 -book-usage off -cpu"
        else:
            cmd = "./edax-4.4 -solve ./board.txt -cpu"

        output_str = subprocess.run(cmd, capture_output=True, text=True).stdout
        move = output_str.split('\n')[2][57:].split()[0]

        return m(move)
```


## WZebra
以下のサイトより、WZebra(Windows版GUIアプリケーション)をインストールして下さい。

http://radagast.se/othello/

![wzebra_install.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2653390/ab1acd6f-b647-1491-6baf-60e6a183c618.png)

上図の下線で示したインストーラに従いインストールして下さい。

::: note warn
WZebraのアプリケーションは武道会実施前にあらかじめ起動しておいて下さい。また、Bookの使用有無や読み手数についても、事前に手動で設定しておいて下さい。
:::

### Pythonライブラリのインストール
WZebraの操作用に、`pyautogui`と`pywin32`をインストールして下さい。また、クリップボードの操作のため`pyperclip`をインストールして下さい。
```
py -3.7 -m pip install pyautogui
py -3.7 -m pip install pywin32
py -3.7 -m pip install pyperclip
```

### AI実行用コード
``` wzebra.py
import subprocess
import time

from reversi import strategies, Recorder
from reversi import C as c
from reversi.strategies import AbstractStrategy
from reversi.move import Move as m

import pyautogui
import win32gui
from ctypes import *
import pyperclip


class WZebra(AbstractStrategy):
    pass_cnt = 0
    def next_move(self, color, board):
        if board.size != 8:
            return strategies.Random.get_next_move(color, board)

        print("\n[WZebra's turn]")

        # WZebraを前面に
        self.forground('WZebra')

        # ウィンドウサイズを正規化
        self.fix_active_window()

        # WZebraの手番を設定
        print(' - set color')
        if color == c.black:
            self.set_wzebra_black()
        else:
            self.set_wzebra_white()

        # 新規盤面を設定
        print(' - set newgame')
        self.set_newgame()

        # 棋譜を入力
        print(' - set record')
        record = str(Recorder(board))
        self.set_record(record)

        # WZebraの打ち手を取得
        print(' - get move')
        self.start_game()
        move = self.get_move(color)
        print(' - put', move)

        # パス回数クリア
        self.pass_cnt = 0

        return m(move)

    def forground(self, title):
        FindWindowEx = windll.user32.FindWindowExW
        SetForegroundWindow = windll.user32.SetForegroundWindow
        hwnd = FindWindowEx(None, None, None, title)
        if hwnd != 0:
            SetForegroundWindow(hwnd)
            return True
        return False

    def fix_active_window(self):
        hwnd = win32gui.GetForegroundWindow()
        win32gui.MoveWindow(hwnd, 0, 0, 500, 500, True)

    def set_wzebra_black(self):
        pyautogui.moveTo(150, 35)
        pyautogui.click()
        pyautogui.moveTo(150, 60)
        time.sleep(0.5)
        pyautogui.moveTo(350, 80)
        pyautogui.click()

    def set_wzebra_white(self):
        pyautogui.moveTo(150, 35)
        pyautogui.click()
        pyautogui.moveTo(150, 60)
        time.sleep(0.5)
        pyautogui.moveTo(350, 105)
        pyautogui.click()

    def set_newgame(self):
        pyautogui.moveTo(85, 35)
        pyautogui.click()
        pyautogui.moveTo(120, 60)
        pyautogui.click()
        time.sleep(0.5)

    def set_record(self, record):
        # メニューから棋譜を入力を選ぶ
        pyautogui.moveTo(35, 35)
        pyautogui.click()
        pyautogui.moveTo(100, 155)
        pyautogui.click()

        # 棋譜入力ウィンドウを正規化
        self.fix_active_window()

        # 棋譜を設定
        pyautogui.press("backspace")
        pyautogui.write(record)
        time.sleep(1)

        # OKを押す
        pyautogui.moveTo(200, 465)
        pyautogui.click()
        time.sleep(2)

        # パスの場合OKを押す
        self.check_pass()
        if self.pass_cnt > 0:
            self.pass_cnt -= 1  # 手を取得するための調整

        # 対局終了の場合OKを押す
        self.check_end()

    def check_pass(self):
        while self.forground('WZebra: 情報'):
            self.fix_active_window()
            pyautogui.moveTo(70, 125)
            pyautogui.click()
            self.pass_cnt += 1
            time.sleep(1)

    def check_end(self):
        self.forground('WZebra: 対局終了')
        self.fix_active_window()
        pyautogui.moveTo(150, 125)
        pyautogui.click()
        pyautogui.moveTo(250, 125)
        pyautogui.click()

    def get_move(self, color):
        # 棋譜欄を右クリックしてクリップボードにコピー
        pyautogui.click(385, 220, 1, 0, 'right')
        pyautogui.moveTo(420, 230)
        pyautogui.click()

        # 最終手をWZebraの打ち手とする
        moves = pyperclip.paste().split('\n')
        if color == c.black and self.pass_cnt:
            move = moves[-2-self.pass_cnt].split()[-3]
        else:
            move = moves[-2-self.pass_cnt].split()[-1]
        return move

    def start_game(self):
        # Start game!を押す
        pyautogui.moveTo(380, 390)
        pyautogui.click()
        time.sleep(2)

        # パスの場合OKを押す
        self.check_pass()

        # 対局終了の場合OKを押す
        self.check_end()
```

::: note warn
WZebra処理中はマウスカーソルを触らないでください。もし誤って操作してしまい、自動操作の挙動がおかしくなった場合は、マウスカーソルを画面の左上に移動させてください。処理を**緊急停止**させることができます。
:::


## Egaroucid
### ダウンロード
以下のサイトより、コンソール用のアプリをダウンロードして下さい。

https://www.egaroucid.nyanyan.dev/ja/console/

![egaroucid_download.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2653390/51741de4-3449-17c1-9efe-983253d3cb0d.png)

上図の下線で示したファイルをダウンロードして下さい。(本記事では都合により、Generic版を使用しましたが、お使いのPCが対応していればSIMD版で問題ないと思います)


### AI実行用コード
``` egaroucid.py
import subprocess

from reversi import strategies
from reversi.strategies import AbstractStrategy
from reversi.move import Move as m


class Egaroucid(AbstractStrategy):
    def next_move(self, color, board):
        if board.size != 8:
            return strategies.Random.get_next_move(color, board)

        with open('./board.txt', 'w') as f:
            f.write(board.get_board_line_info(color))

        if board._black_score + board._white_score < 56:
            cmd = "./Egaroucid_for_Console.exe -solve ./board.txt -l 8 -nobook -t 1"
        else:
            cmd = "./Egaroucid_for_Console.exe -solve ./board.txt -t 1"

        output_str = subprocess.run(cmd, capture_output=True, text=True).stdout
        move = output_str.split('\n')[1][46:46+2]

        return m(move)
```

## ChatGPT
### openaiのインストールとAPIキーの取得
以下の記事を参考にして、`openai`ライブラリをインストールし、APIキーを取得しました。

https://qiita.com/Nikto/items/bebba9bd847b6ec8254d

### AI実行用コード
```chatgpt.py
import subprocess
import time

from reversi import strategies
from reversi.strategies import AbstractStrategy
from reversi.move import Move as m
from reversi import C as c

import openai


API_KEY = "あなたのAPIキー"


class ChatGPT(AbstractStrategy):
    def next_move(self, color, board):
        if board.size != 8:
            return strategies.Random.get_next_move(color, board)

        time.sleep(21)

        # 打ち手を決めるプロンプトを用意
        turn = '黒' if color == c.black else '白'
        moves = [str(m(*i)) for i in board.get_legal_moves(color)]
        str_moves = '\n'.join(moves)
        content = f"""あなたは最強のリバーシプレイヤーです。
現在のゲームの状況を以下に示します。
着手可能な手の中から、最も勝てそうな手を1つ選んで下さい。
ただし、回答は着手可能な手のみを答えて下さい。それ以外の説明は一切不要です。

# あなたの手番
{turn}です

# 盤面
8x8=64マスの状態を以下に示します。Oは白、*は黒、-は空きを示しています。
{board.get_board_line_info('black')[:64]}

#着手可能な手
{str_moves}
"""

        # ChatGPTの手を取得
        openai.api_key = API_KEY
        response = openai.ChatCompletion.create(
            model='gpt-3.5-turbo',
            messages=[
                {'role': 'user', 'content': content}
            ],
            temperature=0.0,
        )
        gpt_move = response['choices'][0]['message']['content']

        print(board)
        print('turn  :', turn)
        print('moves :', moves)
        print('gpt   :', gpt_move)

        return m(gpt_move) if gpt_move in moves else m(moves[0])
```

:::note warn
ChatGPTのAPI利用は従量課金制となっています。トライアル期間中の無料枠のトークン（会話に使った単語）の消費量が制限を越えると、料金が発生するようです。お試しされる場合はくれぐれもご注意下さい。
:::


## 武道会
```budokai.py
from reversi import Reversi
from reversi.strategies import WindowUserInput as Me
from reversi.strategies import Random
from reversi.strategies import MonteCarlo as Mcs
from reversi.strategies import MonteCarlo_EndGame as McsEnd
from reversi.strategies import _Tora_ as ToraJoseki
from reversi.strategies import _Blank_ as Blank

from edax import Edax
from wzebra import WZebra
from egaroucid import Egaroucid
from chatgpt import ChatGPT


if __name__ == '__main__':
    players = {
        # ↓↓↓↓↓ Add players here ↓↓↓↓↓
        'Edax': Edax(),
        'WZebra': WZebra(),
        'Egaroucid': Egaroucid(),
        'Blank8': Blank(depth=8),
        'Random': Random(),
        'Mcs': Mcs(100),
        'Mcs-Ⅱ': Mcs(1000),
        'Mcs-Ⅲ': ToraJoseki(McsEnd(2000, 16)),
        'ChatGPT': ChatGPT(),
        'Me': None,
        # ↑↑↑↑↑ Add players here ↑↑↑↑↑
    }
    app = Reversi(players)
    players['Me'] = Me(app.window)
    app.start()
```

## 配置
これまでに、ダウンロードしたファイルや、用意したPythonコードは以下の配置として下さい。
```
C:.
└─budokai
    │  budokai.py                … AI対戦を観戦できるコード
    │  edax-4.4                  … Edax本体
    │  edax.py                   … Edax本体を扱うためのコード
    │  wzebra.py                 … WZebra本体を扱うためのコード
    │  Egaroucid_for_Console.exe … Egaroucid本体
    │  egaroucid.py              … Egaroucid本体を扱うためのコード
    │  chatgpt.py                … ChatGPTを扱うためのコード
    │
    ├─data                      … Edaxの評価値とBook
    │      book.dat
    │      eval.dat
    │
    └─resources                 … Egaroucidの評価値とBook
        │  book.egbk
        │  empty_book.egbk
        │  eval.egev
        │
        └─hash
                hash23.eghs
                hash24.eghs
                hash25.eghs
                hash26.eghs
                hash27.eghs
```

## 開始方法
以下でアプリケーションが起動します。
```
py -3.7 budokai.py
```

お好きな対戦AIを選んで開始して下さい。対戦が終わると、カレントフォルダに棋譜を記載したテキストファイルが作成されます。

今回紹介した組み合わせ以外の対戦についても、もしご興味があれば是非お試し下さい！



# おわりに
強豪リバーシAIやおまけAI達による武道の祭典、**天下一リバーシAI武道会**はいかがでしたでしょうか。皆さんの予想は的中しましたか？

大会を振り返って筆者自身としましては、自分や自分のAIがもっと強かったら良いのにっ！！とか、強豪どれ一つにも勝てやしねぇ！！といった、悔しい思いもあったりします。ですが、やはりスター達の戦いはすごかった。心から憧れます。

リバーシAIの作り方を少しずつ理解していくと、強豪AI達の処理速度や探索効率、評価関数の精度など、強さの裏に潜む「そこまでやるかっ！！」という、血のにじむような努力が見え隠れしはじめます。私もすべてを知るわけではありませんが、とても深い世界が広がっているのを感じます。

本記事は、そのような強豪AIの表向きのすごさだけでもうまく伝えられたらなぁ、と思い書かせていただきました。一人でも多くの人がリバーシAI作りに興味を持っていただけたら、私にとってこの上ない喜びとなります。

それでは皆様、よきリバーシ・ライフを！