coalg / diary

0 stars 0 forks source link

すごいPythonゲームでズルをしよう(FF10を例に) #27

Open coalg opened 4 months ago

coalg commented 4 months ago

FF10というゲームがあります。古いゲームですが、ストーリーがめっぽう面白いです。ストーリーは面白いけれどミニゲームはかなり大変で、その一つに「雷よけ」というミニゲームがあります。この記事ではミニゲーム「雷よけ」をPythonを使ってクリアする方法を紹介します。

モチベーション:雷よけのルールは簡単で、不定期に画面が光るので素早く◯ボタンを押して雷を避けるというものです。ミニゲームをクリアすると強い武器がもらえます。そのためクリアしたいですが、反射神経が悪い自分は200回連続で雷よけをするのがムズカシイ。100回超えたあたりで集中力が切れてしまう。やり直しもしんどい。最後にプレイしたのが10年以上前なのでいい加減にクリアできてもいいのではないか。光ったらボタンを押せばいいだけなので簡単なプログラムを書けばクリアできるのではないか。

ということで、文末の文献を参考にプログラムを書いてみました。参考文献と手順は同じですが、こちらはSteam版向けという違いがあります。

import time
from itertools import product
import win32gui
import pydirectinput as pd
from PIL import ImageGrab

def main():
    N = 30
    WHITE_RATIO = 0.5

    hwnd = win32gui.FindWindow(None, r"FINAL FANTASY X")
    win32gui.SetForegroundWindow(hwnd)

    while True:
        # threashold
        TH = 175
        is_white = lambda x: 0 if x < TH else 255

        dimensions = win32gui.GetWindowRect(hwnd)
        image = ImageGrab.grab(dimensions)
        image = image.resize((N, N))
        image = image.convert("L")
        image = image.point(is_white)
        xs = [image.getpixel((x, y)) for (x, y) in product(range(N), range(N)) if image.getpixel((x,y)) == 255]
        (w, h) = image.size
        ratio = len(xs) / (w * h)
        if ratio > WHITE_RATIO:
            # Thunder!
            try:
                pd.press("enter")
            except:
                pass
            time.sleep(.5)

        # 60fps
        time.sleep(0.0166667)

if __name__ == "__main__":
    main()

一つ解説すると、有名なPyAutoGUIではなくpydirectinputを使っています。理由はPyDirectInputの説明にある通りで仮想キーコードではなくDirectInput経由でないとアプリが入力を受け付けないためです。

image

このツールのおかげでナイト・オブ・タマネギを最大強化することが出来ました。やったね。

参考 FF10の雷除けを自動化した話 - panchiga's blog