寿司打を自動化しよう！

## 1. はじめに

私が設立したサークル「Terminal」にて配布した資料を公開します。自身も初心者であるため、間違っている部分や改善点等あれば教えていただけると幸いです。

この記事では、Pythonを用いてブラウザゲーム「寿司打」を自動でプレイするための環境構築と実行手順を解説します。

seleniumでのWebブラウザの自動操作、pyautoでのマウス・キーボード操作の自動化、そして画面に表示される文字の認識（OCR）を組み合わせて、寿司打のタイピングを自動で行います。

**注意:**

- 実行中はマウスやキーボードの制御がスクリプトに奪われます。プログラムを強制終了したい場合は、実行しているターミナル（コマンドプロンプト）で `Ctrl + C` を押してください。

## 2. 必要なもの

- **Windows OS**: このガイドは Windows 環境を前提としています。
- **Google Chrome ブラウザ**: 寿司打をプレイ環境として使用します。
- **Python**: プログラミング言語で、データの処理・分析が得意な言語です。
- **Tesseract-OCR**: 画像から文字を読み取るための OCR エンジンです。

## 3. 環境構築

### 3.0. Chromeのインストール

Google Chromeがまだインストールされていない場合は、以下の手順でインストールします。

1. **Chromeのダウンロード**:
   - Google Chromeの公式サイト (https://www.google.com/chrome/) にアクセスします。

   - 「Chrome をダウンロード」ボタンをクリックします。

2. **Chromeのインストール**:
   - ダウンロードしたインストーラー（ChromeSetup.exe）を実行します。

   - インストールは自動的に進行します。

   - インストールが完了すると、Chromeが自動的に起動します。

3. **インストール確認**:
   - Chromeが正常に起動し、ウェブページを表示できることを確認します。

### 3.1. Python のインストール

Python がまだインストールされていない場合は、以下の手順でインストールします。

1.  **Python のダウンロード**:

    - Python の公式サイト ([https://www.python.org/downloads/](https://www.python.org/downloads/)) にアクセスします。

    - 最新版の Python（例: Python 3.10.x や 3.11.x）のインストーラーをダウンロードします。通常、「Download Python X.X.X」というボタンがあります。

2.  **Python のインストール**:

    - ダウンロードしたインストーラー（`.exe`ファイル）を実行します。

    - **重要:** インストーラーの最初の画面で、一番下にある**「Add python.exe to PATH」というチェックボックスにチェックを入れてください。** これにより、コマンドプロンプトで Python を簡単に実行できるようになります。
        ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4065241/a93a9a9e-ac8a-476a-903b-b9e6aaaabdef.png)

    - 「Install Now」をクリックしてインストールを開始します。

    - インストールが完了したら、「Close」をクリックします。


3.  **インストール確認**:
    - コマンドプロンプトを開きます。（Windows キーを押し、「cmd」と入力して Enter）

    - 以下のコマンドを入力して Enter キーを押します。
      ```bash
      python --version
      ```

    - `Python 3.X.X` のようにバージョン情報が表示されれば、正しくインストールされています。
 
### 3.2. Tesseract-OCR のインストール

Tesseract-OCR は、画像内の文字を認識するために必要です。

1.  **Tesseract-OCR のダウンロード**:

    - Windows 用の Tesseract インストーラーは、[UB Mannheim の Tesseract リポジトリ](https://github.com/UB-Mannheim/tesseract/wiki) からダウンロードできます。

    - `tesseract-ocr-w64-setup-vX.X.X....exe` (64 ビット版)をダウンロードします。

2.  **Tesseract-OCR のインストール**:

    - ダウンロードしたインストーラーを実行します。

    - インストール先フォルダを確認しておきます。デフォルトでは `C:\Program Files\Tesseract-OCR` にインストールされます。このパスは後でスクリプト内で使用されることがあります。

    - 画面の指示に従ってインストールを完了します。設定はすべて変更しなくて大丈夫です。

### 3.3. 仮想環境の作成と有効化

プロジェクトごとに Python の環境を分離するために、仮想環境を作成します。仮想環境を作成することで、プロジェクトごとに別のバージョンのpythonを利用できたり、必要最低限のライブラリのみをインストールできます。

1.  **プロジェクトフォルダの作成**:

    - スクリプトを保存するためのフォルダを任意の場所に作成します。（例: `C:\Developments\sushida-auto`）

    - コマンドプロンプト(PowerShell)を開き、作成したフォルダに移動します。
      ```
      cd C:\Developments\sushida-auto
      ```

2.  **仮想環境の作成**:

    - コマンドプロンプトで以下のコマンドを実行して、仮想環境を作成します。[仮想環境名]の部分には、お好きな名前を入れてください。

      ```bash
      python -m venv .[仮想環境名]

      例: python -m venv .test
      ```

3.  **仮想環境の有効化 (アクティベート)**:
    - コマンドプロンプトで以下のコマンドを実行して、作成した仮想環境を有効にします。
      ```
      .[仮想環境名]\Scripts\activate
      ```
      
    - コマンドプロンプトの行頭に `(.[仮想環境名])` と表示されれば、仮想環境が有効になっています。

### 3.4. 必要な Python ライブラリのインストール

スクリプトの実行に必要なライブラリをインストールします。

1.  **`requirements.txt` ファイルの作成 (推奨)**:

    - プロジェクトフォルダ（例: `C:\Developments\sushida-auto`）に、`requirements.txt` という名前のテキストファイルを作成し、以下の内容を記述します。
    ```txt
    selenium
    pyautogui
    pyocr
    Pillow 
    ```
    このように記述することで、必要なライブラリを一括でダウンロードすることができます。

2.  **ライブラリのインストール**:
    - 仮想環境が有効になっているコマンドプロンプトで、以下のいずれかのコマンドを実行します。

      - `requirements.txt` を作成した場合:
        ```bash
        pip install -r requirements.txt
        ```

      - `requirements.txt` を作成しない場合 (ライブラリを個別にインストール):
        ```bash
        pip install selenium pyautogui pyocr Pillow
        ```

    - ライブラリのダウンロードとインストールが自動的に行われます。

## 4. スクリプトの準備

1.  **`main.py` スクリプトの配置**:

    - 作成したプロジェクトフォルダ（例: `C:\Developments\sushida-auto`）に、`main.py` を作成します。ファイルの内容は以下の通りです。

    ```python
    from selenium import webdriver
    from selenium.webdriver.common.by import By
    import pyautogui
    import time
    import pyocr
    import os
    import pyocr.builders
    from PIL import Image
    from selenium.common.exceptions import UnexpectedAlertPresentException

    path_tesseract = "C:\\Program Files\\Tesseract-OCR"
    if path_tesseract not in os.environ["PATH"].split(os.pathsep):
        os.environ["PATH"] += os.pathsep + path_tesseract

    builder = pyocr.builders.TextBuilder()
    tools = pyocr.get_available_tools()
    tool = tools[0]

    window_x = 800
    window_y = 750

    options = webdriver.ChromeOptions()
    options.add_experimental_option("detach", True)
    options.add_argument("--disable-gpu")

    driver = webdriver.Chrome(options=options)
    driver.set_window_size(window_x, window_y)
    driver.get("https://sushida.net/")
    window_position = driver.get_window_position()

    element = driver.find_element(
        By.XPATH, "/html/body/div[2]/div[2]/div[2]/div[3]/a[2]")
    element.click()

    time.sleep(5)

    pyautogui.moveTo(window_position['x'] + window_x - 400,
                     window_position['y'] + window_y - 250)
    pyautogui.doubleClick()
    time.sleep(3)
    pyautogui.doubleClick()
    time.sleep(1)
    pyautogui.press("enter")
    time.sleep(2)

    while True:
        try:
            driver.get_screenshot_as_file("canvas.png")
        except UnexpectedAlertPresentException:
            alert = driver.switch_to.alert
            alert.accept()
            driver.get_screenshot_as_file("canvas.png")

        image = Image.open("canvas.png")
        image = image.crop((270, 337, 488, 362))
        image.save("cropped_image.png")
        
        result = tool.image_to_string(image, lang="eng", builder=builder)
        print(result)
        pyautogui.write(result, interval=0.0001)
    ```

    以下に、この `main.py` スクリプトの各部分が何をしているのかを詳細に解説します。

    **1. 必要なライブラリの読み込み**

    スクリプトの実行に必要な、さまざまな機能を持った部品（ライブラリ）を読み込みます。

    ```python
    from selenium import webdriver
    from selenium.webdriver.common.by import By
    import pyautogui
    import time
    import pyocr
    import os
    import pyocr.builders
    from PIL import Image
    from selenium.common.exceptions import UnexpectedAlertPresentException
    ```

    - `from selenium import webdriver`: Web ブラウザ（この場合は Chrome）を自動で操作するための Selenium ライブラリの主要な部品を読み込みます。

    - `from selenium.webdriver.common.by import By`: Selenium が Web ページ上の要素（ボタンやテキストボックスなど）を見つけるための方法（ID、クラス名、XPATH など）を指定する部品を読み込みます。

    - `import pyautogui`: マウスの移動やクリック、キーボードの入力といった GUI 操作を自動化するためのライブラリを読み込みます。

    - `import time`: プログラムの実行を一時的に停止させる（例: `time.sleep(5)` で 5 秒待つ）ためのライブラリを読み込みます。

    - `import pyocr`: 画像から文字を認識する OCR（Optical Character Recognition）機能を利用するためのライブラリを読み込みます。

    - `import os`: オペレーティングシステム（Windows など）の機能を利用するためのライブラリを読み込みます。ここでは主にファイルパスの操作や環境変数の設定に使います。

    - `import pyocr.builders`: `pyocr`ライブラリで、OCR 結果をどのような形式（テキスト、数値など）で受け取るかを指定するための部品を読み込みます。

    - `from PIL import Image`: 画像を開いたり、切り抜いたり、保存したりといった画像処理を行うための Pillow ライブラリ（PIL から派生）の`Image`モジュールを読み込みます。

    - `from selenium.common.exceptions import UnexpectedAlertPresentException`: Selenium で Web 操作中に予期しないアラート（ポップアップメッセージ）が表示された場合に発生するエラー（例外）を処理するために読み込みます。

    **2. Tesseract OCR のパス設定**

    画像から文字を読み取るために使われる Tesseract OCR ソフトウェアがどこにインストールされているかをスクリプトに教えます。

    ```python
    path_tesseract = "C:\\Program Files\\Tesseract-OCR"
    if path_tesseract not in os.environ["PATH"].split(os.pathsep):
        os.environ["PATH"] += os.pathsep + path_tesseract
    ```

    - `path_tesseract = "C:\\Program Files\\Tesseract-OCR"`: Tesseract OCR がインストールされているフォルダの場所（パス）を`path_tesseract`という変数に保存します。Windows の標準的なインストール場所です。もし異なる場所にインストールした場合は、ここを修正する必要があります。

    - `if path_tesseract not in os.environ["PATH"].split(os.pathsep):`: 現在の環境変数`PATH`（プログラムが外部コマンドを探すための場所リスト）に、`path_tesseract`で指定したパスが含まれているか確認します。

    - `os.environ["PATH"] += os.pathsep + path_tesseract`: もし含まれていなければ、環境変数`PATH`に`path_tesseract`のパスを追加します。これにより、スクリプトが Tesseract OCR の実行ファイルを見つけられるようになります。

    **3. PyOCR の初期設定**

    `pyocr`ライブラリを使って文字認識を行うための準備をします。

    ```python
    builder = pyocr.builders.TextBuilder()
    tools = pyocr.get_available_tools()
    tool = tools[0]
    ```

    - `builder = pyocr.builders.TextBuilder()`: OCR で文字を認識した結果を、単純なテキスト（文字列）として取得するための設定を作成し、`builder`変数に保存します。

    - `tools = pyocr.get_available_tools()`: 現在の PC 環境で利用可能な OCR ツール（Tesseract OCR など）のリストを取得し、`tools`変数に保存します。

    - `tool = tools[0]`: 利用可能な OCR ツールのリストから、最初のツールを選択し（通常は Tesseract OCR がこれに該当します）、`tool`変数に保存します。この`tool`を使って実際に文字認識を行います。

    **4. ブラウザウィンドウサイズの設定**

    自動操作する Chrome ブラウザのウィンドウサイズを定義します。

    ```python
    window_x = 800
    window_y = 750
    ```

    - `window_x = 800`: ブラウザウィンドウの幅を 800 ピクセルに設定します。
    
    - `window_y = 750`: ブラウザウィンドウの高さを 750 ピクセルに設定します。

    **5. Chrome ブラウザの起動オプション設定**

    Chrome ブラウザを起動する際の色々なオプションを設定します。

    ```python
    options = webdriver.ChromeOptions()
    options.add_experimental_option("detach", True)
    ```

    - `options = webdriver.ChromeOptions()`: Chrome ブラウザの起動オプションを管理するためのオブジェクトを作成し、`options`変数に保存します。

    - `options.add_experimental_option("detach", True)`: Selenium によるスクリプトの実行が終了した後も、ブラウザウィンドウが自動的に閉じないようにするためのオプションです。

    **6. WebDriver の初期化と寿司打サイトへのアクセス**

    設定したオプションを使って Chrome ブラウザを起動し、寿司打のウェブサイトを開きます。

    ```python
    driver = webdriver.Chrome(options=options)
    driver.set_window_size(window_x, window_y)
    driver.get("https://sushida.net/")
    window_position = driver.get_window_position()
    ```

    - `driver = webdriver.Chrome(options=options)`: 上記で設定した`options`を使って、新しい Chrome ブラウザのインスタンス（操作対象）を起動し、`driver`変数に保存します。この`driver`を通じてブラウザを操作します。

    - `driver.set_window_size(window_x, window_y)`: 起動したブラウザのウィンドウサイズを、先に定義した`window_x`（幅）と`window_y`（高さ）に設定します。

    - `driver.get("https://sushida.net/")`: ブラウザで指定した URL（寿司打の公式サイト）を開きます。

    - `window_position = driver.get_window_position()`: 現在開いているブラウザウィンドウの画面上の左上の X 座標と Y 座標を取得し、`window_position`変数に保存します。これは後で`pyautogui`を使って画面上の特定の位置をクリックする際に、基準点として使います。

    **7. ゲーム開始までの画面操作**

    寿司打のゲームを開始するために必要なボタンクリックや待機処理を行います。

    ```python
    element = driver.find_element(
        By.XPATH, "/html/body/div[2]/div[2]/div[2]/div[3]/a[2]")
    element.click()

    time.sleep(5)

    pyautogui.moveTo(window_position['x'] + window_x - 400,
                     window_position['y'] + window_y - 250) # 人によって座標は違うので修正が必要
    pyautogui.doubleClick()
    time.sleep(3)
    pyautogui.doubleClick()
    time.sleep(1)
    pyautogui.press("enter")
    time.sleep(2)
    ```

    - `element = driver.find_element(By.XPATH, "/html/body/div[2]/div[2]/div[2]/div[3]/a[2]")`: 寿司打のウェブページ上にある特定の要素（「寿司打をPlay」のボタン）を見つけ出し、`element`変数に保存します。`By.XPATH`は HTML 構造における要素の絶対的な位置を示す方法です。

    - `element.click()`: 見つけた`element`（ボタン）をクリックします。

    - `time.sleep(5)`: 画面遷移や読み込みのために 5 秒間プログラムの実行を停止します。

    - `pyautogui.moveTo(window_position['x'] + window_x - 400, window_position['y'] + window_y - 250)`: マウスカーソルを画面上の特定の位置に移動させます。`window_position`（ブラウザの左上座標）と`window_x`, `window_y`（ブラウザのサイズ）を元に、ブラウザウィンドウ内の相対的な座標を指定しています。

    - `pyautogui.doubleClick()`: 現在のマウスカーソル位置でダブルクリックします。これは、寿司打のゲーム開始フローにおけるクリック操作（例：コース選択後の「ゲームスタート」ボタンなど）を想定しています。

    - `time.sleep(3)`: 3 秒間待機します。

    - `pyautogui.doubleClick()`: 再度ダブルクリックします。（例：難易度選択後の「ゲームスタート」ボタンなど）

    - `time.sleep(1)`: 1 秒間待機します。

    - `pyautogui.press("enter")`: Enter キーを押します。寿司打のゲーム開始の最終確認で使います。

    - `time.sleep(2)`: ゲームが実際に始まるまでの準備として 2 秒間待機します。

    **8. メインループ：自動タイピング処理**

    ここからがゲームのメイン部分で、無限ループ（`while True:`）の中で、画面の文字を認識してタイピングする処理を繰り返します。

    ```python
    while True:
        try:
            driver.get_screenshot_as_file("canvas.png")
        except UnexpectedAlertPresentException:
            alert = driver.switch_to.alert
            alert.accept()
            driver.get_screenshot_as_file("canvas.png")

        image = Image.open("canvas.png")
        image = image.crop((270, 337, 488, 362)) # 人によって座標は違うので修正が必要
        image.save("cropped_image.png")

        result = tool.image_to_string(image, lang="eng", builder=builder)
        print(result)
        pyautogui.write(result, interval=0.0001)
    ```

    - `while True:`: このループは、手動でプログラムを停止する（例: `Ctrl+C`を押す）まで無限に繰り返されます。

    - `try:`: このブロック内の処理でエラーが発生する可能性に備えます。

    - `driver.get_screenshot_as_file("canvas.png")`: 現在のブラウザの表示内容全体のスクリーンショットを撮影し、`canvas.png`というファイル名で保存します。
    
    - `except UnexpectedAlertPresentException:`: もし`try:`ブロック内で`UnexpectedAlertPresentException`というエラーが発生した場合、この`except`ブロックの処理が実行されます。
    
    - `alert = driver.switch_to.alert`: 表示されているアラートに操作の対象を切り替えます。
    
    - `alert.accept()`: アラートの「OK」ボタンや「はい」ボタンに相当する操作を行い、アラートを閉じます。
    
    - `driver.get_screenshot_as_file("canvas.png")`: アラートを閉じた後、再度スクリーンショットを撮り直します。
    
    - `image = Image.open("canvas.png")`: 保存されたスクリーンショットファイル (`canvas.png`) を Pillow ライブラリで開いて、画像オブジェクトとして`image`変数に読み込みます。
    
    - `image = image.crop((270, 337, 488, 362))`: 読み込んだ画像(`image`)の中から、特定の範囲を切り抜きます（クロップします）。`(270, 337, 488, 362)` は切り抜く矩形領域の左上の X 座標, 左上の Y 座標, 右下の X 座標, 右下の Y 座標をそれぞれピクセル単位で指定しています。数値は`canvas.png`がうまく文字を認識できるような感じになるよう適切な値に設定してください。
    
    - `result = tool.image_to_string(image, lang="eng", builder=builder)`: 切り抜いた画像 (`image`) に対して、`pyocr`の`tool`（Tesseract OCR）を使って文字認識を実行します。`lang="eng"`で英語として認識するように指定し、`builder`で結果をテキストで受け取るようにしています。認識された文字列は`result`変数に保存されます。
    
    - `print(result)`: 認識された文字列 (`result`) をコンソールに出力します。デバッグのために出力されています。
    
    - `pyautogui.write(result, interval=0.0001)`: 認識された文字列 (`result`) を`pyautogui`を使って自動でキーボード入力します。`interval=0.0001`では各文字を入力する間の時間間隔を 0.0001 秒に設定しています。

## 5. 実行方法

1.  **仮想環境の有効化**:

    - コマンドプロンプトを開き、プロジェクトフォルダに移動し、仮想環境を有効にします (まだ有効にしていない場合)。

      ```
      cd C:\Developments\sushida-auto

      .[仮想環境名]\Scripts\activate
      ```

2.  **スクリプトの実行**:

    - コマンドプロンプトで以下のコマンドを入力して Enter キーを押します。
      ```bash
      python main.py
      ```

3.  **動作確認**:

    - スクリプトが実行されると、自動的に Chrome ブラウザが起動し、寿司打のサイトが開かれ、ゲームが開始されます。
    - 画面に表示される文字が自動でタイピングされていきます。
    - コマンドプロンプトには、OCR で認識された文字が表示されます。

4.  **スクリプトの停止**:
    - スクリプトを途中で停止したい場合は、コマンドプロンプトのウィンドウを選択した状態で `Ctrl + C` キーを押してください。

## 6. おわりに

この記事では、pythonでseleniumとpyautoを用いて、webブラウザを自動で操作しました。

seleniumを用いると、webブラウザの操作を自動化することが出来ます。スクレイピングなどにも利用できるため、興味を持った人はぜひ調べてみましょう。