ゲームをクリアするAIを作る

#モチベーション
AIにゲームを学習させて、クリアできるとこまでやりたい。
(このプロジェクトは、とてもしょうもない事で滞っています。割とマジで助けてほしい。理由等は後述)
pythonで作る。

#作成するAIの目標
ファミコン用ゲームソフト「スーパーマリオブラザーズ3」の1-1をクリアする。

##大雑把な流れ
ゲーム画面をプログラムから読んで、都度、最適なキー入力を導き出し、それを入力する。

#具体的な開発フロー
しょうもないことに現在0-2で詰んでる。

0.AIが学習できる環境を整える
　0-1.ゲーム画面をプログラムに取り込めるようにする
　0-2.ゲームにプログラムからキー入力を与えることができるようにする
　0-3.教師データの作成
1.学習フェイズ
　1-1.学習レイヤの作成
　1-2.教師データの学習
2.テスト

#0-1.ゲーム画面をプログラムに取り込めるようにする
OpenCVを使って、キャプチャしたいアプリを指定してやる。
実行すると、アプリが開いて同じ画面が2つ表示されることになる。
キー入力があるとプログラムで生成した方のウインドウが消える。
cv2.cvtColor(im,cv2.COLOR_BGR2RGB)は、OpenCVはキャプチャした3チャンネル画像をBGRの順で取得しているので、この1行を入れないと、出力されるウインドウではB成分とR成分が反対になってでてくるのでしんどい。

```python:captureWindow.py
import cv2
import numpy as np
from pywinauto import application
from time import sleep

app = application.Application().start("アプリへのフルパス.exe")
sleep(1)
while(1):
	im=np.asarray(app[u'ウインドウの名前'].CaptureAsImage())
	im=cv2.cvtColor(im,cv2.COLOR_BGR2RGB)
	cv2.imshow("cap",im)
	if cv2.waitKey(1) > 0:
		cv2.destroyAllWindows()
		break
```

#0-2.ゲームにプログラムからキー入力を与えることができるようにする
そもそもどうやってやるのかが解らなかったので、色々ググった結果、
「ウインドウのハンドルを取得して、そこに文字を送信する」とやりたいことができるのかな？

とりあえずテストで、プログラムからメモ帳に文字を入力できるようにする。
そのためにはメモ帳のハンドルが欲しい。ってことで

##ハンドルを調べる
VisualStudio(Community 2015)を開いて、スタートページ->ツール->spy++ x86を開く。
双眼鏡だけのアイコンからファインダーツールの照準みたいなやつをドラッグして、調べたいウインドウの上までもっていってドロップする。今回はメモ帳のテキストボックスに照準を合わせた。

![2018-02-20.png](https://qiita-image-store.s3.amazonaws.com/0/216625/3407f716-6a96-1a7f-9a6d-ed8a06ab08a6.png)
ハンドルは0xd0940だとわかった。この値は毎回変化するので注意。これを使って、プログラムを通してメモ帳に文字を入力する。0xd0940は最後の行に書いてある。

```python:inputMemo.py
# -*- coding: utf-8 -*-
#指定されたハンドルに文字列を送信する
#2018/02/20
import ctypes
import array
 
WM_CHAR = 0x0102
CTRL_C = 3

def enum_child_windows_proc(handle, list):
    list.append(handle)
    return 1
 
if __name__ == "__main__":

    try:
        from msvcrt import getch
    except ImportError:
        def getch():
            import sys
            import tty
            import termios
            fd = sys.stdin.fileno()
            old = termios.tcgetattr(fd)
            try:
                tty.setraw(fd)
                return sys.stdin.read(1)
            finally:
                termios.tcsetattr(fd, termios.TCSADRAIN, old)

    parent_handle = ctypes.windll.user32.FindWindowW(0, "無題 - メモ帳")
    child_handles = array.array("i")
    ENUM_CHILD_WINDOWS = ctypes.WINFUNCTYPE(\
            ctypes.c_int,\
            ctypes.c_int,\
            ctypes.py_object)
    ctypes.windll.user32.EnumChildWindows(\
            parent_handle,\
            ENUM_CHILD_WINDOWS(enum_child_windows_proc),\
            ctypes.py_object(child_handles))
    while 1:
        key = ord(getch())
        if key == CTRL_C:
            break
        else:

            print("input="+hex(key))
            ctypes.windll.user32.SendMessageW(0xd0940, WM_CHAR, key, 0)
```
このプログラムにはハンドルをプログラムで探した形跡も残っている。(ctypes.windll.user32.EnumChildWindowsとかその辺)
入力の最後にエンターキーを押したくはないので、一文字ずつすぐ入力されるようにしてある。

![マイ-ムービー.gif](https://qiita-image-store.s3.amazonaws.com/0/216625/f25632d9-1f3b-24b4-27ed-82ffe723a4bf.gif)

##ここで開発は停止
なんでかっていうと、ファミコンエミュレータ(具体的に言うとfceux)のハンドルをspy++で取得しようとしたら親ハンドル1個しか見つかんねぇの!!プログラムで調べたら子ハンドルが全部で300個ぐらいでてきて、(まぁこの時点で俺的には謎なんだけど)それ全部に文字を送ってみたけど、全部無反応!
??なんで～??

#参考文献
[OpenCVによるUSBカメラ画像の取得](https://qiita.com/vs4sh/items/4a9ce178f1b2fd26ea30)
[OpenCVでデスクトップキャプチャ](http://opencv.blog.jp/python/desktop_capture)
[[Python][Windows] Pythonでスクリーンキャプチャを行う](https://qiita.com/koara-local/items/6a98298d793f22cf2e36)
[tanaka0079/python](https://github.com/tanaka0079/python/blob/python/opencv/desktop_capture.py)
[Pythonで他のアプリケーションを操作する](https://t.co/wPQZSFe55V)
[自作あぷりからAPIで他のあぷりをいじるときのめも。](http://d.hatena.ne.jp/maeyan/20091227/1261848549)