gym-super-mario-brosで途中から再開する方法

mario_backup_reset.gif

日曜日の勉強会の仲間が「マリオAIチャレンジ」をやっているのを見ていて、楽しそうだったので最近触っています。

karaage.hatenadiary.jp

まだ学習をさせるところまでは行けていなくて、マリオに行動を配列で指示したり、gifなどの画像に出力させるところをいじくっているところです。

ところで、途中まで進んだマリオを、そこから再開できる方法があるか考えていました。それができれば計算が早くなるという想定です。最初はdeepcopyでできるのではないかと考えたのですが、やってみるとうまくいきませんでした。おそらくNES emulator (=nes_env.pyの_LIBオブジェクト)がpythonのdeepcopyでは適切にコピーされないのだと思います。

不慣れなPythonなのですが、コードをがんばって追いかけると、nes_env.py_backupメソッドresetメソッドを呼べば良さそうなことまでわかりました。backupメソッドを呼ぶとその箇所が保存されて、その後のresetメソッドではそこに戻ることができるようです。 しかし、resetメソッドはSuperMarioBrosEnvから呼べるのですが、backupメソッドは外部に公開されていないメソッドなので、使えないようなのです。困った。

いろいろ調べたところ、envオブジェクトはWrapperクラスのサブクラスの複数回の委譲を繰り返してSuperMarioBrosEnv(そしてNESEnv)に到達するようで、このWrapperクラスにbackupメソッドを追加すれば良さそうだということに気がつきました。

Pythonのクラスにメソッドを後から追加する方法を調べて、手探りで書いてみました。

# Wrapperクラスにbackupメソッドを追加
def backup(self):
  if hasattr(self.env, "_backup"):
    self.env._backup() # gym_super_mario_bros.smb_env.SuperMarioBrosEnv#backup
  else:
    self.env.backup() # 委譲

Wrapper.backup = backup

(↑2023/01/08 22:55修正)

backupメソッドを持つオブジェクトが見つかるまで委譲先を探して、backupメソッドを持つオブジェクト(SuperMarioBrosEnvクラスのオブジェクト)が見つかったらそれを実行します。 やってみると、うまくいくようです。やった!

ちなみに、Wrapperクラスは委譲の仕組みが入っているので、resetメソッドは何もしなくても委譲されてSuperMarioBrosEnvクラスのresetメソッドが呼ばれます。SuperMarioBrosEnvクラスの_backupメソッドが委譲されていなかったのは、 _ で始まるメソッド名だからです。 (2023/01/08 22:55追記)

Google Colaboratoryも、最小限のコードでの再現環境として作っておきました。 colab.research.google.com

わかりにくいかもしれませんが、最初はクリボーにぶつかってやられているけど、ちょっと戻ってジャンプで回避しています。