<jupyter_start><jupyter_text>-------自作環境を用意するには 'srl.base.env.EnvBase' を継承して作ります。  ただ、作りたい環境によってはいらない情報もあるので、  'srl.base.env.genre'に各環境に合わせたインタフェースも用意しています。   ここでは一人プレイでアクションと状態が共に離散値を取る環境を作成していきます。  'srl.base.env.genre.singleplay.SinglePlayEnv' を継承してクラスを作っていきます。以下のプロパティと関数が最低限必要となります。``` pythonfrom typing import Any, List, Tuple, castimport numpy as npfrom srl.base.env.genre import SinglePlayEnvfrom srl.base.define import EnvObservationTypefrom srl.base.env.base import SpaceBaseclass MyEnv(SinglePlayEnv):     アクションの形式を返す（後述）    @property    def action_space(self) -> SpaceBase:        raise NotImplementedError()     状態の形式を返す（後述）    @property    def observation_space(self) -> SpaceBase:        raise NotImplementedError()     環境の種類を指定    @property    def observation_type(self) -> EnvObservationType:        raise NotImplementedError()         1エピソードの最大ターン数    @property    def max_episode_steps(self) -> int:        raise NotImplementedError()     エピソードの最初に呼ばれる関数     初期状態を返す    def call_reset(self) -> np.ndarray:        raise NotImplementedError()     1stepの関数     アクションを受け取り、1step進めて、[状態,報酬,終わりか,情報]を返す    def call_step(self, action: int) -> Tuple[np.ndarray, float, bool, dict]:        raise NotImplementedError()         backup/restoreで環境の状態を保存・復元できるように関数を作る    def backup(self) -> Any:        raise NotImplementedError()    def restore(self, data: Any) -> None:        raise NotImplementedError()``` 実際に作ってみます。<jupyter_code>import enum
from dataclasses import dataclass
from typing import Any, Tuple

import numpy as np
from srl.base.define import EnvObservationType
from srl.base.env.base import SpaceBase
from srl.base.env.genre.singleplay import SinglePlayEnv
from srl.base.env.spaces import BoxSpace, DiscreteSpace


class Action(enum.Enum):
    LEFT = 0
    RIGHT = 1


@dataclass
class MyEnv(SinglePlayEnv):

    move_reward: float = -0.04

    def __post_init__(self):
        self.field = [-1, 0, 0, 0, 0, 0, 0, 0, 1]

    @property
    def action_space(self) -> SpaceBase:
        return DiscreteSpace(len(Action))

    @property
    def observation_space(self) -> SpaceBase:
        return BoxSpace(
            low=0,
            high=len(self.field),
            shape=(1,),
        )

    @property
    def observation_type(self) -> EnvObservationType:
        return EnvObservationType.DISCRETE

    @property
    def max_episode_steps(self) -> int:
        return 20

    def call_reset(self) -> np.ndarray:
        self.player_pos = 4
        return np.array(self.player_pos)

    def call_step(self, action_: int) -> Tuple[np.ndarray, float, bool, dict]:
        action = Action(action_)

        if action == Action.LEFT:
            self.player_pos -= 1
        elif action == Action.RIGHT:
            self.player_pos += 1

        if self.field[self.player_pos] == -1:
            return np.array(self.player_pos), -1, True, {}

        if self.field[self.player_pos] == 1:
            return np.array(self.player_pos), 1, True, {}

        return np.array(self.player_pos), self.move_reward, False, {}

    def backup(self) -> Any:
        return self.player_pos

    def restore(self, data: Any) -> None:
        self.player_pos = data

    def render_terminal(self):
        s = ""
        for x in range(len(self.field)):
            if x == self.player_pos:
                s += "P"
            elif self.field[x] == -1:
                s += "X"
            elif self.field[x] == 1:
                s += "G"
            else:
                s += "."
        print(s)<jupyter_output>c:\Users\poco\venv\rl39-gpu\lib\site-packages\flatbuffers\compat.py:19: DeprecationWarning: the imp module is deprecated in favour of importlib; see the module's documentation for alternative uses
  import imp
c:\Users\poco\venv\rl39-gpu\lib\site-packages\keras\utils\image_utils.py:36: DeprecationWarning: NEAREST is deprecated and will be removed in Pillow 10 (2023-07-01). Use Resampling.NEAREST or Dither.NONE instead.
  'nearest': pil_image.NEAREST,
c:\Users\poco\venv\rl39-gpu\lib\site-packages\keras\utils\image_utils.py:37: DeprecationWarning: BILINEAR is deprecated and will be removed in Pillow 10 (2023-07-01). Use Resampling.BILINEAR instead.
  'bilinear': pil_image.BILINEAR,
c:\Users\poco\venv\rl39-gpu\lib\site-packages\keras\utils\image_utils.py:38: DeprecationWarning: BICUBIC is deprecated and will be removed in Pillow 10 (2023-07-01). Use Resampling.BICUBIC instead.
  'bicubic': pil_image.BICUBIC,
c:\Users\poco\venv\rl39-gpu\lib\site-packages\keras\utils\image_utils.py:39: Depr[...]<jupyter_text>'SpaceBase' は現状5種類あります。(コードは `srl.base.env.spaces` を見てください)+ DiscreteSpace  1つの整数を表します。  例えば DiscreteSpace(5) とした場合、0～4 の値を取ります。  + ArrayDiscreteSpace  整数の配列を取ります。  例えば ArrayDiscreteSpace([3, 2]) とした場合、[0, 0] や [2, 1] 等の値を取ります。  + ContinuousSpace  1つの小数を表します。  例えば ContinuousSpace(-1, 1) とした場合、-1～1 の値を取ります。  + ArrayContinuousSpace  小数の配列を取ります。  例えば ArrayContinuousSpace(3, -1, 1) とした場合、[0.1, -0.5, 0.9] 等の値を取ります。  + BoxSpace  numpy配列を指定の範囲内で取り扱います。（np.ndarray型）  例えば BoxSpace(shape=(1, 2), low=-1, high=1) とした場合、[[-0.1, 1.0]] や [[0.1, 0.2] 等の値を取ります。   作成出来たら登録します。  引数は以下です。  ``` pythonid: ユニークな名前  entry_point: __name__ + ":" + クラス名  kwargs: クラス生成時の引数```<jupyter_code>from srl.base.env import registration

registration.register(
    id="MyEnv",
    entry_point=__name__ + ":MyEnv",
    kwargs={
        "move_reward": -0.04,
    },
)<jupyter_output><empty_output><jupyter_text>以下のように実行できます。  'SinglePlayEnvWrapper' を通すとシングルプレイ用のインタフェースで実行できます。<jupyter_code>import srl
from srl.base.env.singleplay_wrapper import SinglePlayEnvWrapper

env = srl.envs.make("MyEnv")
env = SinglePlayEnvWrapper(env)  # change single play interface

state = env.reset()
done = False
total_reward = 0
step = 0
env.render()

while not done:
    action = env.sample()
    state, reward, done, info = env.step(action)
    total_reward += reward
    step += 1
    print(f"step {step}, action {action}, reward {reward}, done {done}")
    env.render()

print(total_reward)<jupyter_output>X...P...G
step 1, action 1, reward -0.04, done False
X....P..G
step 2, action 0, reward -0.04, done False
X...P...G
step 3, action 1, reward -0.04, done False
X....P..G
step 4, action 0, reward -0.04, done False
X...P...G
step 5, action 0, reward -0.04, done False
X..P....G
step 6, action 1, reward -0.04, done False
X...P...G
step 7, action 1, reward -0.04, done False
X....P..G
step 8, action 0, reward -0.04, done False
X...P...G
step 9, action 0, reward -0.04, done False
X..P....G
step 10, action 1, reward -0.04, done False
X...P...G
step 11, action 0, reward -0.04, done False
X..P....G
step 12, action 0, reward -0.04, done False
X.P.....G
step 13, action 1, reward -0.04, done False
X..P....G
step 14, action 0, reward -0.04, done False
X.P.....G
step 15, action 0, reward -0.04, done False
XP......G
step 16, action 0, reward -1, done True
P.......G
-1.6<jupyter_text>以下でテストできます。<jupyter_code>from srl.test import TestEnv

tester = TestEnv()
tester.play_test("MyEnv")<jupyter_output>X...P...G
X..P....G
X.P.....G
X..P....G
X...P...G
X..P....G
X.P.....G
X..P....G
X...P...G
X....P..G
X.....P.G
X......PG
X.......P<jupyter_text>Runnerを使うと以下のように学習できます。<jupyter_code>import srl
from srl.runner import sequence
from srl.runner.callbacks import PrintProgress

config = sequence.Config(
    env_config=srl.envs.Config("MyEnv"),
    rl_config=srl.rl.ql.Config(),  # Q学習
)

# --- train
config.set_train_config(max_episodes=10000, callbacks=[PrintProgress()])
parameter, memory = sequence.train(config)

config.set_play_config(max_episodes=100)
rewards, _, _ = sequence.play(config, parameter)
print("100エピソードの平均結果", np.mean(rewards))

from srl.runner.callbacks import Rendering

render = Rendering(step_stop=False, enable_animation=True)
config.set_play_config(max_episodes=1, callbacks=[render])
rewards, _, _ = sequence.play(config, parameter)

# render.create_anime(fps=1).save("MyEnv.gif")
render.display(fps=1)<jupyter_output>### 0
X...P...G
{'fraction_non_alphanumeric': 0.13919275745001886, 'fraction_numerical': 0.02829121086382497, 'mean_word_length': 3.267167381974249, 'pattern_counts': {'":': 3, '<': 17, '<?xml version=': 0, '>': 33, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '5', 'dir': 'jupyter-structured-clean-dedup', 'id': '136600', 'license': 'MIT', 'n_tokens_mistral': 3362, 'n_tokens_neox': 3169, 'n_words': 653, 'path': 'examples/custom_env.ipynb', 'repo_name': 'pocokhc/simple_rl', 'size': '7953'}