1. 构建自定义Gridworld环境

构建自定义Gridworld环境 学习卡片

任务目标

实现一个4×4网格世界环境,智能体从左上角出发,目标是到达右下角终点。每一步移动获得-1奖励,到达终点获得+10奖励并终止回合。环境需继承gym.Env,实现reset、step、render方法,并正确设置observation_space与action_space,使其可通过标准Gym API调用。

环境依赖

  • Python 3.8+
  • gymnasium(推荐)或gym 0.26+
  • numpy

完整实现

import gymnasium as gym
import numpy as np
from gymnasium import spaces

class GridworldEnv(gym.Env):
    """
    4x4 Gridworld:
    - 状态: 0~15 (row*4+col)
    - 动作: 0=上, 1=右, 2=下, 3=左
    - 奖励: 每步-1, 到达终点+10
    - 终止: 到达状态15
    """
    metadata = {"render_modes": ["human", "ansi"]}

    def __init__(self, render_mode=None):
        super().__init__()
        self.grid_size = 4
        self.n_states = self.grid_size * self.grid_size
        self.start_state = 0
        self.goal_state = self.n_states - 1

        # 动作空间: 0上, 1右, 2下, 3左
        self.action_space = spaces.Discrete(4)
        # 状态空间: 0~15
        self.observation_space = spaces.Discrete(self.n_states)

        assert render_mode is None or render_mode in self.metadata["render_modes"]
        self.render_mode = render_mode

        self.state = None

    def _get_coords(self, state):
        return state // self.grid_size, state % self.grid_size

    def _get_state(self, row, col):
        # 边界约束: 不允许移出网格
        row = max(0, min(row, self.grid_size - 1))
        col = max(0, min(col, self.grid_size - 1))
        return row * self.grid_size + col

    def reset(self, seed=None, options=None):
        super().reset(seed=seed)
        self.state = self.start_state
        if self.render_mode == "human":
            self.render()
        return self.state, {}

    def step(self, action):
        row, col = self._get_coords(self.state)

        if action == 0:      # 上
            row -= 1
        elif action == 1:    # 右
            col += 1
        elif action == 2:    # 下
            row += 1
        elif action == 3:    # 左
            col -= 1
        else:
            raise ValueError(f"Invalid action: {action}")

        # 边界约束(撞墙则停留在原地)
        new_state = self._get_state(row, col)
        self.state = new_state

        terminated = (self.state == self.goal_state)
        reward = 10.0 if terminated else -1.0

        if self.render_mode == "human":
            self.render()

        return self.state, reward, terminated, False, {}

    def render(self):
        if self.render_mode == "ansi":
            return self._render_ansi()
        elif self.render_mode == "human":
            print(self._render_ansi())

    def _render_ansi(self):
        output = ""
        for r in range(self.grid_size):
            row_str = ""
            for c in range(self.grid_size):
                s = r * self.grid_size + c
                if s == self.state:
                    row_str += " A "
                elif s == self.goal_state:
                    row_str += " G "
                else:
                    row_str += " . "
            output += row_str + "\n"
        return output

    def close(self):
        pass

运行与验证

将上述代码保存为gridworld_env.py,然后运行以下验证脚本:

from gridworld_env import GridworldEnv
import gymnasium as gym

# 注册环境(可选,用于gym.make)
gym.register(
    id="Gridworld-v0",
    entry_point="gridworld_env:GridworldEnv",
)

# 方式1: 直接实例化
env = GridworldEnv(render_mode="ansi")
obs, _ = env.reset()
print("初始状态:", obs)

# 执行一步向右移动
obs, reward, terminated, truncated, info = env.step(1)
print(f"状态={obs}, 奖励={reward}, 终止={terminated}")
print(env.render())

# 方式2: 通过gym.make创建
env2 = gym.make("Gridworld-v0", render_mode="ansi")
obs2, _ = env2.reset()
print("gym.make创建成功,初始状态:", obs2)

# 手动走一条路径到终点验证终止逻辑
env3 = GridworldEnv()
obs3, _ = env3.reset()
# 路径: 右(1), 右(1), 下(2), 下(2) -> 到达(2,2)即状态10
# 继续: 右(1), 下(2) -> 到达(3,3)即状态15
for action in [1, 1, 2, 2, 1, 2]:
    obs3, reward3, terminated3, _, _ = env3.step(action)
    print(f"动作={action} -> 状态={obs3}, 奖励={reward3}, 终止={terminated3}")
    if terminated3:
        print("到达终点,回合终止")
        break

通过条件

  • 环境可正常实例化,reset()返回状态0和空字典。
  • step()返回5元组,动作0~3分别对应上、右、下、左移动。
  • 在边界执行移出动作时,状态保持不变(撞墙效果)。
  • 到达状态15时terminated=True且奖励为+10;其他步骤奖励为-1。
  • 通过gym.make("Gridworld-v0")可成功创建环境实例。
  • 运行验证脚本,最后一步动作2后输出终止=True,说明终止逻辑正确。

常见问题

  • 版本兼容:若使用gym(非gymnasium),需将terminated, truncated合并为done,即return self.state, reward, terminated, {},并移除truncated返回值。
  • 注册失败:确保gridworld_env.py在当前工作目录,或已安装为Python模块。
  • 渲染输出:render_mode="ansi"返回字符串,"human"打印到控制台;不设置渲染模式则render()不执行任何操作。