東方projectSTGゲームのAIを作るときの便利なやつまとめ

# TL;DR
まとめただけなのでこの記事にあまり価値はないです
ctypes大事

# 環境
```
OS:Windows 10
Python:3.9.0
```
# スクショ取得
pillowを使います

```python
from PIL import ImageGrab

def get_screenshot():
    region=get_region() #こちらは各自で実装してください
    img=ImageGrab.grab(region)
    return region

```

# キー入力

pyautoguiを使うを失敗します（原因わからん）
なのでctypesを利用して直接win32apiをたたきます。
そこでこの記事

https://zhuanlan.zhihu.com/p/344924619

(日本語英語以外を毛嫌いしてたら一生見なかったかも)
コード抜粋、変更

```python
import ctypes

PUL = ctypes.POINTER(ctypes.c_ulong)

key = {"up":0x48, "down":0x50, "left":0x4D, "right":0x4B,"z":0x2C,"Enter":0x1C,"Shift":54}
class KeyBdInput(ctypes.Structure):
    _fields_ = [("wVk", ctypes.c_ushort),
                ("wScan", ctypes.c_ushort),
                ("dwFlags", ctypes.c_ulong),
                ("time", ctypes.c_ulong),
                ("dwExtraInfo", PUL)]

class Input_I(ctypes.Union):
    _fields_ = [("ki", KeyBdInput),
                 ("mi", MouseInput),
                 ("hi", HardwareInput)]


class Input(ctypes.Structure):
    _fields_ = [("type", ctypes.c_ulong),
                ("ii", Input_I)]

# Actuals Functions
def PressKey(hexKeyCode):
    extra = ctypes.c_ulong(0)
    ii_ = Input_I()
    ii_.ki = KeyBdInput(0, hexKeyCode, 0x0008, 0, ctypes.pointer(extra))
    x = Input(ctypes.c_ulong(1), ii_)
    ctypes.windll.user32.SendInput(1, ctypes.pointer(x), ctypes.sizeof(x))


def ReleaseKey(hexKeyCode):
    extra = ctypes.c_ulong(0)
    ii_ = Input_I()
    ii_.ki = KeyBdInput( 0, hexKeyCode, 0x0008 | 0x0002, 0, ctypes.pointer(extra) )
    x = Input(ctypes.c_ulong(1), ii_)
    ctypes.windll.user32.SendInput(1, ctypes.pointer(x), ctypes.sizeof(x))
```

# プロセスメモリ参照

https://digitaltravesia.jp/usamimihurricane/webhelp/index.html?page=_RESOURCE%2FUsaMimiHurricane%2Fdistribute.html

うさみみハリケーンで実験を行った後にReadProcessMemory関数でメモリ参照を行います。
ただ正直よくわからなかったので下の記事を参考に必要部分のみをコードに書き起こしました。

https://qiita.com/deta-mamoru/items/31fd5b5ddef2a74376d4

以下抜粋コード(コピペばかりでごめんなさい)



```python:defines.py
from ctypes import *

# types
DWORD     = c_ulong
PVOID     = c_void_p
ULONG_PTR = c_ulong
LONG      = c_long
# constant
INFINITE                  = 0xFFFFFFFF
DBG_CONTINUE              = 0x00010002
DBG_EXCEPTION_NOT_HANDLED = 0x80010001
PROCESS_ALL_ACCESS= ( 0x000F0000 ) | ( 0x00100000 ) |0xFFFF  #ここだけ追加
class EXCEPTION_RECORD(Structure):
    pass

EXCEPTION_RECORD._fields_ = [
    ("ExceptionCode",        DWORD),
    ("ExceptionFlags",       DWORD),
    ("ExceptionRecord",      POINTER(EXCEPTION_RECORD)),
    ("ExceptionAddress",     PVOID),
    ("NumberParameters",     DWORD),
    ("ExceptionInformation", ULONG_PTR*15),
]

class EXCEPTION_RECORD(Structure):
    _fields_ = [
        ("ExceptionCode",        DWORD),
        ("ExceptionFlags",       DWORD),
        ("ExceptionRecord",      POINTER(EXCEPTION_RECORD)),
        ("ExceptionAddress",     PVOID),
        ("NumberParameters",     DWORD),
        ("ExceptionInformation", ULONG_PTR*15),
    ]

class EXCEPTION_DEBUG_INFO(Structure):
    _fields_ = [
        ("ExceptionRecord", EXCEPTION_RECORD),
        ("dwFirstCance",    DWORD),
    ]

class U(Union):
    _fields_ = [
        ("Exception", EXCEPTION_DEBUG_INFO),
    ]

class DEBUG_EVENT(Structure):
    _fields_ = [
        ("dwDebugEventCode", DWORD),
        ("dwProcessId",      DWORD),
        ("dwThreadId",       DWORD),
        ("u",                U),
    ]

class LUID(Structure):
    _fields_ = [
        ("LowPart",  DWORD),
        ("HighPart", LONG),
    ]

class LUID_AND_ATTRIBUTES(Structure):
    _fields_ = [
        ("Luid",       LUID),
        ("Attributes", DWORD),
    ]

class TOKEN_PRIVILEGES(Structure):
    _fields_ = [
        ("PrivilegeCount", DWORD),
        ("Privileges",     LUID_AND_ATTRIBUTES),
    ]
```

```python
from ctypes          import *
from defines         import *
#from privilege       import set_debug_privilege
#from page_protection import get_page_info, set_page_protection, show_protection

kernel32 = windll.kernel32

#set_debug_privilege()  #書かなくても動いたけど、場合によっては必要かも

def open_process(pid):
    h_process = kernel32.OpenProcess(PROCESS_ALL_ACCESS, False, pid)
    if not h_process:
        print(WinError(GetLastError()))
        return False
    return h_process

def read_process_memory(h_process, address, length):
    data     = None
    read_buf = create_string_buffer(length)
    count    = c_ulonglong(0)
    if not kernel32.ReadProcessMemory(h_process, address, read_buf, length, byref(count)):
        print(WinError(GetLastError()))
        return False
    else:
        data += str(read_buf.raw)
    return data


def address_main():
    pid  = input("pid: ")#後述の方法で自動化可能

    h_process = open_process(int(pid))
    address=0x004F7844
    data = read_process_memory(h_process, address, 1)
    print(data)


if __name__ == "__main__":
    address_main()

```

実行時の残基を表示するコードです

# PID取得
tasklistコマンドを使う
東方輝針城の場合
```cmd
tasklist | find "th14.exe"
```

以下実行結果例
```cmd
th14.exe                     16984 Console                    1    159,780 K
```
これを加工すればpython側で自動取得できます

```python

import subprocess

def get_pid():
    r=subprocess.run("tasklist | find \"th14.exe\"",shell=True,stdout=subprocess.PIPE)
    return int(r.split()[1].stdout.decode())

```

# 追記
新規の情報ほぼ0のしょうもない記事すぎる...