ゲーム「2048」をPythonで自作してAIに解かせる(1) ~Webページ上の「2048」をプログラミングで操ってみる~

ゲーム「2048」をPythonで自作し、自作のAIに解かせます。
これ自体はありふれたテーマで、ググれば色々出てくるのですが、僕みたいな初心者向けに書かれた詳しい記事は見つからなかったので、敢えて書いてみることにしました。

この記事では手始めに、（自作ではなく）Webページ上の「2048」をプログラミングで操作してみます。

# 環境
- MacBook Pro (15-inch, 2016)
- OS: macOS High Sierra (10.13.5)
- Python 3.6.4

```
$ sw_vers
ProductName:	Mac OS X
ProductVersion:	10.13.5
BuildVersion:	17F77
$ python --version
Python 3.6.4 :: Anaconda custom (64-bit)
```

# 「2048」とは？
「2048」は最高に面白いパズルゲームです。
2014年に公開されましたが、当時高校生だった僕の周囲では結構流行ってました。
もしご存知なければ、実際にプレイしてみるのが早いと思います。
以下のページで遊ぶことができます[1]（iPhone6とMacBook Proで動作を確認しました）。
https://gabrielecirulli.github.io/2048/
僕みたいに2の累乗が好きな人はハマると思います。
# 「2048」をプログラミングで操ってみよう
「2048」は、「上」「右」「下」「左」の4方向のどれかを受け取り、タイルの位置と得点を出力するシステムと捉えることができます。
そこで、方向を送信するプログラムを作りたくなります。
例えばfor文で「上」「右」「下」「左」を100回送信すれば、

「上」「右」「下」「左」「上」「右」「下」「左」「上」「右」「下」「左」「上」「右」「下」「左」「上」「右」「下」「左」「上」「右」「下」「左」「上」「右」「下」「左」「上」「右」「下」「左」「上」「右」「下」「左」「上」「右」「下」「左」・・・「上」「右」「下」「左」

という操作を一瞬で実行させることができます。（僕ら人間が素手でやったら指が疲れちゃいますね）
知性のカケラもありませんが、ある程度の得点は期待できるでしょう。
というわけで、まずはこれをやってみます。

# Seleniumを使う準備
先ほどご紹介したWeb上の「2048」を遊ぶためには、矢印キーを押す必要がありました。
ここではプログラムに矢印キーを「押させる」ために、Seleniumというライブラリを使用します。
SeleniumはWebブラウザの起動から制御までしてくれる素敵なライブラリです。
terminalで以下を実行して、Seleniumをダウンロードします。

```
$ pip install selenium
```

ただしSeleniumがサポートしているブラウザは限られていて、ここではFirefoxを使用します。Firefoxをお持ちでない方は、ご面倒でしょうがダウンロードをお願いします🙏
https://www.mozilla.org/ja/firefox/new/

さて、FirefoxをSeleniumから制御するためには、geckodriverというドライバーが必要です。
どうやら使用するブラウザごとにそれぞれ別のドライバーが必要なようで、例えばChromeを使う場合はchromedriverというドライバーが必要みたいです。（僕もよくわかってません）

自分のOSに合ったgeckodriverを以下からダウンロードします。
https://github.com/mozilla/geckodriver/releases
この時、geckodriverがどこに保存されたかを覚えておいてください。（自分の分かりやすい場所に移動した方がいいかも？）

お疲れ様でした。
以上で、Seleniumを使う準備が整いました。
# Seleniumで「2048」を起動し、操る
いよいよ、パソコンに「2048」を遊ばせる時がやってきました。
コードを以下の`selenium2048.py`に示します。（参考文献[2]の11章を参考にして書きました。）
`browser = webdriver.Firefox(executable_path="")`の「"　"」に、**geckodriverの場所を書いてから実行してください。**
例えば、hogeさんが`/Users/hoge/`に`geckodriver`というフォルダを作り、そこにgeckodriverを保存した場合は、
`"/Users/hoge/geckodriver/geckodriver"`
と書いてください。

```python:selenium2048.py
# seleniumのインポート
from selenium import webdriver
from selenium.webdriver.common.keys import Keys

# Firefoxの起動と「2048」への移動
browser = webdriver.Firefox(executable_path="/Users/hoge/geckodriver/geckodriver")# ここにgeckodriverの場所を書いてください
browser.get('https://gabrielecirulli.github.io/2048/')#「2048」のページのURL

# 「上右下左」をn回送信する関数
def send_allows(n):
    for i in range(n):
        html_elem.send_keys(Keys.UP) # 「上」を送信
        html_elem.send_keys(Keys.RIGHT) # 「右」を送信
        html_elem.send_keys(Keys.DOWN) # 「下」を送信
        html_elem.send_keys(Keys.LEFT) # 「左」を送信

# 「上右下左」を100回送信
html_elem = browser.find_element_by_tag_name('html')
send_allows(100)
```

`send_allows(n)`関数を変更するだけで、自由自在に「2048」をプレイできます。
さらに別の関数（上上上上・・・とか）を作って順番に実行してみると、より複雑なプレイスタイルが構築できます。

#そしてAIへ・・・
先ほどの`selenium2048.py`ではそこそこの得点は取れるものの、僕ら人間様の足元にも及ばない成績しか取れなかったと思います。
**では、どうして僕ら人間様は高得点が取れるのでしょうか？**
具体的な戦略やテクニック（右下に集めると高得点が取れるよ、とか）についてはググれば無数に出てくるわけですが、それ以前に人間様とパソコンの間には大いなる隔たりがあります。

人間様は
①盤面を見て、
②それぞれの手を実行した結果を計算（想像）して、
③一番良さそうな手を選ぶ
ということをしれっとやっています。さすがですね。

一方のパソコンは
①盤面は見えないし、（「目隠し状態」）
②当然先も読めないし、
③やたらめったら矢印を叩きまくるだけ
というポンコツ仕様です。

そこで、当面の目標は

**盤面を認識して、
ある方向を送信した時の結果（=「1手先」の盤面）を計算して、
それがどれくらい「良い」かを評価し、一番「良い手」を選び続ける
プログラム**

の作成です。

今回はここまで！
次回の記事をお楽しみに！

# 参考文献
[1] 2048 （2048が実際に遊べます。iPhone6とMacBook Proで動作確認しました。2018/8/9にアクセスを確認しました。）
https://gabrielecirulli.github.io/2048/

[2] Al Sweigart 著、相川　愛三 訳(2017)　『退屈なことはPythonにやらせよう ―ノンプログラマーにもできる自動化処理プログラミング』 オライリージャパン

# 言い訳
（2024年追記）
本記事は私が学部生の時に適当に書いた記事です。
今見返すと酷い内容ですが、臥薪嘗胆のために公開しておきます。