ターミナル上でゲームボーイを動かす【Python】

[Pythonアドベントカレンダー2020](https://qiita.com/advent-calendar/2020/python) の12日目です！

2020/12/13追記：**OSやブラウザのバージョンによっては絵文字が表示されないようです。**出力はgifにしているので、プログラムは心の目で読んでください。
また**記事内のコードをブラウザで実行できる**ようにしました！
[Colaboratoryを開く](https://colab.research.google.com/github/dannyso16/Gameboy-on-terminal/blob/master/Qiita_gameboy_on_terminal.ipynb)



# 完成したもの

ターミナルでゲームボーイを動かしました！
フォントサイズが小さくて分かりにくいですが、**各ドットが絵文字🟥🟧🟨🟩🟦🟪🟫⬛⬜で表現**されています。

![color_cool.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/305855/9df11501-83b1-0fa4-47cb-8799ead33973.gif)



いくつか**カラーバリエーション**をつくってみました。

**目に優しいver.**
![color_hot-min.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/305855/90af84c3-e708-3e6a-0002-8004297ffca1.gif)

**点字ver.**
点字（⠒, ⠛, ⣤, ⣿）は絵文字を描画できないターミナルでも動くのでうれしい。
![color_mono_Trim.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/305855/9b841c36-7056-dff3-cd10-4a828b59f0e9.gif)




## （完成しなかったもの）

ボタン入力部分を実装できていないので、**まだ遊べません…**。タイトル画面を無限に眺めることはできます。今後の課題にします。

# 環境

![Windows10-Education](https://img.shields.io/badge/Windows10-Education-brightgreen) ![Windows terminal](https://img.shields.io/badge/Terminal-Windows terminal-brightgreen) ![python](https://img.shields.io/badge/Pythonl-3.6, 3.7-brightgreen)

ゲームボーイエミュレータのPyBoyのWindows version が`Python 3.6 and 3.7`しか対応していないので注意です（2020//12/12現在）。**OSとターミナルは何でも良いはずですが、絵文字を描画できるターミナルでないとカラーでは実行できません**。ただしオプション引数を指定すれば、**絵文字が描画できないターミナルでも点字を使ってゲームボーイができる**ようにしました！あとゲームボーイのROMファイル`hoge.gb`も必要です。がんばってカセットから吸い出しましょう。

実装はこちら→[GitHub: dannyso16/Gameboy-on-terminal](https://github.com/dannyso16/Gameboy-on-terminal)

以下丁寧語やめます。

# 記事の流れ

以下の順に説明していく。

1. [ANSIエスケープコードとは？](#1ansiエスケープコード)
2. [ゲームボーイエミュレータ「PyBoy」の概要](#2ゲームボーイエミュレータpyboy)
3. [pyboyから画面情報を抜き出す](#3pyboyから画面情報を抜き出す)
4. [実装](#4実装)



# 1.ANSIエスケープコード

## 基本

基本の部分は @kuroituさんの記事「[Pythonで進捗表示したい  Qiita](https://qiita.com/kuroitu/items/f18acf87269f4267e8c1#発展ansiエスケープコード)」が分かりやすいので、「発展ansiエスケープコード」の部分を見てほしい。

要は**ANSIエスケープコードを使えばカーソルの制御ができる**もので、「カーソルを上に3行動かす」ようなことができる。

| ANSIエスケープコード |                             効果                             |
| :------------------: | :----------------------------------------------------------: |
|       \033[nA        |                    カーソルを上にn行移動                     |
|       \033[nB        |                    カーソルを下にn行移動                     |
|       \033[nC        |                     カーソルを右にn移動                      |
|       \033[nD        |                     カーソルを左にn移動                      |
|       \033[nK        | n=0のときはカーソルより後ろの文字列を削除、<br />n=1のときはカーソルより前の文字列を削除、<br />n=2のときは行全体を削除 |

これを応用すると、さきに紹介した記事のようにプログレスバーを自由に実装出来たりする。まずは簡単な例から動かして理解を深めよう。**適当な顔文字をアニメーション**させてみる。

2020/12/13追記：Colaboratoryで試せるようにしました！→[Colaboratoryを開く](https://colab.research.google.com/github/dannyso16/Gameboy-on-terminal/blob/master/Qiita_gameboy_on_terminal.ipynb)

```python
import time

spin_characters = ["( ˘ω˘  )",
                   "(   ˘ω )",
                   "(     ˘)",
                   "(˘     )",
                   "(ω˘    )",
                   "(˘ω˘   )"
                   ]
cycle = len(spin_characters)

for i in range(cycle*5):
    i %= cycle
    s = spin_characters[i]
    print(s, end="")
    time.sleep(0.2)
    print("\033[1A")

# 後処理
print("\033[2K")
```

`(˘ω˘   )`がくるくる回っているのが確認できたら完璧！！

![kao_qiita_Trim.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/305855/119dff17-89e4-acfd-aa84-48214f1841ef.gif)


やっていることは単純で

1. 描画する
2. 少し待つ
3. カーソルを左端に戻す

を繰り返しているだけだ。カーソルを左端に戻して`print`すると上書きされるのがポイントだ。



## 絵文字でドット絵をつくる

ドット絵を表すために、色つきの四角形の絵文字🟥🟧🟨🟩🟦🟪🟫⬛⬜を使う。ちょっと意外だが、🟥🟧🟨🟩🟦🟪🟫の7つは、2019年の Unicode 12.0 で新しく追加されたものらしい。

とりあえず**マリオのドット絵を書いてみた**。

```python
# マリオ：ドット絵は右を参考に書いたhttps://www.spriters-resource.com/nes/supermariobros/sheet/50365/
stand = """
⬜⬜⬜⬜⬜⬜🟥🟥🟥🟥🟥⬜⬜⬜⬜⬜
⬜⬜⬜⬜🟥🟥🟥🟥🟥🟥🟥🟥🟥⬜⬜⬜
⬜⬜⬜⬜🟫🟫🟫🟨🟨🟫🟨⬜⬜⬜⬜⬜
⬜⬜⬜🟫🟨🟫🟨🟨🟨🟫🟨🟨🟨⬜⬜⬜
⬜⬜⬜🟫🟨🟫🟫🟨🟨🟨🟫🟨🟨🟨⬜⬜
⬜⬜⬜🟫🟫🟨🟨🟨🟨🟫🟫🟫🟫⬜⬜⬜
⬜⬜⬜⬜⬜🟨🟨🟨🟨🟨🟨🟨⬜⬜⬜⬜
⬜⬜⬜⬜🟥🟥🟦🟥🟥🟥⬜⬜⬜⬜⬜⬜
⬜⬜⬜🟥🟥🟥🟦🟥🟥🟦🟥🟥🟥⬜⬜⬜
⬜⬜🟥🟥🟥🟥🟦🟦🟦🟦🟥🟥🟥🟥⬜⬜
⬜⬜🟨🟨🟥🟦🟨🟦🟦🟨🟦🟥🟨🟨⬜⬜
⬜⬜🟨🟨🟨🟦🟦🟦🟦🟦🟦🟨🟨🟨⬜⬜
⬜⬜🟨🟨🟦🟦🟦🟦🟦🟦🟦🟦🟨🟨⬜⬜
⬜⬜⬜⬜🟦🟦🟦⬜⬜🟦🟦🟦⬜⬜⬜⬜
⬜⬜⬜🟫🟫🟫⬜⬜⬜⬜🟫🟫🟫⬜⬜⬜
⬜⬜🟫🟫🟫🟫⬜⬜⬜⬜🟫🟫🟫🟫⬜⬜"""
```

なかなかの出来栄えだ！



## ドット絵アニメーション

まずは描画したいデータを頑張ってつくる。パラパラ漫画のやり方だ。

```python
spin_characters = [
    """
⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜
⬜⬜⬜⬜⬜⬜🟥🟥🟥🟥🟥⬜⬜⬜⬜⬜
⬜⬜⬜⬜🟥🟥🟥🟥🟥🟥🟥🟥🟥⬜⬜⬜
⬜⬜⬜⬜🟫🟫🟫🟨🟨🟫🟨⬜⬜⬜⬜⬜
⬜⬜⬜🟫🟨🟫🟨🟨🟨🟫🟨🟨🟨⬜⬜⬜
⬜⬜⬜🟫🟨🟫🟫🟨🟨🟨🟫🟨🟨🟨⬜⬜
⬜⬜⬜🟫🟫🟨🟨🟨🟨🟫🟫🟫🟫⬜⬜⬜
⬜⬜⬜⬜⬜🟨🟨🟨🟨🟨🟨🟨⬜⬜⬜⬜
⬜⬜⬜⬜🟥🟥🟥🟦🟥🟥⬜⬜⬜⬜⬜⬜
⬜⬜⬜🟥🟥🟥🟥🟦🟦🟥🟥⬜⬜⬜⬜⬜
⬜⬜⬜🟥🟥🟥🟦🟦🟨🟦🟦⬜⬜⬜⬜⬜
⬜⬜⬜🟥🟥🟥🟥🟦🟦🟦🟦⬜⬜⬜⬜⬜
⬜⬜⬜🟦🟥🟥🟨🟨🟦🟦🟦⬜⬜⬜⬜⬜
⬜⬜⬜⬜🟦🟥🟨🟨🟦🟦⬜⬜⬜⬜⬜⬜
⬜⬜⬜⬜⬜🟦🟦🟦🟫🟫🟫⬜⬜⬜⬜⬜
⬜⬜⬜⬜⬜🟫🟫🟫🟫⬜⬜⬜⬜⬜⬜⬜""",

    """
⬜⬜⬜⬜⬜⬜🟥🟥🟥🟥🟥⬜⬜⬜⬜⬜
⬜⬜⬜⬜🟥🟥🟥🟥🟥🟥🟥🟥🟥⬜⬜⬜
⬜⬜⬜⬜🟫🟫🟫🟨🟨🟫🟨⬜⬜⬜⬜⬜
⬜⬜⬜🟫🟨🟫🟨🟨🟨🟫🟨🟨🟨⬜⬜⬜
⬜⬜⬜🟫🟨🟫🟫🟨🟨🟨🟫🟨🟨🟨⬜⬜
⬜⬜⬜🟫🟫🟨🟨🟨🟨🟫🟫🟫🟫⬜⬜⬜
⬜⬜⬜⬜⬜🟨🟨🟨🟨🟨🟨🟨⬜⬜⬜⬜
⬜⬜⬜⬜⬜⬜⬜🟥🟦🟦🟥⬜⬜⬜⬜⬜
⬜⬜⬜⬜⬜🟥🟥🟥🟥🟦🟥🟨🟨⬜⬜⬜
⬜⬜⬜🟨🟨🟥🟥🟥🟥🟥🟥🟨🟨🟨⬜⬜
⬜⬜🟨🟨🟨🟦🟥🟥🟥🟥🟥🟨🟨⬜⬜⬜
⬜⬜⬜🟫🟫🟦🟦🟦🟦🟦🟦🟦⬜⬜⬜⬜
⬜⬜⬜🟫🟦🟦🟦🟦🟦🟦🟦🟦⬜⬜⬜⬜
⬜⬜🟫🟫🟦🟦⬜⬜🟦🟦🟦⬜⬜⬜⬜⬜
⬜⬜🟫⬜⬜⬜⬜🟫🟫🟫⬜⬜⬜⬜⬜⬜
⬜⬜⬜⬜⬜⬜⬜⬜🟫🟫🟫⬜⬜⬜⬜⬜""",

    """
⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜
⬜⬜⬜⬜⬜⬜🟥🟥🟥🟥🟥⬜⬜⬜⬜⬜
⬜⬜⬜⬜🟥🟥🟥🟥🟥🟥🟥🟥🟥⬜⬜⬜
⬜⬜⬜⬜🟫🟫🟫🟨🟨🟫🟨⬜⬜⬜⬜⬜
⬜⬜⬜🟫🟨🟫🟨🟨🟨🟫🟨🟨🟨⬜⬜⬜
⬜⬜⬜🟫🟨🟫🟫🟨🟨🟨🟫🟨🟨🟨⬜⬜
⬜⬜⬜🟫🟫🟨🟨🟨🟨🟫🟫🟫🟫⬜⬜⬜
⬜⬜⬜⬜⬜🟨🟨🟨🟨🟨🟨🟨⬜⬜⬜⬜
⬜⬜⬜⬜🟥🟥🟥🟦🟥🟥⬜⬜⬜⬜⬜⬜
⬜⬜⬜🟥🟥🟥🟥🟦🟦🟥🟥⬜⬜⬜⬜⬜
⬜⬜⬜🟥🟥🟥🟦🟦🟨🟦🟦⬜⬜⬜⬜⬜
⬜⬜⬜🟥🟥🟥🟥🟦🟦🟦🟦⬜⬜⬜⬜⬜
⬜⬜⬜🟦🟥🟥🟨🟨🟦🟦🟦⬜⬜⬜⬜⬜
⬜⬜⬜⬜🟦🟥🟨🟨🟦🟦⬜⬜⬜⬜⬜⬜
⬜⬜⬜⬜⬜🟦🟦🟦🟫🟫🟫⬜⬜⬜⬜⬜
⬜⬜⬜⬜⬜🟫🟫🟫🟫⬜⬜⬜⬜⬜⬜⬜""",

    """
⬜⬜⬜⬜⬜⬜🟥🟥🟥🟥🟥⬜⬜⬜⬜⬜
⬜⬜⬜⬜🟥🟥🟥🟥🟥🟥🟥🟥🟥⬜⬜⬜
⬜⬜⬜⬜🟫🟫🟫🟨🟨🟫🟨⬜⬜⬜⬜⬜
⬜⬜⬜🟫🟨🟫🟨🟨🟨🟫🟨🟨🟨⬜⬜⬜
⬜⬜⬜🟫🟨🟫🟫🟨🟨🟨🟫🟨🟨🟨⬜⬜
⬜⬜⬜🟫🟫🟨🟨🟨🟨🟫🟫🟫🟫⬜⬜⬜
⬜⬜⬜⬜⬜🟨🟨🟨🟨🟨🟨🟨⬜⬜⬜⬜
⬜⬜🟥🟥🟥🟥🟦🟥🟥🟥🟦⬜⬜⬜⬜⬜
🟨🟨🟥🟥🟥🟥🟦🟦🟥🟥🟥🟦🟥🟨🟨🟨
🟨🟨🟨⬜🟥🟥🟦🟦🟦🟦🟦🟦🟥🟥🟨🟨
🟨🟨⬜⬜🟦🟦🟦🟨🟦🟦🟦🟨⬜⬜🟫⬜
⬜⬜⬜🟦🟦🟦🟦🟦🟦🟦🟦🟦🟦🟫🟫⬜
⬜⬜🟦🟦🟦🟦🟦🟦🟦🟦🟦🟦🟦🟫🟫⬜
⬜🟫🟫🟦🟦🟦⬜⬜⬜⬜🟦🟦🟦🟫🟫⬜
⬜🟫🟫🟫⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜
⬜⬜🟫🟫🟫⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜
""",

    """
⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜
⬜⬜⬜⬜⬜⬜🟥🟥🟥🟥🟥⬜⬜⬜⬜⬜
⬜⬜⬜⬜🟥🟥🟥🟥🟥🟥🟥🟥🟥⬜⬜⬜
⬜⬜⬜⬜🟫🟫🟫🟨🟨🟫🟨⬜⬜⬜⬜⬜
⬜⬜⬜🟫🟨🟫🟨🟨🟨🟫🟨🟨🟨⬜⬜⬜
⬜⬜⬜🟫🟨🟫🟫🟨🟨🟨🟫🟨🟨🟨⬜⬜
⬜⬜⬜🟫🟫🟨🟨🟨🟨🟫🟫🟫🟫⬜⬜⬜
⬜⬜⬜⬜⬜🟨🟨🟨🟨🟨🟨🟨⬜⬜⬜⬜
⬜⬜⬜⬜🟥🟥🟥🟦🟥🟥⬜⬜⬜⬜⬜⬜
⬜⬜⬜🟥🟥🟥🟥🟦🟦🟥🟥⬜⬜⬜⬜⬜
⬜⬜⬜🟥🟥🟥🟦🟦🟨🟦🟦⬜⬜⬜⬜⬜
⬜⬜⬜🟥🟥🟥🟥🟦🟦🟦🟦⬜⬜⬜⬜⬜
⬜⬜⬜🟦🟥🟥🟨🟨🟦🟦🟦⬜⬜⬜⬜⬜
⬜⬜⬜⬜🟦🟥🟨🟨🟦🟦⬜⬜⬜⬜⬜⬜
⬜⬜⬜⬜⬜🟦🟦🟦🟫🟫🟫⬜⬜⬜⬜⬜
⬜⬜⬜⬜⬜🟫🟫🟫🟫⬜⬜⬜⬜⬜⬜⬜""",
]
```

あとはさっきの要領で、描画→カーソルを左上に戻す→描画… とくり返せばいい。

```python
import time

FPS = 20

rows = spin_characters[0].split("\n")
rows = [r for r in rows if r]
height = len(rows)

during = 50
cycle = len(spin_characters)

for i in range(during):
    rows = spin_characters[i % cycle].split("\n")
    rows = [r for r in rows if r]

    for row in rows:
        # row = "⬜"*i + row
        print(row)
    time.sleep(1/FPS)
    print("\033[{}A".format(height+1))
else:
    # 全部終わったら"立ち"状態を表示
    rows = stand.split("\n")
    rows = [r for r in rows if r]

    for row in rows:
        # row = "⬜"*i + row
        print(row)
    time.sleep(0.05)
    print("\033[{}A".format(height+1))

# 後処理
print("\033[{}B".format(height+1))

```

上のコードを実行するとマリオが走る！

![mario_qiita_Trim.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/305855/191ea158-7a7d-3432-1bb3-4d25db4235c0.gif)

## ここまでまとめ

**ターミナル上でドット絵をアニメーションさせることができた。**そしてアニメーションさせるには各フレームでのドット絵が必要であることが分かった。つまり**ゲームボーイの画面情報さえ取得できれば、ターミナルに描画できる状態だ！**

2節でエミュレータの概要を説明したのち、3節でエミュレータの画面情報を取得していく。

# 2.ゲームボーイエミュレータ「PyBoy」

さすがにエミュレータを自作する力はないので、既存のものを借りる。エミュレータの概要をつかむ前に、ゲームボーイについておさらいしておく。

## ゲームボーイの画面について

- 解像度：160×144ドット
- 画面：4階調モノクロ
  - RGBだと、(0, 0, 0), (85, 85, 85),  (153, 153, 153), (255, 255, 255)の4色
- フレームレート：だいたい30fps

実装の際にこの辺の情報が役に立ったり役に立たなかったりするので参考までに。

## PyBoyの導入
![pyboy.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/305855/b0414981-1b80-cd17-f239-d11bb32bce6a.jpeg)



基本的には[PyBoy installation (GitHub)](https://github.com/Baekalfen/PyBoy/wiki/Installation)に丁寧に書いてあるのでこれに従えばいい。LinuxやMacはコマンド一発で入るが、Windowsはちょっとめんどくさい。Windows10の場合を以下に乗せておく（興味ない方は[ここ](#3pyboyから画面情報を抜き出す)までスキップ）。

```bash
$ python --version # 3.6 か 3.7 であることを確認する

# SDLの導入
$ (New-Object Net.WebClient).DownloadFile('https://www.libsdl.org/release/SDL2-devel-2.0.10-VC.zip', 'SDL2-devel-2.0.10-VC.zip')
$ Expand-Archive -Force 'SDL2-devel-2.0.10-VC.zip' C:\SDL2\
```

自分のPCだと`Expand-Archive -Force 'SDL2-devel-2.0.10-VC.zip' C:\SDL2\`がうまく動作しなかったので、GUIでzipを解凍したのち`C:\SDL2\`配下に置いた。

```bash
# パスを通す
$ setx PYSDL2_DLL_PATH C:\SDL2\SDL2-2.0.10\lib\x64
$ setx PATH "%PATH%;C:\SDL2\SDL2-2.0.10\lib\x64"> 

$ exit # (ターミナルを再起動しとく)

$ python -m pip install --upgrade --user pip
$ python -m pip install --user pyboy
```

ここまで来たら、実際にエミュレータを起動できる。

```bash
python -m pyboy path/to/rom.gb
```
![pyboy_installation_demo.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/305855/b9d824e9-eff2-b7ed-b1b5-229487557169.png)
図：自分はポケモン青で試した、起動すると嬉しい。

<br>

操作は以下の表を参考にするといい。

●基本的なキーマップ

| Keyboard key | GameBoy equivalant |
| ------------ | ------------------ |
| Up           | Up                 |
| Down         | Down               |
| Left         | Left               |
| Right        | Right              |
| A            | A                  |
| S            | B                  |
| Return       | Start              |
| Backspace    | Select             |

●特殊操作

| Keyboard key | Emulator function       |
| ------------ | ----------------------- |
| Escape       | Quit                    |
| D            | Debug                   |
| Space        | Unlimited FPS           |
| Z            | Save state              |
| X            | Load state              |
| I            | Toggle screen recording |
| ,            | Rewind backwards        |
| .            | Rewind forward          |

なお手元にROMファイルがない場合は、`pyboy/pyboy/default_rom.gb`にテスト用のROMがあるのでそれで試すといい。

![pyboy_installation_demo2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/305855/c451d35c-560a-6341-03f9-6f2202b8ef57.png)

これで PyBoyが導入できた！



# 3.PyBoyから画面情報を抜き出す

スクリーンショット機能もあるので、なんか良いのないかとソースコードを眺めてたら、AI用の`botsupport`なるものがあった。

[pyboy.botsupport documentation](https://docs.pyboy.dk/botsupport/)

この中の`screen()`が使えそうである。

> ```python
> def screen(self)
> ```
>
> Use this method to get a [`Screen`](https://docs.pyboy.dk/botsupport/screen.html#pyboy.botsupport.screen.Screen) object. This can be used to get the screen buffer in a variety of formats.
>
> It's also here you can find the screen position (SCX, SCY, WX, WY) for each scan line in the screen buffer. See [`Screen.tilemap_position()`](https://docs.pyboy.dk/botsupport/screen.html#pyboy.botsupport.screen.Screen.tilemap_position) for more information.
>
> ## Returns
>
> [`Screen`](https://docs.pyboy.dk/botsupport/screen.html#pyboy.botsupport.screen.Screen): A Screen object with helper functions for reading the screen buff

さらにラッキーなことに return されるScreen object には `np.array` で値を返すメソッドがある。

> ```python
> def screen_ndarray(self)
> ```
>
> Provides the screen data in NumPy format. The dataformat is always RGB.
>
> ## Returns
>
> numpy.ndarray:
> Screendata in ndarray of bytes with shape (160, 144, 3)

ありがてえ。さらにソースを眺めていると、OpenAIGymのためのクラスも作られている。観測として画面情報が与えられているので、`observation`に画面情報を代入する部分を流用して実装できそうだ。

```python
# Pyboy/pyboy/openai_gym.py より
def _get_observation(self):
        if self.observation_type == "raw":
            observation = np.asarray(self.pyboy.botsupport_manager().screen().screen_ndarray(), dtype=np.uint8)
        elif self.observation_type in ["tiles", "compressed", "minimal"]:
            observation = self.game_wrapper._game_area_np(self.observation_type)
        else:
            raise NotImplementedError(f"observation_type {self.observation_type} is invalid")
        return observation
```

# 4.実装

GitHubを見てください→[GitHub: dannyso16/Gameboy-on-terminal](https://github.com/dannyso16/Gameboy-on-terminal)

絵文字は4byte文字だからかわからないが、**毎フレーム描画するとフレームレートが💩だった**。各処理の実行時間を測定してみると、ターミナルに絵文字を描画する部分が完全にボトルネックだったので、これを改善しようとした。

![tooooo_slow.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/305855/676ce420-58ac-8b28-b6d0-43c384f66f58.gif)


## 工夫：ターミナルを変える（失敗）

描画が速いターミナルを使えばいいやん、と思って、[Alacritty](https://github.com/alacritty/alacritty)を試した。残念ながら絵文字は表示できなかった。断念。



## 工夫：描画をさぼる

仕方がないので、数フレームに一回描画をすることでフレームレートを30あたりに保つようにした。ただしスキップする割合は完全にハードコーディングしてしまったので、実行するPCによって調整してください…。

## 

# まとめ

ターミナルの描画速度なんて考えたこともなかったのですが、ゲームをするにはターミナルの描画の遅さが課題となりました。**ターミナルはゲームを遊ぶのに適していないようです！**ターミナルで1万文字以上の描画を高速で行いたい需要はなさそうなので仕方がないですが、いつかターミナルで遊べるようになったらいいですね。

# 余談

実は11月くらいからプログレスバーの代わりにマリオを躍らせる記事をこっそり書いていましたが、先を越されました。（[Pythonで進捗表示したい！@kuroitu on Qiita](https://qiita.com/kuroitu/items/f18acf87269f4267e8c1)）。自分のやつより面白くて完全に敗北したので、趣向を変えてゲームの記事にした経緯があったりします。

あと点字で描画すると絵文字で描画するより明らかに frame rate が高くなる現象は、原因がわからずじまいでした。（点字は3byte文字で絵文字が4byte文字だからっぽい？）。

# 参考

[Qiita記事にさりげなく（けど、わかりやすく）環境を記載する Qiita](https://qiita.com/zizi4n5/items/c5f0013236fd055c3e02)
[いろいろな色の四角形 絵文字  Lets emoji](https://lets-emoji.com/colorful-square-emoji/)
[Pythonで進捗表示したい  Qiita](https://qiita.com/kuroitu/items/f18acf87269f4267e8c1)
[ゲームボーイ wikipedia](https://ja.wikipedia.org/wiki/ゲームボーイ)
[spriters-resource: mario-bros](https://www.spriters-resource.com/nes/supermariobros/sheet/50365/)

Pyboy関係
[Baekalfen/PyBoy GitHub](https://github.com/Baekalfen/PyBoy)
[PyBoy installation GitHub](https://github.com/Baekalfen/PyBoy/wiki/Installation)
[PyBoy documentation](https://docs.pyboy.dk/)
[pyboy.botsupport documentation](https://docs.pyboy.dk/botsupport/)