├── examples ├── .gitignore ├── Random_Sampling.py ├── Profile_Performance.py ├── readme.md └── Human_Playing_Commandline.py ├── docs ├── .gitignore ├── masks.png ├── rooms │ ├── Sokoban-v0.png │ ├── Sokoban-v1.png │ ├── Sokoban-v2.png │ ├── Sokoban-v1_1.png │ ├── Sokoban-huge-v0.png │ ├── Sokoban-large-v0.png │ ├── Sokoban-large-v1.png │ ├── Sokoban-large-v2.png │ ├── Sokoban-small-v0.png │ ├── Sokoban-small-v1.png │ ├── TwoPlayer-Sokoban-v0.png │ ├── TwoPlayer-Sokoban-v1.png │ ├── TwoPlayer-Sokoban-v2.png │ ├── TwoPlayer-Sokoban-v3.png │ ├── TwoPlayer-Sokoban-v4.png │ ├── TwoPlayer-Sokoban-v5.png │ ├── FixedTarget-Sokoban-v0.png │ ├── FixedTarget-Sokoban-v1.png │ ├── FixedTarget-Sokoban-v2.png │ ├── FixedTarget-Sokoban-v3.png │ ├── Tiny_World_Sokoban-v0.png │ ├── Tiny_World_Sokoban-v1.png │ ├── Tiny_World_Sokoban-v2.png │ ├── FixedTarget-Sokoban-v2_1.png │ ├── Tiny_World_Sokoban-v1_1.png │ ├── Tiny_World_Sokoban-huge-v0.png │ ├── Tiny_World_Sokoban-large-v0.png │ ├── Tiny_World_Sokoban-large-v1.png │ ├── Tiny_World_Sokoban-large-v2.png │ ├── Tiny_World_Sokoban-small-v0.png │ ├── Tiny_World_Sokoban-small-v1.png │ └── Sokoban-Fixed-Targets-Example.png ├── Animations │ ├── solved_1.gif │ ├── solved_3.gif │ ├── solved_4.gif │ ├── solved_5.gif │ ├── not_solved_2.gif │ ├── TwoPlayer_solved_0.gif │ ├── TwoPlayer_solved_1.gif │ ├── TwoPlayer_solved_2.gif │ ├── pushAndPull_solved_0.gif │ ├── pushAndPull_solved_1.gif │ ├── pushAndPull_solved_2.gif │ ├── fixedTargets_solved_0.gif │ ├── fixedTargets_solved_1.gif │ ├── fixedTargets_solved_2.gif │ ├── fixedTargets_solved_3.gif │ ├── fixedTargets_solved_4.gif │ └── fixedTargets_solved_5.gif ├── SocialMedia │ ├── preview_raw.xcf │ └── preview_simple.png └── variations │ ├── FixedTargets.md │ ├── README.md │ ├── PushAndPull.md │ ├── Boxoban.md │ └── TwoPlayer.md ├── gym_sokoban ├── envs │ ├── surface │ │ ├── box.png │ │ ├── floor.png │ │ ├── player.png │ │ ├── wall.png │ │ ├── box_target.png │ │ ├── box_on_target.png │ │ ├── multibox │ │ │ ├── box0.png │ │ │ ├── box1.png │ │ │ ├── box2.png │ │ │ ├── box3.png │ │ │ ├── box0_target.png │ │ │ ├── box1_target.png │ │ │ ├── box2_target.png │ │ │ ├── box3_target.png │ │ │ ├── box0_on_target.png │ │ │ ├── box1_on_target.png │ │ │ ├── box2_on_target.png │ │ │ ├── box3_on_target.png │ │ │ ├── box0_on_wrong_target.png │ │ │ ├── box1_on_wrong_target.png │ │ │ ├── box2_on_wrong_target.png │ │ │ └── box3_on_wrong_target.png │ │ ├── raw │ │ │ ├── boxes │ │ │ │ ├── box.xcf │ │ │ │ ├── box0.xcf │ │ │ │ ├── box1.xcf │ │ │ │ ├── box2.xcf │ │ │ │ ├── box3.xcf │ │ │ │ ├── box0_target.xcf │ │ │ │ ├── box1_target.xcf │ │ │ │ ├── box2_target.xcf │ │ │ │ ├── box3_target.xcf │ │ │ │ ├── box_target.xcf │ │ │ │ ├── box_on_target.xcf │ │ │ │ ├── box0_on_target.xcf │ │ │ │ ├── box1_on_target.xcf │ │ │ │ ├── box2_on_target.xcf │ │ │ │ ├── box3_on_target.xcf │ │ │ │ ├── box0_on_wrong_target.xcf │ │ │ │ ├── box1_on_wrong_target.xcf │ │ │ │ ├── box2_on_wrong_target.xcf │ │ │ │ └── box3_on_wrong_target.xcf │ │ │ ├── player │ │ │ │ ├── player.xcf │ │ │ │ ├── player2.xcf │ │ │ │ ├── player2_on_target.xcf │ │ │ │ └── player_on_target.xcf │ │ │ └── blank_surface │ │ │ │ ├── floor.xcf │ │ │ │ └── wall.xcf │ │ ├── player_on_target.png │ │ ├── tiny_world │ │ │ ├── box.png │ │ │ ├── floor.png │ │ │ ├── wall.png │ │ │ ├── player.png │ │ │ ├── box_target.png │ │ │ ├── box_on_target.png │ │ │ └── player_on_target.png │ │ └── multiplayer │ │ │ ├── player0.png │ │ │ ├── player1.png │ │ │ ├── player0_on_target.png │ │ │ └── player1_on_target.png │ ├── __init__.py │ ├── sokoban_env_fixed_targets.py │ ├── available_envs.json │ ├── sokoban_env_pull.py │ ├── sokoban_env_two_player.py │ ├── boxoban_env.py │ ├── sokoban_env.py │ ├── room_utils.py │ ├── render_utils.py │ └── sokoban_env_variations.py └── __init__.py ├── .gitignore ├── deploy.sh ├── LICENSE ├── setup.py └── README.md /examples/.gitignore: -------------------------------------------------------------------------------- 1 | images/ 2 | -------------------------------------------------------------------------------- /docs/.gitignore: -------------------------------------------------------------------------------- 1 | /SingleFrames 2 | /toGif 3 | run* 4 | 5 | -------------------------------------------------------------------------------- /docs/masks.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpSchrader/gym-sokoban/HEAD/docs/masks.png -------------------------------------------------------------------------------- /docs/rooms/Sokoban-v0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpSchrader/gym-sokoban/HEAD/docs/rooms/Sokoban-v0.png -------------------------------------------------------------------------------- /docs/rooms/Sokoban-v1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpSchrader/gym-sokoban/HEAD/docs/rooms/Sokoban-v1.png -------------------------------------------------------------------------------- /docs/rooms/Sokoban-v2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpSchrader/gym-sokoban/HEAD/docs/rooms/Sokoban-v2.png -------------------------------------------------------------------------------- /docs/rooms/Sokoban-v1_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpSchrader/gym-sokoban/HEAD/docs/rooms/Sokoban-v1_1.png -------------------------------------------------------------------------------- /docs/Animations/solved_1.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpSchrader/gym-sokoban/HEAD/docs/Animations/solved_1.gif -------------------------------------------------------------------------------- /docs/Animations/solved_3.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpSchrader/gym-sokoban/HEAD/docs/Animations/solved_3.gif -------------------------------------------------------------------------------- /docs/Animations/solved_4.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpSchrader/gym-sokoban/HEAD/docs/Animations/solved_4.gif -------------------------------------------------------------------------------- /docs/Animations/solved_5.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpSchrader/gym-sokoban/HEAD/docs/Animations/solved_5.gif -------------------------------------------------------------------------------- /docs/rooms/Sokoban-huge-v0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpSchrader/gym-sokoban/HEAD/docs/rooms/Sokoban-huge-v0.png -------------------------------------------------------------------------------- /docs/Animations/not_solved_2.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpSchrader/gym-sokoban/HEAD/docs/Animations/not_solved_2.gif -------------------------------------------------------------------------------- /docs/SocialMedia/preview_raw.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpSchrader/gym-sokoban/HEAD/docs/SocialMedia/preview_raw.xcf -------------------------------------------------------------------------------- /docs/rooms/Sokoban-large-v0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpSchrader/gym-sokoban/HEAD/docs/rooms/Sokoban-large-v0.png -------------------------------------------------------------------------------- /docs/rooms/Sokoban-large-v1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpSchrader/gym-sokoban/HEAD/docs/rooms/Sokoban-large-v1.png -------------------------------------------------------------------------------- /docs/rooms/Sokoban-large-v2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpSchrader/gym-sokoban/HEAD/docs/rooms/Sokoban-large-v2.png -------------------------------------------------------------------------------- /docs/rooms/Sokoban-small-v0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpSchrader/gym-sokoban/HEAD/docs/rooms/Sokoban-small-v0.png -------------------------------------------------------------------------------- /docs/rooms/Sokoban-small-v1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpSchrader/gym-sokoban/HEAD/docs/rooms/Sokoban-small-v1.png -------------------------------------------------------------------------------- /gym_sokoban/envs/surface/box.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpSchrader/gym-sokoban/HEAD/gym_sokoban/envs/surface/box.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.egg-info 2 | *.pyc 3 | .idea 4 | /logs 5 | /notebooks 6 | *~ 7 | .sokoban_cache/ 8 | dist/ 9 | build/ 10 | -------------------------------------------------------------------------------- /docs/SocialMedia/preview_simple.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpSchrader/gym-sokoban/HEAD/docs/SocialMedia/preview_simple.png -------------------------------------------------------------------------------- /docs/rooms/TwoPlayer-Sokoban-v0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpSchrader/gym-sokoban/HEAD/docs/rooms/TwoPlayer-Sokoban-v0.png -------------------------------------------------------------------------------- /docs/rooms/TwoPlayer-Sokoban-v1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpSchrader/gym-sokoban/HEAD/docs/rooms/TwoPlayer-Sokoban-v1.png -------------------------------------------------------------------------------- /docs/rooms/TwoPlayer-Sokoban-v2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpSchrader/gym-sokoban/HEAD/docs/rooms/TwoPlayer-Sokoban-v2.png -------------------------------------------------------------------------------- /docs/rooms/TwoPlayer-Sokoban-v3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpSchrader/gym-sokoban/HEAD/docs/rooms/TwoPlayer-Sokoban-v3.png -------------------------------------------------------------------------------- /docs/rooms/TwoPlayer-Sokoban-v4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpSchrader/gym-sokoban/HEAD/docs/rooms/TwoPlayer-Sokoban-v4.png -------------------------------------------------------------------------------- /docs/rooms/TwoPlayer-Sokoban-v5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpSchrader/gym-sokoban/HEAD/docs/rooms/TwoPlayer-Sokoban-v5.png -------------------------------------------------------------------------------- /gym_sokoban/envs/surface/floor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpSchrader/gym-sokoban/HEAD/gym_sokoban/envs/surface/floor.png -------------------------------------------------------------------------------- /gym_sokoban/envs/surface/player.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpSchrader/gym-sokoban/HEAD/gym_sokoban/envs/surface/player.png -------------------------------------------------------------------------------- /gym_sokoban/envs/surface/wall.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpSchrader/gym-sokoban/HEAD/gym_sokoban/envs/surface/wall.png -------------------------------------------------------------------------------- /docs/rooms/FixedTarget-Sokoban-v0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpSchrader/gym-sokoban/HEAD/docs/rooms/FixedTarget-Sokoban-v0.png -------------------------------------------------------------------------------- /docs/rooms/FixedTarget-Sokoban-v1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpSchrader/gym-sokoban/HEAD/docs/rooms/FixedTarget-Sokoban-v1.png -------------------------------------------------------------------------------- /docs/rooms/FixedTarget-Sokoban-v2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpSchrader/gym-sokoban/HEAD/docs/rooms/FixedTarget-Sokoban-v2.png -------------------------------------------------------------------------------- /docs/rooms/FixedTarget-Sokoban-v3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpSchrader/gym-sokoban/HEAD/docs/rooms/FixedTarget-Sokoban-v3.png -------------------------------------------------------------------------------- /docs/rooms/Tiny_World_Sokoban-v0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpSchrader/gym-sokoban/HEAD/docs/rooms/Tiny_World_Sokoban-v0.png -------------------------------------------------------------------------------- /docs/rooms/Tiny_World_Sokoban-v1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpSchrader/gym-sokoban/HEAD/docs/rooms/Tiny_World_Sokoban-v1.png -------------------------------------------------------------------------------- /docs/rooms/Tiny_World_Sokoban-v2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpSchrader/gym-sokoban/HEAD/docs/rooms/Tiny_World_Sokoban-v2.png -------------------------------------------------------------------------------- /docs/Animations/TwoPlayer_solved_0.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpSchrader/gym-sokoban/HEAD/docs/Animations/TwoPlayer_solved_0.gif -------------------------------------------------------------------------------- /docs/Animations/TwoPlayer_solved_1.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpSchrader/gym-sokoban/HEAD/docs/Animations/TwoPlayer_solved_1.gif -------------------------------------------------------------------------------- /docs/Animations/TwoPlayer_solved_2.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpSchrader/gym-sokoban/HEAD/docs/Animations/TwoPlayer_solved_2.gif -------------------------------------------------------------------------------- /docs/Animations/pushAndPull_solved_0.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpSchrader/gym-sokoban/HEAD/docs/Animations/pushAndPull_solved_0.gif -------------------------------------------------------------------------------- /docs/Animations/pushAndPull_solved_1.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpSchrader/gym-sokoban/HEAD/docs/Animations/pushAndPull_solved_1.gif -------------------------------------------------------------------------------- /docs/Animations/pushAndPull_solved_2.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpSchrader/gym-sokoban/HEAD/docs/Animations/pushAndPull_solved_2.gif -------------------------------------------------------------------------------- /docs/rooms/FixedTarget-Sokoban-v2_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpSchrader/gym-sokoban/HEAD/docs/rooms/FixedTarget-Sokoban-v2_1.png -------------------------------------------------------------------------------- /docs/rooms/Tiny_World_Sokoban-v1_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpSchrader/gym-sokoban/HEAD/docs/rooms/Tiny_World_Sokoban-v1_1.png -------------------------------------------------------------------------------- /gym_sokoban/envs/surface/box_target.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpSchrader/gym-sokoban/HEAD/gym_sokoban/envs/surface/box_target.png -------------------------------------------------------------------------------- /docs/Animations/fixedTargets_solved_0.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpSchrader/gym-sokoban/HEAD/docs/Animations/fixedTargets_solved_0.gif -------------------------------------------------------------------------------- /docs/Animations/fixedTargets_solved_1.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpSchrader/gym-sokoban/HEAD/docs/Animations/fixedTargets_solved_1.gif -------------------------------------------------------------------------------- /docs/Animations/fixedTargets_solved_2.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpSchrader/gym-sokoban/HEAD/docs/Animations/fixedTargets_solved_2.gif -------------------------------------------------------------------------------- /docs/Animations/fixedTargets_solved_3.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpSchrader/gym-sokoban/HEAD/docs/Animations/fixedTargets_solved_3.gif -------------------------------------------------------------------------------- /docs/Animations/fixedTargets_solved_4.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpSchrader/gym-sokoban/HEAD/docs/Animations/fixedTargets_solved_4.gif -------------------------------------------------------------------------------- /docs/Animations/fixedTargets_solved_5.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpSchrader/gym-sokoban/HEAD/docs/Animations/fixedTargets_solved_5.gif -------------------------------------------------------------------------------- /docs/rooms/Tiny_World_Sokoban-huge-v0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpSchrader/gym-sokoban/HEAD/docs/rooms/Tiny_World_Sokoban-huge-v0.png -------------------------------------------------------------------------------- /docs/rooms/Tiny_World_Sokoban-large-v0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpSchrader/gym-sokoban/HEAD/docs/rooms/Tiny_World_Sokoban-large-v0.png -------------------------------------------------------------------------------- /docs/rooms/Tiny_World_Sokoban-large-v1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpSchrader/gym-sokoban/HEAD/docs/rooms/Tiny_World_Sokoban-large-v1.png -------------------------------------------------------------------------------- /docs/rooms/Tiny_World_Sokoban-large-v2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpSchrader/gym-sokoban/HEAD/docs/rooms/Tiny_World_Sokoban-large-v2.png -------------------------------------------------------------------------------- /docs/rooms/Tiny_World_Sokoban-small-v0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpSchrader/gym-sokoban/HEAD/docs/rooms/Tiny_World_Sokoban-small-v0.png -------------------------------------------------------------------------------- /docs/rooms/Tiny_World_Sokoban-small-v1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpSchrader/gym-sokoban/HEAD/docs/rooms/Tiny_World_Sokoban-small-v1.png -------------------------------------------------------------------------------- /gym_sokoban/envs/surface/box_on_target.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpSchrader/gym-sokoban/HEAD/gym_sokoban/envs/surface/box_on_target.png -------------------------------------------------------------------------------- /gym_sokoban/envs/surface/multibox/box0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpSchrader/gym-sokoban/HEAD/gym_sokoban/envs/surface/multibox/box0.png -------------------------------------------------------------------------------- /gym_sokoban/envs/surface/multibox/box1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpSchrader/gym-sokoban/HEAD/gym_sokoban/envs/surface/multibox/box1.png -------------------------------------------------------------------------------- /gym_sokoban/envs/surface/multibox/box2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpSchrader/gym-sokoban/HEAD/gym_sokoban/envs/surface/multibox/box2.png -------------------------------------------------------------------------------- /gym_sokoban/envs/surface/multibox/box3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpSchrader/gym-sokoban/HEAD/gym_sokoban/envs/surface/multibox/box3.png -------------------------------------------------------------------------------- /gym_sokoban/envs/surface/raw/boxes/box.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpSchrader/gym-sokoban/HEAD/gym_sokoban/envs/surface/raw/boxes/box.xcf -------------------------------------------------------------------------------- /docs/rooms/Sokoban-Fixed-Targets-Example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpSchrader/gym-sokoban/HEAD/docs/rooms/Sokoban-Fixed-Targets-Example.png -------------------------------------------------------------------------------- /gym_sokoban/envs/surface/player_on_target.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpSchrader/gym-sokoban/HEAD/gym_sokoban/envs/surface/player_on_target.png -------------------------------------------------------------------------------- /gym_sokoban/envs/surface/raw/boxes/box0.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpSchrader/gym-sokoban/HEAD/gym_sokoban/envs/surface/raw/boxes/box0.xcf -------------------------------------------------------------------------------- /gym_sokoban/envs/surface/raw/boxes/box1.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpSchrader/gym-sokoban/HEAD/gym_sokoban/envs/surface/raw/boxes/box1.xcf -------------------------------------------------------------------------------- /gym_sokoban/envs/surface/raw/boxes/box2.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpSchrader/gym-sokoban/HEAD/gym_sokoban/envs/surface/raw/boxes/box2.xcf -------------------------------------------------------------------------------- /gym_sokoban/envs/surface/raw/boxes/box3.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpSchrader/gym-sokoban/HEAD/gym_sokoban/envs/surface/raw/boxes/box3.xcf -------------------------------------------------------------------------------- /gym_sokoban/envs/surface/tiny_world/box.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpSchrader/gym-sokoban/HEAD/gym_sokoban/envs/surface/tiny_world/box.png -------------------------------------------------------------------------------- /gym_sokoban/envs/surface/tiny_world/floor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpSchrader/gym-sokoban/HEAD/gym_sokoban/envs/surface/tiny_world/floor.png -------------------------------------------------------------------------------- /gym_sokoban/envs/surface/tiny_world/wall.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpSchrader/gym-sokoban/HEAD/gym_sokoban/envs/surface/tiny_world/wall.png -------------------------------------------------------------------------------- /gym_sokoban/envs/surface/raw/player/player.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpSchrader/gym-sokoban/HEAD/gym_sokoban/envs/surface/raw/player/player.xcf -------------------------------------------------------------------------------- /gym_sokoban/envs/surface/raw/player/player2.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpSchrader/gym-sokoban/HEAD/gym_sokoban/envs/surface/raw/player/player2.xcf -------------------------------------------------------------------------------- /gym_sokoban/envs/surface/tiny_world/player.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpSchrader/gym-sokoban/HEAD/gym_sokoban/envs/surface/tiny_world/player.png -------------------------------------------------------------------------------- /gym_sokoban/envs/surface/multibox/box0_target.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpSchrader/gym-sokoban/HEAD/gym_sokoban/envs/surface/multibox/box0_target.png -------------------------------------------------------------------------------- /gym_sokoban/envs/surface/multibox/box1_target.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpSchrader/gym-sokoban/HEAD/gym_sokoban/envs/surface/multibox/box1_target.png -------------------------------------------------------------------------------- /gym_sokoban/envs/surface/multibox/box2_target.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpSchrader/gym-sokoban/HEAD/gym_sokoban/envs/surface/multibox/box2_target.png -------------------------------------------------------------------------------- /gym_sokoban/envs/surface/multibox/box3_target.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpSchrader/gym-sokoban/HEAD/gym_sokoban/envs/surface/multibox/box3_target.png -------------------------------------------------------------------------------- /gym_sokoban/envs/surface/multiplayer/player0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpSchrader/gym-sokoban/HEAD/gym_sokoban/envs/surface/multiplayer/player0.png -------------------------------------------------------------------------------- /gym_sokoban/envs/surface/multiplayer/player1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpSchrader/gym-sokoban/HEAD/gym_sokoban/envs/surface/multiplayer/player1.png -------------------------------------------------------------------------------- /gym_sokoban/envs/surface/raw/boxes/box0_target.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpSchrader/gym-sokoban/HEAD/gym_sokoban/envs/surface/raw/boxes/box0_target.xcf -------------------------------------------------------------------------------- /gym_sokoban/envs/surface/raw/boxes/box1_target.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpSchrader/gym-sokoban/HEAD/gym_sokoban/envs/surface/raw/boxes/box1_target.xcf -------------------------------------------------------------------------------- /gym_sokoban/envs/surface/raw/boxes/box2_target.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpSchrader/gym-sokoban/HEAD/gym_sokoban/envs/surface/raw/boxes/box2_target.xcf -------------------------------------------------------------------------------- /gym_sokoban/envs/surface/raw/boxes/box3_target.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpSchrader/gym-sokoban/HEAD/gym_sokoban/envs/surface/raw/boxes/box3_target.xcf -------------------------------------------------------------------------------- /gym_sokoban/envs/surface/raw/boxes/box_target.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpSchrader/gym-sokoban/HEAD/gym_sokoban/envs/surface/raw/boxes/box_target.xcf -------------------------------------------------------------------------------- /gym_sokoban/envs/surface/tiny_world/box_target.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpSchrader/gym-sokoban/HEAD/gym_sokoban/envs/surface/tiny_world/box_target.png -------------------------------------------------------------------------------- /gym_sokoban/envs/surface/multibox/box0_on_target.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpSchrader/gym-sokoban/HEAD/gym_sokoban/envs/surface/multibox/box0_on_target.png -------------------------------------------------------------------------------- /gym_sokoban/envs/surface/multibox/box1_on_target.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpSchrader/gym-sokoban/HEAD/gym_sokoban/envs/surface/multibox/box1_on_target.png -------------------------------------------------------------------------------- /gym_sokoban/envs/surface/multibox/box2_on_target.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpSchrader/gym-sokoban/HEAD/gym_sokoban/envs/surface/multibox/box2_on_target.png -------------------------------------------------------------------------------- /gym_sokoban/envs/surface/multibox/box3_on_target.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpSchrader/gym-sokoban/HEAD/gym_sokoban/envs/surface/multibox/box3_on_target.png -------------------------------------------------------------------------------- /gym_sokoban/envs/surface/raw/blank_surface/floor.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpSchrader/gym-sokoban/HEAD/gym_sokoban/envs/surface/raw/blank_surface/floor.xcf -------------------------------------------------------------------------------- /gym_sokoban/envs/surface/raw/blank_surface/wall.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpSchrader/gym-sokoban/HEAD/gym_sokoban/envs/surface/raw/blank_surface/wall.xcf -------------------------------------------------------------------------------- /gym_sokoban/envs/surface/raw/boxes/box_on_target.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpSchrader/gym-sokoban/HEAD/gym_sokoban/envs/surface/raw/boxes/box_on_target.xcf -------------------------------------------------------------------------------- /gym_sokoban/envs/surface/raw/boxes/box0_on_target.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpSchrader/gym-sokoban/HEAD/gym_sokoban/envs/surface/raw/boxes/box0_on_target.xcf -------------------------------------------------------------------------------- /gym_sokoban/envs/surface/raw/boxes/box1_on_target.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpSchrader/gym-sokoban/HEAD/gym_sokoban/envs/surface/raw/boxes/box1_on_target.xcf -------------------------------------------------------------------------------- /gym_sokoban/envs/surface/raw/boxes/box2_on_target.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpSchrader/gym-sokoban/HEAD/gym_sokoban/envs/surface/raw/boxes/box2_on_target.xcf -------------------------------------------------------------------------------- /gym_sokoban/envs/surface/raw/boxes/box3_on_target.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpSchrader/gym-sokoban/HEAD/gym_sokoban/envs/surface/raw/boxes/box3_on_target.xcf -------------------------------------------------------------------------------- /gym_sokoban/envs/surface/tiny_world/box_on_target.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpSchrader/gym-sokoban/HEAD/gym_sokoban/envs/surface/tiny_world/box_on_target.png -------------------------------------------------------------------------------- /deploy.sh: -------------------------------------------------------------------------------- 1 | python setup.py sdist bdist_wheel 2 | 3 | # To production: twine upload dist/* 4 | python -m twine upload --repository-url https://test.pypi.org/legacy/ dist/* -------------------------------------------------------------------------------- /gym_sokoban/envs/surface/raw/player/player2_on_target.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpSchrader/gym-sokoban/HEAD/gym_sokoban/envs/surface/raw/player/player2_on_target.xcf -------------------------------------------------------------------------------- /gym_sokoban/envs/surface/raw/player/player_on_target.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpSchrader/gym-sokoban/HEAD/gym_sokoban/envs/surface/raw/player/player_on_target.xcf -------------------------------------------------------------------------------- /gym_sokoban/envs/surface/tiny_world/player_on_target.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpSchrader/gym-sokoban/HEAD/gym_sokoban/envs/surface/tiny_world/player_on_target.png -------------------------------------------------------------------------------- /gym_sokoban/envs/surface/multibox/box0_on_wrong_target.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpSchrader/gym-sokoban/HEAD/gym_sokoban/envs/surface/multibox/box0_on_wrong_target.png -------------------------------------------------------------------------------- /gym_sokoban/envs/surface/multibox/box1_on_wrong_target.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpSchrader/gym-sokoban/HEAD/gym_sokoban/envs/surface/multibox/box1_on_wrong_target.png -------------------------------------------------------------------------------- /gym_sokoban/envs/surface/multibox/box2_on_wrong_target.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpSchrader/gym-sokoban/HEAD/gym_sokoban/envs/surface/multibox/box2_on_wrong_target.png -------------------------------------------------------------------------------- /gym_sokoban/envs/surface/multibox/box3_on_wrong_target.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpSchrader/gym-sokoban/HEAD/gym_sokoban/envs/surface/multibox/box3_on_wrong_target.png -------------------------------------------------------------------------------- /gym_sokoban/envs/surface/multiplayer/player0_on_target.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpSchrader/gym-sokoban/HEAD/gym_sokoban/envs/surface/multiplayer/player0_on_target.png -------------------------------------------------------------------------------- /gym_sokoban/envs/surface/multiplayer/player1_on_target.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpSchrader/gym-sokoban/HEAD/gym_sokoban/envs/surface/multiplayer/player1_on_target.png -------------------------------------------------------------------------------- /gym_sokoban/envs/surface/raw/boxes/box0_on_wrong_target.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpSchrader/gym-sokoban/HEAD/gym_sokoban/envs/surface/raw/boxes/box0_on_wrong_target.xcf -------------------------------------------------------------------------------- /gym_sokoban/envs/surface/raw/boxes/box1_on_wrong_target.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpSchrader/gym-sokoban/HEAD/gym_sokoban/envs/surface/raw/boxes/box1_on_wrong_target.xcf -------------------------------------------------------------------------------- /gym_sokoban/envs/surface/raw/boxes/box2_on_wrong_target.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpSchrader/gym-sokoban/HEAD/gym_sokoban/envs/surface/raw/boxes/box2_on_wrong_target.xcf -------------------------------------------------------------------------------- /gym_sokoban/envs/surface/raw/boxes/box3_on_wrong_target.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpSchrader/gym-sokoban/HEAD/gym_sokoban/envs/surface/raw/boxes/box3_on_wrong_target.xcf -------------------------------------------------------------------------------- /gym_sokoban/envs/__init__.py: -------------------------------------------------------------------------------- 1 | from gym_sokoban.envs.sokoban_env import SokobanEnv, ACTION_LOOKUP, CHANGE_COORDINATES 2 | from gym_sokoban.envs import room_utils 3 | from gym_sokoban.envs.sokoban_env_variations import * 4 | 5 | 6 | -------------------------------------------------------------------------------- /gym_sokoban/__init__.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import pkg_resources 3 | import json 4 | from gym.envs.registration import register 5 | 6 | logger = logging.getLogger(__name__) 7 | 8 | resource_package = __name__ 9 | env_json = pkg_resources.resource_filename(resource_package, '/'.join(('envs', 'available_envs.json'))) 10 | 11 | with open(env_json) as f: 12 | 13 | envs = json.load(f) 14 | 15 | for env in envs: 16 | register( 17 | id=env["id"], 18 | entry_point=env["entry_point"] 19 | ) 20 | -------------------------------------------------------------------------------- /examples/Random_Sampling.py: -------------------------------------------------------------------------------- 1 | import gym 2 | import gym_sokoban 3 | import time 4 | 5 | # Before you can make a Sokoban Environment you need to call: 6 | # import gym_sokoban 7 | # This import statement registers all Sokoban environments 8 | # provided by this package 9 | env_name = 'Sokoban-v0' 10 | env = gym.make(env_name) 11 | 12 | ACTION_LOOKUP = env.unwrapped.get_action_lookup() 13 | print("Created environment: {}".format(env_name)) 14 | 15 | for i_episode in range(1):#20 16 | observation = env.reset() 17 | 18 | for t in range(100):#100 19 | env.render(mode='human') 20 | action = env.action_space.sample() 21 | 22 | # Sleep makes the actions visible for users 23 | time.sleep(1) 24 | observation, reward, done, info = env.step(action) 25 | 26 | print(ACTION_LOOKUP[action], reward, done, info) 27 | if done: 28 | print("Episode finished after {} timesteps".format(t+1)) 29 | env.render() 30 | break 31 | 32 | env.close() 33 | 34 | time.sleep(10) 35 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Max-Philipp Schrader 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /examples/Profile_Performance.py: -------------------------------------------------------------------------------- 1 | import cProfile 2 | import gym 3 | import gym_sokoban 4 | import time 5 | import argparse 6 | 7 | parser = argparse.ArgumentParser(description='Run environment with random selected actions.') 8 | parser.add_argument('--rounds', '-r', metavar='rounds', type=int, 9 | help='number of rounds to play (default: 20)', default=20) 10 | parser.add_argument('--env', '-e', metavar='env', 11 | help='Environment to load (default: Sokoban-v0)', default='Sokoban-v0') 12 | 13 | 14 | args = parser.parse_args() 15 | env_name = args.env 16 | n = args.rounds 17 | 18 | cProfile.run('gym.make("{}")'.format(env_name), sort='time') 19 | 20 | env = gym.make(env_name) 21 | 22 | start = time.time() 23 | for i in range(n): 24 | print('Reset {}/{}'.format(i+1, n)) 25 | env.reset() 26 | 27 | end = time.time() 28 | delta = end-start 29 | hours, remainder = divmod(delta, 3600) 30 | minutes, seconds = divmod(remainder, 60) 31 | print('Done.\nReset {} times in {}:{}:{}'.format(n, int(hours), int(minutes), int(seconds))) 32 | hours, remainder = divmod(delta*1.0/n, 3600) 33 | minutes, seconds = divmod(remainder, 60) 34 | print('Avg {}:{}:{}'.format(int(hours), int(minutes), seconds)) 35 | -------------------------------------------------------------------------------- /examples/readme.md: -------------------------------------------------------------------------------- 1 | ## Examples 2 | 3 | This readme walks you through all steps to run an external gym environment. 4 | The example will use this repository [gym-sokoban](https://github.com/mpSchrader/gym-sokoban/) as this is part of this repository, but never the less this works similar with other external gym environments. 5 | 6 | ### 1 Install the additional package 7 | You need to clone the repository and install the package as follows: 8 | ```Bash 9 | git clone git@github.com:mpSchrader/gym-sokoban.git 10 | cd gym-sokoban 11 | pip install -e . 12 | ``` 13 | ### 2 Import packages in your code 14 | 15 | To use an external gym environment you allways need to import the corresponding package along with the regular gym package. 16 | ```Python 17 | import gym 18 | import gym_sokoban 19 | ``` 20 | 21 | ### 3 Load the environment 22 | From now on everything is as you are used to it. You can simply make the environment, render it, perform actions and so on. 23 | 24 | ```Python 25 | env = gym.make('Sokoban-v0') 26 | 27 | env.render(mode='human') 28 | 29 | action = env.action_space.sample() 30 | observation, reward, done, info = env.step(action) 31 | ``` 32 | Now that you are all set with the preparations enjoy the external environment. 33 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup, find_packages 2 | 3 | with open("README.md", "r") as fh: 4 | long_description = fh.read() 5 | long_description = long_description.replace('(/gym_sokoban/envs/', '(https://github.com/mpSchrader/gym-sokoban/gym_sokoban/envs/') 6 | long_description = long_description.replace('(/docs/rooms', '(https://github.com/mpSchrader/gym-sokoban/blob/master/docs/rooms') 7 | long_description = long_description.replace('(/example', '(https://github.com/mpSchrader/gym-sokoban/example') 8 | #print(long_description) 9 | 10 | 11 | setup( 12 | name='gym_sokoban', 13 | version='0.0.5', 14 | author="Max-Philipp Schrader", 15 | description='Sokoban environment for OpenAI Gym', 16 | long_description=long_description, 17 | long_description_content_type="text/markdown", 18 | url="https://github.com/mpSchrader/gym-sokoban", 19 | install_requires=['gym>=0.2.3', 'numpy>=1.14.1', 'tqdm>=4.32.1', 'imageio>=2.3.0', 'requests>=2.22.0'], 20 | packages=find_packages(), 21 | package_data={ 22 | 'gym_sokoban': ['envs/*', 'envs/surface/*', 'envs/surface/*/*'], 23 | }, 24 | include_package_data=True, 25 | classifiers=[ 26 | "Programming Language :: Python :: 3", 27 | "License :: OSI Approved :: MIT License", 28 | "Operating System :: OS Independent", 29 | ], 30 | ) 31 | -------------------------------------------------------------------------------- /docs/variations/FixedTargets.md: -------------------------------------------------------------------------------- 1 | # Variation: Fixed Targets 2 | 3 | | Example Game 1 | Example Game 2 | Example Game 3 | 4 | | :---: | :---: | :---: 5 | | ![Game 1](/docs/Animations/fixedTargets_solved_0.gif?raw=true) | ![Game 2](/docs/Animations/fixedTargets_solved_4.gif?raw=true) | ![Game 3](/docs/Animations/fixedTargets_solved_5.gif?raw=true) | 6 | 7 | 8 | ## 1. Idea 9 | In this variation the player not only has to push all boxes on the targets, but also push every box on specific target. 10 | The boxes and targets are color coded, such that for example the blue box needs to end up on the blue target. 11 | 12 | ## 2. Rules 13 | The rules in general stay the same, which means the player has to push all boxes on the targets. There are two changes. 14 | First a player only gets a reward if a box ends up on its specific target. 15 | Second the game end when every box are placed on their specific targets. 16 | 17 | ## 3. Room Configurations 18 | Similar to the regular Sokoban there exist multiple room configurations. 19 | 20 | | Room Id | Grid-Size | Pixels | #Boxes | Example | 21 | | --- | :---: | :---: | :---: | :---: | 22 | | FixedTarget-Sokoban-v0 | 10x10 | 160x160 | 3 | ![FixedTarget-Sokoban-v0](/docs/rooms/FixedTarget-Sokoban-v0.png) | 23 | | FixedTarget-Sokoban-v1 | 10x10 | 160x160 | 4 | ![FixedTarget-Sokoban-v1](/docs/rooms/FixedTarget-Sokoban-v1.png) | 24 | | FixedTarget-Sokoban-v2 | 7x7 | 112x112 | 2 | ![FixedTarget-Sokoban-v2](/docs/rooms/FixedTarget-Sokoban-v2.png) | 25 | | FixedTarget-Sokoban-v3 | 7x7 | 112x112 | 3 | ![FixedTarget-Sokoban-v3](/docs/rooms/FixedTarget-Sokoban-v3.png) | 26 | 27 | -------------------------------------------------------------------------------- /docs/variations/README.md: -------------------------------------------------------------------------------- 1 | # Variations 2 | 3 | | Fixed Targets | Two Player | Push&Pull | 4 | | :---: | :---: | :---: 5 | | ![Game 1](/docs/Animations/fixedTargets_solved_0.gif?raw=true) | ![Game 2](/docs/Animations/TwoPlayer_solved_2.gif?raw=true) | ![Game 3](/docs/Animations/pushAndPull_solved_0.gif?raw=true) | 6 | 7 | 8 | Besides the regular game of Sokoban, this repository implements or will implement variations, which might make the game easier or more complicated. Except noted differently the variations do not implement a Tiny-World version. 9 | 10 | ## Variations 11 | | Variation | Summary | Expected Difficulty | Example | Tiny World | Status | Details | 12 | | --- | :---: | :---: | :---: | :---: | :---: | :---: | 13 | | Fixed Targets | Every box has to be pushed on the target with the same color. | More difficult | ![Fixed-Targets](/docs/rooms/Sokoban-Fixed-Targets-Example.png) | Yes | implemented | [ReadMe](/docs/variations/FixedTargets.md) | 14 | | Multiple Player | There are two players in the room. Every round one of the two players can be used. There is no order of moves between the two players. | More difficult | ![TwoPlayer](/docs/rooms/TwoPlayer-Sokoban-v2.png) | Yes | planned | [ReadMe](/docs/variations/TwoPlayer.md) | 15 | | Push&Pull | The player can not only push the boxes, but also pull them. Therefor no more irreversible moves exist. | Easier | ![PushAndPull-Targets](/docs/rooms/Sokoban-v1.png) | Yes | implemented | [ReadMe](/docs/variations/FixedTargets.md) | 16 | 17 | ## Tiny World 18 | 19 | Currently the tiny world variations are only available for the regular Sokoban game. 20 | -------------------------------------------------------------------------------- /docs/variations/PushAndPull.md: -------------------------------------------------------------------------------- 1 | # Variation: Push&Pull 2 | 3 | | Example Game 1 | Example Game 2 | Example Game 3 | 4 | | :---: | :---: | :---: 5 | | ![Game 1](/docs/Animations/pushAndPull_solved_0.gif?raw=true) | ![Game 2](/docs/Animations/pushAndPull_solved_1.gif?raw=true) | ![Game 3](/docs/Animations/pushAndPull_solved_2.gif?raw=true) | 6 | 7 | 8 | ## 1. Idea 9 | This variation follows the same rules as the regular game with on exception: 10 | The player can not only push boxes, but also pull the boxes. 11 | Because of that, the number of available actions increase to 13. The action mapping is the following. 12 | 13 | 14 | | Action | ID | 15 | | -------- | :---: | 16 | | No Operation | 0 | 17 | | Push Up | 1 | 18 | | Push Down | 2 | 19 | | Push Left | 3 | 20 | | Push Right | 4 | 21 | | Move Up | 5 | 22 | | Move Down | 6 | 23 | | Move Left | 7 | 24 | | Move Right | 8 | 25 | | Pull Up | 9 | 26 | | Pull Down | 10 | 27 | | Pull Left | 11 | 28 | | Pull Right | 12 | 29 | 30 | The conditions for winning do not change. Also the room generation algorithm is the same as before. 31 | 32 | ## 2. Room Configurations 33 | Similar to the regular game there exist multiple room configurations, but TinyWorld configurations. 34 | 35 | | Room Id | Grid-Size | Pixels | #Boxes | Example | 36 | | --- | :---: | :---: | :---: | :---: | 37 | | PushAndPull-Sokoban-v0 | 10x10 | 160x160 | 3 | ![PushAndPull-Sokoban-v0](/docs/rooms/Sokoban-v0.png) | 38 | | PushAndPull-Sokoban-v1 | 10x10 | 160x160 | 4 | ![PushAndPull-Sokoban-v1](/docs/rooms/Sokoban-v1.png) | 39 | | PushAndPull-Sokoban-v2 | 7x7 | 112x112 | 2 | ![PushAndPull-Sokoban-v2](/docs/rooms/Sokoban-small-v0.png) | 40 | | PushAndPull-Sokoban-v3 | 7x7 | 112x112 | 3 | ![PushAndPull-Sokoban-v3](/docs/rooms/Sokoban-small-v1.png) | 41 | | PushAndPull-Sokoban-v4 | 13x11 | 208x176 | 4 | ![PushAndPull-Sokoban-v4](/docs/rooms/Sokoban-large-v0.png) | 42 | | PushAndPull-Sokoban-v5 | 13x11 | 208x176 | 5 | ![PushAndPull-Sokoban-v5](/docs/rooms/Sokoban-large-v1.png) | 43 | 44 | 45 | -------------------------------------------------------------------------------- /docs/variations/Boxoban.md: -------------------------------------------------------------------------------- 1 | # Variation: Boxoban 2 | 3 | | Example Game 1 | 4 | | :---: | 5 | | ![Game 1](/docs/Animations/solved_3.gif?raw=true) | 6 | 7 | 8 | ## 1. Idea 9 | Instead of generating a new level on every reset operation, a pregenerated level is choosen randomly. When Boxoban is run for the first time the levels are downloaded from DeepMinds [Github repository](https://github.com/deepmind/boxoban-levels) and stored in the folder _.sokoban_cache_. 10 | 11 | In case you use the Boxoban levels for your research, the authors of the boxoban-levels repository ask you to cite their work as follows: 12 | ``` 13 | @misc{boxobanlevels, 14 | author = {Arthur Guez, Mehdi Mirza, Karol Gregor, Rishabh Kabra, Sebastien Racaniere, Theophane Weber, David Raposo, Adam Santoro, Laurent Orseau, Tom Eccles, Greg Wayne, David Silver, Timothy Lillicrap, Victor Valdes}, 15 | title = {An investigation of Model-free planning: boxoban levels}, 16 | howpublished= {https://github.com/deepmind/boxoban-levels/}, 17 | year = "2018", 18 | } 19 | ``` 20 | Dedending on your use case, please consider the [licence](https://github.com/deepmind/boxoban-levels/blob/master/LICENSE) of DeepMinds repository. 21 | 22 | ## 2. Rules 23 | Same rules as the regular Sokoban game. 24 | 25 | ## 3. Room Configurations 26 | Through the API we provide access to 'unfiltered' and 'medium' levels. The unflitered levels are split into a train, test, and validation set. Whereas the medium levels are only split into train and validation. For more details see DeepMind's [README.md](https://github.com/deepmind/boxoban-levels/blob/master/README.md). 27 | Of course all configurations can be rendered as TinyWorld. 28 | 29 | | Room Id | Grid-Size| Grid-Size | Pixels | #Boxes | 30 | | --- | :---: | :---: | :---: | :---: | 31 | | Boxoban-Train-v0 | unfiltered | 10x10 | 112x112 | 4 | ![Boxoban-v0](/docs/rooms/TwoPlayer-Sokoban-v3.png) | 32 | | Boxoban-Test-v0 | unfiltered | 10x10 | 112x112 | 4 | ![Boxoban-v0](/docs/rooms/TwoPlayer-Sokoban-v3.png) | 33 | | Boxoban-Val-v0 | unfiltered | 10x10 | 112x112 | 4 | ![Boxoban-v0](/docs/rooms/TwoPlayer-Sokoban-v3.png) | 34 | | Boxoban-Train-v1 | medium | 10x10 | 112x112 | 4 | ![Boxoban-v1](/docs/rooms/TwoPlayer-Sokoban-v3.png) | 35 | | Boxoban-Val-v1 | medium | 10x10 | 112x112 | 4 | ![Boxoban-v1](/docs/rooms/TwoPlayer-Sokoban-v3.png) | 36 | -------------------------------------------------------------------------------- /docs/variations/TwoPlayer.md: -------------------------------------------------------------------------------- 1 | # Variation: Two Player 2 | 3 | | Example Game 1 | Example Game 2 | Example Game 3 | 4 | | :---: | :---: | :---: 5 | | ![Game 1](/docs/Animations/TwoPlayer_solved_0.gif?raw=true) | ![Game 2](/docs/Animations/TwoPlayer_solved_1.gif?raw=true) | ![Game 3](/docs/Animations/TwoPlayer_solved_2.gif?raw=true) | 6 | 7 | 8 | ## 1. Idea 9 | This variation contains two different players in the room, which both can be moved like a regular single player. 10 | 11 | 12 | ## 2. Rules 13 | Every round the user can choose which avatar (player) should be used. 14 | The player is chosen by the action number. 15 | The action 0 is the NOOP action, which is a void step changing nothing in the environment. 16 | All actions in the range from 1 to 8 are for player 1, the green avatar. 17 | The remaining actions relate to player 2, the blue avatar. 18 | 19 | | Action (Player 1) | ID | | Action (Player 2) | ID | 20 | | ----------------- | :---: |--| ----------------- | :---: | 21 | | No Operation | 0 | | | | 22 | | P1: Push Up | 1 | | P2: Push Up | 9 | 23 | | P1: Push Down | 2 | | P2: Push Down | 10 | 24 | | P1: Push Left | 3 | | P2: Push Left | 11 | 25 | | P1: Push Right | 4 | | P2: Push Right | 12 | 26 | | P1: Move Up | 5 | | P2: Move Up | 13 | 27 | | P1: Move Down | 6 | | P2: Move Down | 14 | 28 | | P1: Move Left | 7 | | P2: Move Left | 15 | 29 | | P1: Move Right | 8 | | P2: Move Right | 16 | 30 | 31 | 32 | The game ending rules as well as the reward are similar to regular game. 33 | 34 | ## 3. Room Configurations 35 | Similar to the regular Sokoban there exist multiple room configurations. 36 | Of course all configurations can be rendered as TinyWorld. 37 | 38 | | Room Id | Grid-Size | Pixels | #Boxes | Example | 39 | | --- | :---: | :---: | :---: | :---: | 40 | | TwoPlayer-Sokoban-v0 | 7x7 | 160x160 | 2 | ![TwoPlayer-Sokoban-v0](/docs/rooms/TwoPlayer-Sokoban-v0.png) | 41 | | TwoPlayer-Sokoban-v1 | 7x7 | 160x160 | 3 | ![TwoPlayer-Sokoban-v1](/docs/rooms/TwoPlayer-Sokoban-v1.png) | 42 | | TwoPlayer-Sokoban-v2 | 10x10 | 112x112 | 3 | ![TwoPlayer-Sokoban-v2](/docs/rooms/TwoPlayer-Sokoban-v2.png) | 43 | | TwoPlayer-Sokoban-v3 | 10x10 | 112x112 | 4 | ![TwoPlayer-Sokoban-v3](/docs/rooms/TwoPlayer-Sokoban-v3.png) | 44 | | TwoPlayer-Sokoban-v4 | 13x11 | 160x160 | 3 | ![TwoPlayer-Sokoban-v4](/docs/rooms/TwoPlayer-Sokoban-v4.png) | 45 | | TwoPlayer-Sokoban-v5 | 13x11 | 160x160 | 4 | ![TwoPlayer-Sokoban-v5](/docs/rooms/TwoPlayer-Sokoban-v5.png) | 46 | -------------------------------------------------------------------------------- /gym_sokoban/envs/sokoban_env_fixed_targets.py: -------------------------------------------------------------------------------- 1 | from .sokoban_env import SokobanEnv 2 | from .render_utils import room_to_rgb_FT, room_to_tiny_world_rgb_FT 3 | from gym.spaces import Box 4 | 5 | 6 | class FixedTargetsSokobanEnv(SokobanEnv): 7 | 8 | def __init__(self, 9 | dim_room=(10, 10), 10 | max_steps=120, 11 | num_boxes=3, 12 | num_gen_steps=None): 13 | 14 | super(FixedTargetsSokobanEnv, self).__init__(dim_room, max_steps, num_boxes, num_gen_steps) 15 | screen_height, screen_width = (dim_room[0] * 16, dim_room[1] * 16) 16 | self.observation_space = Box(low=0, high=255, shape=(screen_height, screen_width, 3)) 17 | self.boxes_are_on_target = [False] * num_boxes 18 | 19 | _ = self.reset() 20 | 21 | def get_image(self, mode, scale=1): 22 | 23 | if mode.startswith('tiny_'): 24 | img = room_to_tiny_world_rgb_FT(self.room_state, self.box_mapping, self.room_fixed, scale=scale) 25 | else: 26 | img = room_to_rgb_FT(self.room_state, self.box_mapping, self.room_fixed) 27 | 28 | return img 29 | 30 | def step(self, action, observation_mode='rgb_array'): 31 | 32 | observation, self.reward_last, done, info = super(FixedTargetsSokobanEnv, self).step(action, observation_mode) 33 | 34 | return observation, self.reward_last, done, info 35 | 36 | def _calc_reward(self): 37 | self._update_box_mapping() 38 | 39 | # Every step a small penalty is given, This ensures 40 | # that short solutions have a higher reward. 41 | self.reward_last = self.penalty_for_step 42 | 43 | for b in range(len(self.boxes_are_on_target)): 44 | 45 | previous_state = self.boxes_are_on_target[b] 46 | 47 | # Calculate new state 48 | box_id = list(self.box_mapping.keys())[b] 49 | new_state = self.box_mapping[box_id] == box_id 50 | 51 | if previous_state and not new_state: 52 | # Box was pushed of its target 53 | self.reward_last += self.penalty_box_off_target 54 | elif not previous_state and new_state: 55 | # box was pushed on its target 56 | self.reward_last += self.reward_box_on_target 57 | 58 | self.boxes_are_on_target[b] = new_state 59 | 60 | def _update_box_mapping(self): 61 | if self.new_box_position is not None: 62 | box_index = list(self.box_mapping.values()).index(self.old_box_position) 63 | box_id = list(self.box_mapping.keys())[box_index] 64 | self.box_mapping[box_id] = self.new_box_position 65 | 66 | def _check_if_all_boxes_on_target(self): 67 | 68 | for key in self.box_mapping.keys(): 69 | if not key == self.box_mapping[key]: 70 | return False 71 | 72 | return True 73 | -------------------------------------------------------------------------------- /gym_sokoban/envs/available_envs.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id": "Sokoban-v0", 4 | "entry_point": "gym_sokoban.envs:SokobanEnv1" 5 | }, 6 | { 7 | "id": "Sokoban-v1", 8 | "entry_point": "gym_sokoban.envs:SokobanEnv" 9 | }, 10 | { 11 | "id": "Sokoban-v2", 12 | "entry_point": "gym_sokoban.envs:SokobanEnv2" 13 | }, 14 | { 15 | "id": "Sokoban-small-v0", 16 | "entry_point": "gym_sokoban.envs:SokobanEnv_Small0" 17 | }, 18 | { 19 | "id": "Sokoban-small-v1", 20 | "entry_point": "gym_sokoban.envs:SokobanEnv_Small1" 21 | }, 22 | { 23 | "id": "Sokoban-large-v0", 24 | "entry_point": "gym_sokoban.envs:SokobanEnv_Large0" 25 | }, 26 | { 27 | "id": "Sokoban-large-v1", 28 | "entry_point": "gym_sokoban.envs:SokobanEnv_Large1" 29 | }, 30 | { 31 | "id": "Sokoban-large-v2", 32 | "entry_point": "gym_sokoban.envs:SokobanEnv_Large2" 33 | }, 34 | { 35 | "id": "Sokoban-huge-v0", 36 | "entry_point": "gym_sokoban.envs:SokobanEnv_Huge0" 37 | }, 38 | { 39 | "id": "FixedTarget-Sokoban-v0", 40 | "entry_point": "gym_sokoban.envs:FixedTargets_Env_v0" 41 | }, 42 | { 43 | "id": "FixedTarget-Sokoban-v1", 44 | "entry_point": "gym_sokoban.envs:FixedTargets_Env_v1" 45 | }, 46 | { 47 | "id": "FixedTarget-Sokoban-v2", 48 | "entry_point": "gym_sokoban.envs:FixedTargets_Env_v2" 49 | }, 50 | { 51 | "id": "FixedTarget-Sokoban-v3", 52 | "entry_point": "gym_sokoban.envs:FixedTargets_Env_v3" 53 | }, 54 | { 55 | "id": "PushAndPull-Sokoban-v0", 56 | "entry_point": "gym_sokoban.envs:PushAndPull_Env_v0" 57 | }, 58 | { 59 | "id": "PushAndPull-Sokoban-v1", 60 | "entry_point": "gym_sokoban.envs:PushAndPull_Env_v1" 61 | }, 62 | { 63 | "id": "PushAndPull-Sokoban-v2", 64 | "entry_point": "gym_sokoban.envs:PushAndPull_Env_v2" 65 | }, 66 | { 67 | "id": "PushAndPull-Sokoban-v3", 68 | "entry_point": "gym_sokoban.envs:PushAndPull_Env_v3" 69 | }, 70 | { 71 | "id": "PushAndPull-Sokoban-v4", 72 | "entry_point": "gym_sokoban.envs:PushAndPull_Env_v4" 73 | }, 74 | { 75 | "id": "PushAndPull-Sokoban-v5", 76 | "entry_point": "gym_sokoban.envs:PushAndPull_Env_v5" 77 | }, 78 | { 79 | "id": "TwoPlayer-Sokoban-v0", 80 | "entry_point": "gym_sokoban.envs:TwoPlayer_Env0" 81 | }, 82 | { 83 | "id": "TwoPlayer-Sokoban-v1", 84 | "entry_point": "gym_sokoban.envs:TwoPlayer_Env1" 85 | }, 86 | { 87 | "id": "TwoPlayer-Sokoban-v2", 88 | "entry_point": "gym_sokoban.envs:TwoPlayer_Env2" 89 | }, 90 | { 91 | "id": "TwoPlayer-Sokoban-v3", 92 | "entry_point": "gym_sokoban.envs:TwoPlayer_Env3" 93 | }, 94 | { 95 | "id": "TwoPlayer-Sokoban-v4", 96 | "entry_point": "gym_sokoban.envs:TwoPlayer_Env4" 97 | }, 98 | { 99 | "id": "TwoPlayer-Sokoban-v5", 100 | "entry_point": "gym_sokoban.envs:TwoPlayer_Env5" 101 | }, 102 | { 103 | "id": "Boxoban-Train-v0", 104 | "entry_point": "gym_sokoban.envs:Boxban_Env0" 105 | }, 106 | { 107 | "id": "Boxoban-Test-v0", 108 | "entry_point": "gym_sokoban.envs:Boxban_Env0_test" 109 | }, 110 | { 111 | "id": "Boxoban-Val-v0", 112 | "entry_point": "gym_sokoban.envs:Boxban_Env0_val" 113 | }, 114 | { 115 | "id": "Boxoban-Train-v1", 116 | "entry_point": "gym_sokoban.envs:Boxban_Env1" 117 | }, 118 | { 119 | "id": "Boxoban-Val-v1", 120 | "entry_point": "gym_sokoban.envs:Boxban_Env1_val" 121 | } 122 | ] -------------------------------------------------------------------------------- /examples/Human_Playing_Commandline.py: -------------------------------------------------------------------------------- 1 | import gym 2 | import gym_sokoban 3 | import time 4 | from PIL import Image 5 | import numpy as np 6 | import argparse 7 | import os 8 | 9 | parser = argparse.ArgumentParser(description='Run environment with random selected actions.') 10 | parser.add_argument('--rounds', '-r', metavar='rounds', type=int, 11 | help='number of rounds to play (default: 1)', default=1) 12 | parser.add_argument('--steps', '-s', metavar='steps', type=int, 13 | help='maximum number of steps to be played each round (default: 300)', default=300) 14 | parser.add_argument('--env', '-e', metavar='env', 15 | help='Environment to load (default: Sokoban-v0)', default='Sokoban-v0') 16 | parser.add_argument('--save', action='store_true', 17 | help='Save images of single steps') 18 | parser.add_argument('--gifs', action='store_true', 19 | help='Generate Gif files from images') 20 | parser.add_argument('--render_mode', '-m', metavar='render_mode', 21 | help='Render Mode (default: human)', default='human') 22 | 23 | args = parser.parse_args() 24 | env_name = args.env 25 | n_rounds = args.rounds 26 | n_steps = args.steps 27 | save_images = args.save or args.gifs 28 | generate_gifs = args.gifs 29 | render_mode = args.render_mode 30 | observation_mode = 'tiny_rgb_array' if 'tiny' in render_mode else 'rgb_array' 31 | scale_image = 16 32 | 33 | # Creating target directory if images are to be stored 34 | if save_images and not os.path.exists('images'): 35 | try: 36 | os.makedirs('images') 37 | except OSError: 38 | print('Error: Creating images target directory. ') 39 | 40 | ts = time.time() 41 | env = gym.make(env_name) 42 | ACTION_LOOKUP = env.unwrapped.get_action_lookup() 43 | print("Created environment: {}".format(env_name)) 44 | 45 | 46 | def print_available_actions(): 47 | """ 48 | Prints all available actions nicely formatted.. 49 | :return: 50 | """ 51 | available_actions_list = [] 52 | for i in range(len(ACTION_LOOKUP)): 53 | available_actions_list.append( 54 | 'Key: {} - Action: {}'.format(i, ACTION_LOOKUP[i]) 55 | ) 56 | display_actions = '\n'.join(available_actions_list) 57 | print() 58 | print('Action out of Range!') 59 | print('Available Actions:\n{}'.format(display_actions)) 60 | print() 61 | 62 | 63 | for i_episode in range(n_rounds): 64 | print('Starting new game!') 65 | observation = env.reset() 66 | 67 | for t in range(n_steps): 68 | env.render(render_mode, scale=scale_image) 69 | 70 | action = input('Select action: ') 71 | try: 72 | action = int(action) 73 | 74 | if not action in range(len(ACTION_LOOKUP)): 75 | raise ValueError 76 | 77 | except ValueError: 78 | print_available_actions() 79 | continue 80 | 81 | observation, reward, done, info = env.step(action, observation_mode=observation_mode) 82 | print(ACTION_LOOKUP[action], reward, done, info) 83 | print(len(observation), len(observation[0]), len(observation[0][0])) 84 | if save_images: 85 | img = Image.fromarray(np.array(env.render(render_mode, scale=scale_image)), 'RGB') 86 | img.save(os.path.join('images', 'observation_{}_{}.png'.format(i_episode, t))) 87 | 88 | if done: 89 | print("Episode finished after {} timesteps".format(t+1)) 90 | env.render(render_mode, scale=scale_image) 91 | break 92 | 93 | if generate_gifs: 94 | print('') 95 | import imageio 96 | 97 | with imageio.get_writer(os.path.join('images', 'round_{}.gif'.format(i_episode)), mode='I', fps=1) as writer: 98 | 99 | for t in range(n_steps): 100 | try: 101 | 102 | filename = os.path.join('images', 'observation_{}_{}.png'.format(i_episode, t)) 103 | image = imageio.imread(filename) 104 | writer.append_data(image) 105 | 106 | except: 107 | pass 108 | 109 | env.close() 110 | time.sleep(10) 111 | -------------------------------------------------------------------------------- /gym_sokoban/envs/sokoban_env_pull.py: -------------------------------------------------------------------------------- 1 | from .sokoban_env import SokobanEnv, CHANGE_COORDINATES 2 | from gym.spaces import Box 3 | from gym.spaces.discrete import Discrete 4 | 5 | 6 | class PushAndPullSokobanEnv(SokobanEnv): 7 | 8 | def __init__(self, 9 | dim_room=(10, 10), 10 | max_steps=120, 11 | num_boxes=3, 12 | num_gen_steps=None): 13 | 14 | super(PushAndPullSokobanEnv, self).__init__(dim_room, max_steps, num_boxes, num_gen_steps) 15 | screen_height, screen_width = (dim_room[0] * 16, dim_room[1] * 16) 16 | self.observation_space = Box(low=0, high=255, shape=(screen_height, screen_width, 3)) 17 | self.boxes_are_on_target = [False] * num_boxes 18 | self.action_space = Discrete(len(ACTION_LOOKUP)) 19 | 20 | _ = self.reset() 21 | 22 | def step(self, action, observation_mode='rgb_array'): 23 | assert action in ACTION_LOOKUP 24 | 25 | self.num_env_steps += 1 26 | 27 | self.new_box_position = None 28 | self.old_box_position = None 29 | 30 | moved_box = False 31 | if action == 0: 32 | moved_player = False 33 | 34 | # All push actions are in the range of [0, 3] 35 | if action < 5: 36 | moved_player, moved_box = self._push(action) 37 | 38 | elif action < 9: 39 | moved_player = self._move(action) 40 | 41 | else: 42 | moved_player, moved_box = self._pull(action) 43 | 44 | self._calc_reward() 45 | 46 | done = self._check_if_done() 47 | 48 | # Convert the observation to RGB frame 49 | observation = self.render(mode=observation_mode) 50 | 51 | info = { 52 | "action.name": ACTION_LOOKUP[action], 53 | "action.moved_player": moved_player, 54 | "action.moved_box": moved_box, 55 | } 56 | if done: 57 | info["maxsteps_used"] = self._check_if_maxsteps() 58 | info["all_boxes_on_target"] = self._check_if_all_boxes_on_target() 59 | 60 | return observation, self.reward_last, done, info 61 | 62 | def _pull(self, action): 63 | """ 64 | Moves the player to the next field, if it is not occupied. 65 | :param action: 66 | :return: Boolean, indicating a change of the room's state 67 | """ 68 | change = CHANGE_COORDINATES[(action - 1) % 4] 69 | new_position = self.player_position + change 70 | current_position = self.player_position.copy() 71 | pull_content_position = self.player_position - change 72 | 73 | # Move player if the field in the moving direction is either 74 | # an empty field or an empty box target. 75 | if self.room_state[new_position[0], new_position[1]] in [1, 2]: 76 | self.player_position = new_position 77 | self.room_state[(new_position[0], new_position[1])] = 5 78 | self.room_state[current_position[0], current_position[1]] = \ 79 | self.room_fixed[current_position[0], current_position[1]] 80 | 81 | box_next_to_player = self.room_state[pull_content_position[0], pull_content_position[1]] in [3, 4] 82 | if box_next_to_player: 83 | # Move Box 84 | box_type = 4 85 | if self.room_fixed[current_position[0], current_position[1]] == 2: 86 | box_type = 3 87 | self.room_state[current_position[0], current_position[1]] = box_type 88 | self.room_state[pull_content_position[0], pull_content_position[1]] = \ 89 | self.room_fixed[pull_content_position[0], pull_content_position[1]] 90 | 91 | return True, box_next_to_player 92 | 93 | return False, False 94 | 95 | def get_action_lookup(self): 96 | return ACTION_LOOKUP 97 | 98 | def get_action_meanings(self): 99 | return ACTION_LOOKUP 100 | 101 | 102 | ACTION_LOOKUP = { 103 | 0: 'no operation', 104 | 1: 'push up', 105 | 2: 'push down', 106 | 3: 'push left', 107 | 4: 'push right', 108 | 5: 'move up', 109 | 6: 'move down', 110 | 7: 'move left', 111 | 8: 'move right', 112 | 9: 'pull up', 113 | 10: 'pull down', 114 | 11: 'pull left', 115 | 12: 'pull right', 116 | } 117 | 118 | -------------------------------------------------------------------------------- /gym_sokoban/envs/sokoban_env_two_player.py: -------------------------------------------------------------------------------- 1 | from .sokoban_env import SokobanEnv, CHANGE_COORDINATES 2 | from gym.spaces import Box 3 | from gym.spaces.discrete import Discrete 4 | from .render_utils import room_to_rgb, room_to_tiny_world_rgb, color_player_two, color_tiny_player_two 5 | import numpy as np 6 | 7 | 8 | class TwoPlayerSokobanEnv(SokobanEnv): 9 | 10 | def __init__(self, 11 | dim_room=(10, 10), 12 | max_steps=120, 13 | num_boxes=3, 14 | num_gen_steps=None): 15 | 16 | super(TwoPlayerSokobanEnv, self).__init__(dim_room, max_steps, num_boxes, num_gen_steps, reset=False) 17 | screen_height, screen_width = (dim_room[0] * 16, dim_room[1] * 16) 18 | self.observation_space = Box(low=0, high=255, shape=(screen_height, screen_width, 3)) 19 | self.boxes_are_on_target = [False] * num_boxes 20 | self.action_space = Discrete(len(ACTION_LOOKUP)) 21 | self.player_position = [] 22 | self.player_positions = {0: [0,0], 1: [1,1]} 23 | 24 | _ = self.reset(second_player=True) 25 | 26 | def reset(self, render_mode='rgb_array',second_player=True): 27 | super(TwoPlayerSokobanEnv, self).reset(second_player=second_player) 28 | 29 | self.player_positions = { 30 | 0: np.argwhere(self.room_state == 5)[0], 31 | 1: np.argwhere(self.room_state == 5)[1] 32 | } 33 | 34 | return self.render(mode=render_mode) 35 | 36 | def step(self, action, observation_mode='rgb_array'): 37 | assert action in ACTION_LOOKUP 38 | 39 | self.num_env_steps += 1 40 | 41 | self.new_box_position = None 42 | self.old_box_position = None 43 | 44 | active_player = 0 45 | if action > 8: 46 | active_player = 1 47 | 48 | self.player_position = self.player_positions[active_player] 49 | 50 | player_action = (action-1) % 8 51 | 52 | if action == 0: 53 | moved_player = False 54 | moved_box = False 55 | active_player = -1 56 | 57 | # All push actions are in the range of [0, 3] 58 | elif player_action < 4: 59 | moved_player, moved_box = self._push(player_action + 1) 60 | 61 | elif player_action < 8: 62 | moved_player = self._move(player_action + 1) 63 | moved_box = False 64 | 65 | self.player_positions[active_player] = self.player_position 66 | 67 | self._calc_reward() 68 | 69 | done = self._check_if_done() 70 | 71 | # Convert the observation to RGB frame 72 | observation = self.render(mode=observation_mode) 73 | 74 | info = { 75 | "action.name": ACTION_LOOKUP[action], 76 | "action.moved_player": moved_player, 77 | "action.moved_box": moved_box, 78 | "action,active_player": active_player 79 | } 80 | if done: 81 | info["maxsteps_used"] = self._check_if_maxsteps() 82 | info["all_boxes_on_target"] = self._check_if_all_boxes_on_target() 83 | 84 | return observation, self.reward_last, done, info 85 | 86 | def get_image(self, mode, scale=1): 87 | 88 | if mode.startswith('tiny_'): 89 | img = room_to_tiny_world_rgb(self.room_state, self.room_fixed, scale=scale) 90 | img = color_tiny_player_two(img, self.player_positions[1], self.room_fixed, scale=scale) 91 | else: 92 | img = room_to_rgb(self.room_state, self.room_fixed) 93 | img = color_player_two(img, self.player_positions[1], self.room_fixed) 94 | 95 | return img 96 | 97 | def get_action_lookup(self): 98 | return ACTION_LOOKUP 99 | 100 | def get_action_meanings(self): 101 | return ACTION_LOOKUP 102 | 103 | 104 | ACTION_LOOKUP = { 105 | 0: 'no operation', 106 | 1: 'P1: push up', 107 | 2: 'P1: push down', 108 | 3: 'P1: push left', 109 | 4: 'P1: push right', 110 | 5: 'P1: move up', 111 | 6: 'P1: move down', 112 | 7: 'P1: move left', 113 | 8: 'P1: move right', 114 | 9: 'P2: push up', 115 | 10: 'P2: push down', 116 | 11: 'P2: push left', 117 | 12: 'P2: push right', 118 | 13: 'P2: move up', 119 | 14: 'P2: move down', 120 | 15: 'P2: move left', 121 | 16: 'P2: move right' 122 | } 123 | 124 | -------------------------------------------------------------------------------- /gym_sokoban/envs/boxoban_env.py: -------------------------------------------------------------------------------- 1 | from .sokoban_env import SokobanEnv 2 | from .render_utils import room_to_rgb 3 | import os 4 | from os import listdir 5 | from os.path import isfile, join 6 | import requests 7 | import zipfile 8 | from tqdm import tqdm 9 | import random 10 | import numpy as np 11 | 12 | class BoxobanEnv(SokobanEnv): 13 | num_boxes = 4 14 | dim_room=(10, 10) 15 | 16 | def __init__(self, 17 | max_steps=120, 18 | difficulty='unfiltered', split='train'): 19 | self.difficulty = difficulty 20 | self.split = split 21 | self.verbose = False 22 | super(BoxobanEnv, self).__init__(self.dim_room, max_steps, self.num_boxes, None) 23 | 24 | 25 | def reset(self): 26 | self.cache_path = '.sokoban_cache' 27 | self.train_data_dir = os.path.join(self.cache_path, 'boxoban-levels-master', self.difficulty, self.split) 28 | 29 | if not os.path.exists(self.cache_path): 30 | 31 | url = "https://github.com/deepmind/boxoban-levels/archive/master.zip" 32 | 33 | if self.verbose: 34 | print('Boxoban: Pregenerated levels not downloaded.') 35 | print('Starting download from "{}"'.format(url)) 36 | 37 | response = requests.get(url, stream=True) 38 | 39 | if response.status_code != 200: 40 | raise "Could not download levels from {}. If this problem occurs consistantly please report the bug under https://github.com/mpSchrader/gym-sokoban/issues. ".format(url) 41 | 42 | os.makedirs(self.cache_path) 43 | path_to_zip_file = os.path.join(self.cache_path, 'boxoban_levels-master.zip') 44 | with open(path_to_zip_file, 'wb') as handle: 45 | for data in tqdm(response.iter_content()): 46 | handle.write(data) 47 | 48 | zip_ref = zipfile.ZipFile(path_to_zip_file, 'r') 49 | zip_ref.extractall(self.cache_path) 50 | zip_ref.close() 51 | 52 | self.select_room() 53 | 54 | self.num_env_steps = 0 55 | self.reward_last = 0 56 | self.boxes_on_target = 0 57 | 58 | starting_observation = room_to_rgb(self.room_state, self.room_fixed) 59 | 60 | return starting_observation 61 | 62 | def select_room(self): 63 | 64 | generated_files = [f for f in listdir(self.train_data_dir) if isfile(join(self.train_data_dir, f))] 65 | source_file = join(self.train_data_dir, random.choice(generated_files)) 66 | 67 | maps = [] 68 | current_map = [] 69 | 70 | with open(source_file, 'r') as sf: 71 | for line in sf.readlines(): 72 | if ';' in line and current_map: 73 | maps.append(current_map) 74 | current_map = [] 75 | if '#' == line[0]: 76 | current_map.append(line.strip()) 77 | 78 | maps.append(current_map) 79 | 80 | selected_map = random.choice(maps) 81 | 82 | if self.verbose: 83 | print('Selected Level from File "{}"'.format(source_file)) 84 | 85 | self.room_fixed, self.room_state, self.box_mapping = self.generate_room(selected_map) 86 | 87 | 88 | def generate_room(self, select_map): 89 | room_fixed = [] 90 | room_state = [] 91 | 92 | targets = [] 93 | boxes = [] 94 | for row in select_map: 95 | room_f = [] 96 | room_s = [] 97 | 98 | for e in row: 99 | if e == '#': 100 | room_f.append(0) 101 | room_s.append(0) 102 | 103 | elif e == '@': 104 | self.player_position = np.array([len(room_fixed), len(room_f)]) 105 | room_f.append(1) 106 | room_s.append(5) 107 | 108 | 109 | elif e == '$': 110 | boxes.append((len(room_fixed), len(room_f))) 111 | room_f.append(1) 112 | room_s.append(4) 113 | 114 | elif e == '.': 115 | targets.append((len(room_fixed), len(room_f))) 116 | room_f.append(2) 117 | room_s.append(2) 118 | 119 | else: 120 | room_f.append(1) 121 | room_s.append(1) 122 | 123 | room_fixed.append(room_f) 124 | room_state.append(room_s) 125 | 126 | 127 | # used for replay in room generation, unused here because pre-generated levels 128 | box_mapping = {} 129 | 130 | return np.array(room_fixed), np.array(room_state), box_mapping 131 | 132 | 133 | 134 | -------------------------------------------------------------------------------- /gym_sokoban/envs/sokoban_env.py: -------------------------------------------------------------------------------- 1 | import gym 2 | from gym.utils import seeding 3 | from gym.spaces.discrete import Discrete 4 | from gym.spaces import Box 5 | from .room_utils import generate_room 6 | from .render_utils import room_to_rgb, room_to_tiny_world_rgb 7 | import numpy as np 8 | 9 | 10 | class SokobanEnv(gym.Env): 11 | metadata = { 12 | 'render.modes': ['human', 'rgb_array', 'tiny_human', 'tiny_rgb_array', 'raw'], 13 | 'render_modes': ['human', 'rgb_array', 'tiny_human', 'tiny_rgb_array', 'raw'] 14 | } 15 | 16 | def __init__(self, 17 | dim_room=(10, 10), 18 | max_steps=120, 19 | num_boxes=4, 20 | num_gen_steps=None, 21 | reset=True): 22 | 23 | # General Configuration 24 | self.dim_room = dim_room 25 | if num_gen_steps == None: 26 | self.num_gen_steps = int(1.7 * (dim_room[0] + dim_room[1])) 27 | else: 28 | self.num_gen_steps = num_gen_steps 29 | 30 | self.num_boxes = num_boxes 31 | self.boxes_on_target = 0 32 | 33 | # Penalties and Rewards 34 | self.penalty_for_step = -0.1 35 | self.penalty_box_off_target = -1 36 | self.reward_box_on_target = 1 37 | self.reward_finished = 10 38 | self.reward_last = 0 39 | 40 | # Other Settings 41 | self.viewer = None 42 | self.max_steps = max_steps 43 | self.action_space = Discrete(len(ACTION_LOOKUP)) 44 | screen_height, screen_width = (dim_room[0] * 16, dim_room[1] * 16) 45 | self.observation_space = Box(low=0, high=255, shape=(screen_height, screen_width, 3), dtype=np.uint8) 46 | 47 | if reset: 48 | # Initialize Room 49 | _ = self.reset() 50 | 51 | def seed(self, seed=None): 52 | self.np_random, seed = seeding.np_random(seed) 53 | return [seed] 54 | 55 | def step(self, action, observation_mode='rgb_array'): 56 | assert action in ACTION_LOOKUP 57 | assert observation_mode in ['rgb_array', 'tiny_rgb_array', 'raw'] 58 | 59 | self.num_env_steps += 1 60 | 61 | self.new_box_position = None 62 | self.old_box_position = None 63 | 64 | moved_box = False 65 | 66 | if action == 0: 67 | moved_player = False 68 | 69 | # All push actions are in the range of [0, 3] 70 | elif action < 5: 71 | moved_player, moved_box = self._push(action) 72 | 73 | else: 74 | moved_player = self._move(action) 75 | 76 | self._calc_reward() 77 | 78 | done = self._check_if_done() 79 | 80 | # Convert the observation to RGB frame 81 | observation = self.render(mode=observation_mode) 82 | 83 | info = { 84 | "action.name": ACTION_LOOKUP[action], 85 | "action.moved_player": moved_player, 86 | "action.moved_box": moved_box, 87 | } 88 | if done: 89 | info["maxsteps_used"] = self._check_if_maxsteps() 90 | info["all_boxes_on_target"] = self._check_if_all_boxes_on_target() 91 | 92 | return observation, self.reward_last, done, info 93 | 94 | def _push(self, action): 95 | """ 96 | Perform a push, if a box is adjacent in the right direction. 97 | If no box, can be pushed, try to move. 98 | :param action: 99 | :return: Boolean, indicating a change of the room's state 100 | """ 101 | change = CHANGE_COORDINATES[(action - 1) % 4] 102 | new_position = self.player_position + change 103 | current_position = self.player_position.copy() 104 | 105 | # No push, if the push would get the box out of the room's grid 106 | new_box_position = new_position + change 107 | if new_box_position[0] >= self.room_state.shape[0] \ 108 | or new_box_position[1] >= self.room_state.shape[1]: 109 | return False, False 110 | 111 | 112 | can_push_box = self.room_state[new_position[0], new_position[1]] in [3, 4] 113 | can_push_box &= self.room_state[new_box_position[0], new_box_position[1]] in [1, 2] 114 | if can_push_box: 115 | 116 | self.new_box_position = tuple(new_box_position) 117 | self.old_box_position = tuple(new_position) 118 | 119 | # Move Player 120 | self.player_position = new_position 121 | self.room_state[(new_position[0], new_position[1])] = 5 122 | self.room_state[current_position[0], current_position[1]] = \ 123 | self.room_fixed[current_position[0], current_position[1]] 124 | 125 | # Move Box 126 | box_type = 4 127 | if self.room_fixed[new_box_position[0], new_box_position[1]] == 2: 128 | box_type = 3 129 | self.room_state[new_box_position[0], new_box_position[1]] = box_type 130 | return True, True 131 | 132 | # Try to move if no box to push, available 133 | else: 134 | return self._move(action), False 135 | 136 | def _move(self, action): 137 | """ 138 | Moves the player to the next field, if it is not occupied. 139 | :param action: 140 | :return: Boolean, indicating a change of the room's state 141 | """ 142 | change = CHANGE_COORDINATES[(action - 1) % 4] 143 | new_position = self.player_position + change 144 | current_position = self.player_position.copy() 145 | 146 | # Move player if the field in the moving direction is either 147 | # an empty field or an empty box target. 148 | if self.room_state[new_position[0], new_position[1]] in [1, 2]: 149 | self.player_position = new_position 150 | self.room_state[(new_position[0], new_position[1])] = 5 151 | self.room_state[current_position[0], current_position[1]] = \ 152 | self.room_fixed[current_position[0], current_position[1]] 153 | 154 | return True 155 | 156 | return False 157 | 158 | def _calc_reward(self): 159 | """ 160 | Calculate Reward Based on 161 | :return: 162 | """ 163 | # Every step a small penalty is given, This ensures 164 | # that short solutions have a higher reward. 165 | self.reward_last = self.penalty_for_step 166 | 167 | # count boxes off or on the target 168 | empty_targets = self.room_state == 2 169 | player_on_target = (self.room_fixed == 2) & (self.room_state == 5) 170 | total_targets = empty_targets | player_on_target 171 | 172 | current_boxes_on_target = self.num_boxes - \ 173 | np.where(total_targets)[0].shape[0] 174 | 175 | # Add the reward if a box is pushed on the target and give a 176 | # penalty if a box is pushed off the target. 177 | if current_boxes_on_target > self.boxes_on_target: 178 | self.reward_last += self.reward_box_on_target 179 | elif current_boxes_on_target < self.boxes_on_target: 180 | self.reward_last += self.penalty_box_off_target 181 | 182 | game_won = self._check_if_all_boxes_on_target() 183 | if game_won: 184 | self.reward_last += self.reward_finished 185 | 186 | self.boxes_on_target = current_boxes_on_target 187 | 188 | def _check_if_done(self): 189 | # Check if the game is over either through reaching the maximum number 190 | # of available steps or by pushing all boxes on the targets. 191 | return self._check_if_all_boxes_on_target() or self._check_if_maxsteps() 192 | 193 | def _check_if_all_boxes_on_target(self): 194 | empty_targets = self.room_state == 2 195 | player_hiding_target = (self.room_fixed == 2) & (self.room_state == 5) 196 | are_all_boxes_on_targets = np.where(empty_targets | player_hiding_target)[0].shape[0] == 0 197 | return are_all_boxes_on_targets 198 | 199 | def _check_if_maxsteps(self): 200 | return (self.max_steps == self.num_env_steps) 201 | 202 | def reset(self, second_player=False, render_mode='rgb_array'): 203 | try: 204 | self.room_fixed, self.room_state, self.box_mapping = generate_room( 205 | dim=self.dim_room, 206 | num_steps=self.num_gen_steps, 207 | num_boxes=self.num_boxes, 208 | second_player=second_player 209 | ) 210 | except (RuntimeError, RuntimeWarning) as e: 211 | print("[SOKOBAN] Runtime Error/Warning: {}".format(e)) 212 | print("[SOKOBAN] Retry . . .") 213 | return self.reset(second_player=second_player, render_mode=render_mode) 214 | 215 | self.player_position = np.argwhere(self.room_state == 5)[0] 216 | self.num_env_steps = 0 217 | self.reward_last = 0 218 | self.boxes_on_target = 0 219 | 220 | starting_observation = self.render(render_mode) 221 | return starting_observation 222 | 223 | def render(self, mode='human', close=None, scale=1): 224 | assert mode in RENDERING_MODES 225 | 226 | img = self.get_image(mode, scale) 227 | 228 | if 'rgb_array' in mode: 229 | return img 230 | 231 | elif 'human' in mode: 232 | from gym.envs.classic_control import rendering 233 | if self.viewer is None: 234 | self.viewer = rendering.SimpleImageViewer() 235 | self.viewer.imshow(img) 236 | return self.viewer.isopen 237 | 238 | elif 'raw' in mode: 239 | arr_walls = (self.room_fixed == 0).view(np.int8) 240 | arr_goals = (self.room_fixed == 2).view(np.int8) 241 | arr_boxes = ((self.room_state == 4) + (self.room_state == 3)).view(np.int8) 242 | arr_player = (self.room_state == 5).view(np.int8) 243 | 244 | return arr_walls, arr_goals, arr_boxes, arr_player 245 | 246 | else: 247 | super(SokobanEnv, self).render(mode=mode) # just raise an exception 248 | 249 | def get_image(self, mode, scale=1): 250 | 251 | if mode.startswith('tiny_'): 252 | img = room_to_tiny_world_rgb(self.room_state, self.room_fixed, scale=scale) 253 | else: 254 | img = room_to_rgb(self.room_state, self.room_fixed) 255 | 256 | return img 257 | 258 | def close(self): 259 | if self.viewer is not None: 260 | self.viewer.close() 261 | 262 | def set_maxsteps(self, num_steps): 263 | self.max_steps = num_steps 264 | 265 | def get_action_lookup(self): 266 | return ACTION_LOOKUP 267 | 268 | def get_action_meanings(self): 269 | return ACTION_LOOKUP 270 | 271 | 272 | ACTION_LOOKUP = { 273 | 0: 'no operation', 274 | 1: 'push up', 275 | 2: 'push down', 276 | 3: 'push left', 277 | 4: 'push right', 278 | 5: 'move up', 279 | 6: 'move down', 280 | 7: 'move left', 281 | 8: 'move right', 282 | } 283 | 284 | # Moves are mapped to coordinate changes as follows 285 | # 0: Move up 286 | # 1: Move down 287 | # 2: Move left 288 | # 3: Move right 289 | CHANGE_COORDINATES = { 290 | 0: (-1, 0), 291 | 1: (1, 0), 292 | 2: (0, -1), 293 | 3: (0, 1) 294 | } 295 | 296 | RENDERING_MODES = ['rgb_array', 'human', 'tiny_rgb_array', 'tiny_human', 'raw'] 297 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # gym-sokoban 2 | [Sokoban](https://en.wikipedia.org/wiki/Sokoban) is Japanese for warehouse keeper and a traditional video game. 3 | The game is a transportation puzzle, where the player has to push all boxes in the room on the storage locations/ targets. 4 | The possibility of making irreversible mistakes makes these puzzles so challenging especially for [Reinforcement Learning](https://en.wikipedia.org/wiki/Reinforcement_learning) algorithms, which mostly lack the ability to think ahead. 5 |
The repository implements the game Sokoban based on the rules presented [DeepMind's]() paper [Imagination Augmented Agents for Deep Reinforcement Learning](https://papers.nips.cc/paper/7152-imagination-augmented-agents-for-deep-reinforcement-learning). 6 | The room generation is random and therefore, will allow to train Deep Neural Networks without overfitting on a set of predefined rooms. 7 | 8 | 9 | | Example Game 1 | Example Game 2 | Example Game 3 | 10 | | :---: | :---: | :---: 11 | | ![Game 1](/docs/Animations/solved_3.gif?raw=true) | ![Game 2](/docs/Animations/solved_4.gif?raw=true) | ![Game 3](/docs/Animations/solved_5.gif?raw=true) | 12 | 13 | 14 | ## 1 Installation 15 | 16 | ### Via PIP 17 | ```bash 18 | pip install gym-sokoban 19 | ``` 20 | 21 | ### From Repository 22 | ```bash 23 | git clone git@github.com:mpSchrader/gym-sokoban.git 24 | cd gym-sokoban 25 | pip install -e . 26 | ``` 27 | Checkout the [examples](/examples) on how to use an external gym environment. 28 | 29 | ## 2 Game Environment 30 | 31 | ### 2.1 Room Elements 32 | Every room consists of five main elements: walls, floor, boxes, box targets, and a player. They might have different states whether they overlap with a box target or not. 33 | 34 | | Type | State | Graphic | TinyWorld | 35 | | --- | ----- | :---: | :---: | 36 | | Wall | Static | ![Wall](/gym_sokoban/envs/surface/wall.png "Wall") | ![Wall](/gym_sokoban/envs/surface/tiny_world/wall.png "Wall") | 37 | | Floor | Empty | ![Floor](/gym_sokoban/envs/surface/floor.png "Floor") | ![Floor](/gym_sokoban/envs/surface/tiny_world/floor.png "Floor") | 38 | | Box Target | Empty | ![BoxTarget](/gym_sokoban/envs/surface/box_target.png "Box Target") | ![BoxTarget](/gym_sokoban/envs/surface/tiny_world/box_target.png "Box Target") | 39 | | Box | Off Target | ![BoxOffTarget](/gym_sokoban/envs/surface/box.png "Box") | ![BoxOffTarget](/gym_sokoban/envs/surface/tiny_world/box.png "Box") | 40 | | Box | On Target | ![BoxOnTarget](/gym_sokoban/envs/surface/box_on_target.png "Box") | ![BoxOnTarget](/gym_sokoban/envs/surface/tiny_world/box_on_target.png "Box") | 41 | | Player | Off Target | ![PlayerOffTarget](/gym_sokoban/envs/surface/player.png "Player") | ![PlayerOffTarget](/gym_sokoban/envs/surface/tiny_world/player.png "Player") | 42 | | Player | On Target | ![PlayerOnTarget](/gym_sokoban/envs/surface/player_on_target.png "Player") | ![PlayerOnTarget](/gym_sokoban/envs/surface/tiny_world/player_on_target.png "Player") | 43 | 44 | ### 2.2 Actions 45 | The game provides 9 actions to interact with the environment. 46 | Push and Move actions into the directions Up, Down, Left and Right. 47 | The No Operation action is a void action, which does not change anything in the environment. 48 | The mapping of the action numbers to the actual actions looks as follows 49 | 50 | | Action | ID | 51 | | -------- | :---: | 52 | | No Operation | 0 | 53 | | Push Up | 1 | 54 | | Push Down | 2 | 55 | | Push Left | 3 | 56 | | Push Right | 4 | 57 | | Move Up | 5 | 58 | | Move Down | 6 | 59 | | Move Left | 7 | 60 | | Move Right | 8 | 61 | 62 | **Move** simply moves if there is a free field in the direction, which means no blocking box or wall. 63 | 64 | **Push** push tries to move an adjacent box if the next field behind the box is free. 65 | This means no chain pushing of boxes is possible. 66 | In case there is no box at the adjacent field, the push action is handled the same way as the move action into the same direction. 67 | 68 | ### 2.3 Rewards 69 | Finishing the game by pushing all on the targets gives a reward of 10 in the last step. 70 | Also pushing a box on or off a target gives a reward of 1 respectively of -1. 71 | In addition a reward of -0.1 is given for every step, this penalizes solutions with many steps. 72 | 73 | | Reason | Reward | 74 | | ------------------------- | ----: | 75 | | Perform Step | -0.1 | 76 | | Push Box on Target | 1.0 | 77 | | Push Box off Target | -1.0 | 78 | | Push all boxes on targets | 10.0 | 79 | 80 | ### 2.4 Level Generation 81 | Every time a Sokoban environment is loaded or reset a new room is randomly generated. 82 | The generation consists of 3 phases: Topology Generation, Placement of Targets and Players, and Reverse Playing. 83 | #### 2.4.1 Topology Generation 84 | To generate the basic topology of the room, consisting of walls and empty floor, is based on a random walk, which changes its direction at probability 0.35. 85 | At every step centered at the current position, a pattern of fields is set to empty spaces. 86 | The patterns used can be found in [Figure 2](#topologyMask). 87 |
88 |

89 | 90 |

91 |

92 | Figure 2: Masks for creating a topology 93 |

94 |
95 | 96 | 97 | #### 2.4.2 Placement of Elements 98 | During this phase, the player including all n box targets are placed on randomly chosen empty spaces. 99 | 100 | #### 2.4.3 Reverse Playing 101 | This is the crucial phase to ensure a solvable room. 102 | Now Sokoban is played in a reverse fashion, where a player can move and pull boxes. 103 | The goal of this phase is to find the room state, with the highest room score, with a [Depth First Search](https://en.wikipedia.org/wiki/Depth-first_search). 104 | For every room explored during the search is a room score is calculated with the equation shown below. 105 | The equation is a heuristic approach to evaluate the difficulty of the room. 106 | BoxSwaps counts the number of times a player changes the box to pull. 107 | BoxDisplacement is the [Manhattan Distance](https://en.wikipedia.org/wiki/Manhattan_distance) between a specific box and its origin box target. 108 | As long as at least one box is on a target the RoomScore is always 0. 109 |
110 |

111 | 112 |

113 |
114 | 115 | ### 2.5 Configuration 116 | Sokoban has many different variations, such as: Room Size, Number of Boxes, Rendering Modes, or Rules. 117 | 118 | #### 2.5.1 Rendering Modes 119 | Besides the regular Sokoban rendering, each configuration can be rendered as TinyWorld, which has a pixel size equal to the grid size. 120 | To get an environment rendered as a tiny world just add `tiny_` in front of the rendering mode. E.g: `env.render('tiny_rgb_array', scale=scale_tiny)`. Scale allows to increase the size of the rendered tiny world observation. Using scale in combination with the rendering modes, `human` or `rgb_array`, does not influence the output size. 121 | Available rendering modes are: 122 | 123 | | Mode | Description | 124 | | --- | --- 125 | | rgb_array | Well looking 2d rgb image 126 | | human | Displays the current state on screen 127 | | tiny_rgb_array | Each pixel describing one element in the room 128 | | tiny_human | Displays the tiny rgb_array on screen 129 | 130 | 131 | #### 2.5.2 Size Variations 132 | The available room configurations are shown in the table below. 133 | 134 | | Room Id | Grid-Size | Pixels | #Boxes | Example | TinyWorld | 135 | | --- | :---: | :---: | :---: | :---: | :---: | 136 | | Sokoban-v0 | 10x10 | 160x160 | 3 | ![Sokoban-v0](/docs/rooms/Sokoban-v0.png) | ![Sokoban-v0](/docs/rooms/Tiny_World_Sokoban-v0.png) | 137 | | Sokoban-v1 | 10x10 | 160x160 | 4 | ![Sokoban-v1](/docs/rooms/Sokoban-v1.png) | ![Sokoban-v1](/docs/rooms/Tiny_World_Sokoban-v1.png) | 138 | | Sokoban-v2 | 10x10 | 160x160 | 5 | ![Sokoban-v2](/docs/rooms/Sokoban-v2.png) | ![Sokoban-v2](/docs/rooms/Tiny_World_Sokoban-v2.png) | 139 | | Sokoban-small-v0 | 7x7 | 112x112 | 2 | ![Sokoban-small-v0](/docs/rooms/Sokoban-small-v0.png) | ![Sokoban-small-v0](/docs/rooms/Tiny_World_Sokoban-small-v0.png) | 140 | | Sokoban-small-v1 | 7x7 | 112x112 | 3 | ![Sokoban-small-v1](/docs/rooms/Sokoban-small-v1.png) | ![Sokoban-small-v1](/docs/rooms/Tiny_World_Sokoban-small-v1.png) | 141 | | Sokoban-large-v0 | 13x11 | 208x176 | 3 | ![Sokoban-large-v0](/docs/rooms/Sokoban-large-v0.png) | ![Sokoban-large-v0](/docs/rooms/Tiny_World_Sokoban-large-v0.png) | 142 | | Sokoban-large-v1 | 13x11 | 208x176 | 4 | ![Sokoban-large-v1](/docs/rooms/Sokoban-large-v1.png) | ![Sokoban-large-v1](/docs/rooms/Tiny_World_Sokoban-large-v1.png) | 143 | | Sokoban-large-v2 | 13x11 | 208x176 | 5 | ![Sokoban-large-v2](/docs/rooms/Sokoban-large-v2.png) | ![Sokoban-large-v2](/docs/rooms/Tiny_World_Sokoban-large-v2.png) | 144 | | Sokoban-huge-v0 | 13x13 | 208x208 | 5 | ![Sokoban-huge-v0](/docs/rooms/Sokoban-huge-v0.png) | ![Sokoban-huge-v0](/docs/rooms/Tiny_World_Sokoban-huge-v0.png) 145 | 146 | Please note that the larger rooms might take some time to be created, especially on a laptop. 147 | 148 | #### 2.5.3 Other Variations 149 | Besides the regular game of Sokoban, this repository implements or will implement variations, which might make the game easier or more complicated. Except noted differently the variations do not implement a Tiny-World version. 150 | 151 | | Variation | Summary | Expected Difficulty | Example | Tiny World | Status | Details | 152 | | --- | :---: | :---: | :---: | :---: | :---: | :---: | 153 | | Fixed Targets | Every box has to be pushed on the target with the same color. | More difficult | ![Fixed-Targets](/docs/rooms/Sokoban-Fixed-Targets-Example.png) | Yes | implemented | [ReadMe](/docs/variations/FixedTargets.md) | 154 | | Multiple Player | There are two players in the room. Every round one of the two players can be used. There is no order of moves between the two players. | More difficult | ![TwoPlayer](/docs/rooms/TwoPlayer-Sokoban-v2.png) | Yes | implemented | [ReadMe](/docs/variations/TwoPlayer.md) | 155 | | Push&Pull | The player can not only push the boxes, but also pull them. Therefore, no more irreversible moves exist. | Easier | ![PushAndPull-Targets](/docs/rooms/Sokoban-v1.png) | Yes | implemented | [ReadMe](/docs/variations/PushAndPull.md) | 156 | | Boxoban | Uses by DeepMind [pregenerated Sokoban puzzles](https://github.com/deepmind/boxoban-levels). | Similar | ![PushAndPull-Targets](/docs/rooms/Sokoban-v1.png) | Yes | Implemented | [ReadMe](/docs/variations/Boxoban.md) | 157 | 158 | ## 3 Cite 159 | If you are using this repository for your research please cite it with the following information: 160 | ``` 161 | @misc{SchraderSokoban2018, 162 | author = {Schrader, Max-Philipp B.}, 163 | title = {gym-sokoban}, 164 | year = {2018}, 165 | publisher = {GitHub}, 166 | journal = {GitHub repository}, 167 | howpublished = {\url{https://github.com/mpSchrader/gym-sokoban}}, 168 | commit = {#CommitId} 169 | } 170 | ``` 171 | 172 | ## 4 Connect & Contribute 173 | 174 | ### 4.1 Connect 175 | Feel free to get in touch with me to talk about this or other projects. 176 | Either by creating an [issue](https://github.com/mpSchrader/gym-sokoban/issues) or mail me on [LinkedIn](https://www.linkedin.com/in/max-philipp-schrader/). 177 | 178 | If you reached the end and liked the project, please **show your appreciation by starting this project**. 179 | 180 | ### 4.2 Contribute 181 | Feel free to contribute to this project by forking the repo and implement whatever you are missing. 182 | Alternatively, open a new issue in case you need help or want to have a feature added. 183 | -------------------------------------------------------------------------------- /gym_sokoban/envs/room_utils.py: -------------------------------------------------------------------------------- 1 | import random 2 | import numpy as np 3 | import marshal 4 | 5 | 6 | def generate_room(dim=(13, 13), p_change_directions=0.35, num_steps=25, num_boxes=3, tries=4, second_player=False): 7 | """ 8 | Generates a Sokoban room, represented by an integer matrix. The elements are encoded as follows: 9 | wall = 0 10 | empty space = 1 11 | box target = 2 12 | box not on target = 3 13 | box on target = 4 14 | player = 5 15 | 16 | :param dim: 17 | :param p_change_directions: 18 | :param num_steps: 19 | :return: Numpy 2d Array 20 | """ 21 | room_state = np.zeros(shape=dim) 22 | room_structure = np.zeros(shape=dim) 23 | 24 | # Some times rooms with a score == 0 are the only possibility. 25 | # In these case, we try another model. 26 | for t in range(tries): 27 | room = room_topology_generation(dim, p_change_directions, num_steps) 28 | room = place_boxes_and_player(room, num_boxes=num_boxes, second_player=second_player) 29 | 30 | # Room fixed represents all not movable parts of the room 31 | room_structure = np.copy(room) 32 | room_structure[room_structure == 5] = 1 33 | 34 | # Room structure represents the current state of the room including movable parts 35 | room_state = room.copy() 36 | room_state[room_state == 2] = 4 37 | 38 | room_state, score, box_mapping = reverse_playing(room_state, room_structure) 39 | room_state[room_state == 3] = 4 40 | 41 | if score > 0: 42 | break 43 | 44 | if score == 0: 45 | raise RuntimeWarning('Generated Model with score == 0') 46 | 47 | return room_structure, room_state, box_mapping 48 | 49 | 50 | def room_topology_generation(dim=(10, 10), p_change_directions=0.35, num_steps=15): 51 | """ 52 | Generate a room topology, which consits of empty floors and walls. 53 | 54 | :param dim: 55 | :param p_change_directions: 56 | :param num_steps: 57 | :return: 58 | """ 59 | dim_x, dim_y = dim 60 | 61 | # The ones in the mask represent all fields which will be set to floors 62 | # during the random walk. The centered one will be placed over the current 63 | # position of the walk. 64 | masks = [ 65 | [ 66 | [0, 0, 0], 67 | [1, 1, 1], 68 | [0, 0, 0] 69 | ], 70 | [ 71 | [0, 1, 0], 72 | [0, 1, 0], 73 | [0, 1, 0] 74 | ], 75 | [ 76 | [0, 0, 0], 77 | [1, 1, 0], 78 | [0, 1, 0] 79 | ], 80 | [ 81 | [0, 0, 0], 82 | [1, 1, 0], 83 | [1, 1, 0] 84 | ], 85 | [ 86 | [0, 0, 0], 87 | [0, 1, 1], 88 | [0, 1, 0] 89 | ] 90 | ] 91 | 92 | # Possible directions during the walk 93 | directions = [(1, 0), (0, 1), (-1, 0), (0, -1)] 94 | direction = random.sample(directions, 1)[0] 95 | 96 | # Starting position of random walk 97 | position = np.array([ 98 | random.randint(1, dim_x - 1), 99 | random.randint(1, dim_y - 1)] 100 | ) 101 | 102 | level = np.zeros(dim, dtype=int) 103 | 104 | for s in range(num_steps): 105 | 106 | # Change direction randomly 107 | if random.random() < p_change_directions: 108 | direction = random.sample(directions, 1)[0] 109 | 110 | # Update position 111 | position = position + direction 112 | position[0] = max(min(position[0], dim_x - 2), 1) 113 | position[1] = max(min(position[1], dim_y - 2), 1) 114 | 115 | # Apply mask 116 | mask = random.sample(masks, 1)[0] 117 | mask_start = position - 1 118 | level[mask_start[0]:mask_start[0] + 3, mask_start[1]:mask_start[1] + 3] += mask 119 | 120 | level[level > 0] = 1 121 | level[:, [0, dim_y - 1]] = 0 122 | level[[0, dim_x - 1], :] = 0 123 | 124 | return level 125 | 126 | 127 | def place_boxes_and_player(room, num_boxes, second_player): 128 | """ 129 | Places the player and the boxes into the floors in a room. 130 | 131 | :param room: 132 | :param num_boxes: 133 | :return: 134 | """ 135 | # Get all available positions 136 | possible_positions = np.where(room == 1) 137 | num_possible_positions = possible_positions[0].shape[0] 138 | num_players = 2 if second_player else 1 139 | 140 | if num_possible_positions <= num_boxes + num_players: 141 | raise RuntimeError('Not enough free spots (#{}) to place {} player and {} boxes.'.format( 142 | num_possible_positions, 143 | num_players, 144 | num_boxes) 145 | ) 146 | 147 | # Place player(s) 148 | ind = np.random.randint(num_possible_positions) 149 | player_position = possible_positions[0][ind], possible_positions[1][ind] 150 | room[player_position] = 5 151 | 152 | if second_player: 153 | ind = np.random.randint(num_possible_positions) 154 | player_position = possible_positions[0][ind], possible_positions[1][ind] 155 | room[player_position] = 5 156 | 157 | # Place boxes 158 | for n in range(num_boxes): 159 | possible_positions = np.where(room == 1) 160 | num_possible_positions = possible_positions[0].shape[0] 161 | 162 | ind = np.random.randint(num_possible_positions) 163 | box_position = possible_positions[0][ind], possible_positions[1][ind] 164 | room[box_position] = 2 165 | 166 | return room 167 | 168 | 169 | # Global variables used for reverse playing. 170 | explored_states = set() 171 | num_boxes = 0 172 | best_room_score = -1 173 | best_room = None 174 | best_box_mapping = None 175 | 176 | 177 | def reverse_playing(room_state, room_structure, search_depth=100): 178 | """ 179 | This function plays Sokoban reverse in a way, such that the player can 180 | move and pull boxes. 181 | It ensures a solvable level with all boxes not being placed on a box target. 182 | :param room_state: 183 | :param room_structure: 184 | :param search_depth: 185 | :return: 2d array 186 | """ 187 | global explored_states, num_boxes, best_room_score, best_room, best_box_mapping 188 | 189 | # Box_Mapping is used to calculate the box displacement for every box 190 | box_mapping = {} 191 | box_locations = np.where(room_structure == 2) 192 | num_boxes = len(box_locations[0]) 193 | for l in range(num_boxes): 194 | box = (box_locations[0][l], box_locations[1][l]) 195 | box_mapping[box] = box 196 | 197 | # explored_states globally stores the best room state and score found during search 198 | explored_states = set() 199 | best_room_score = -1 200 | best_box_mapping = box_mapping 201 | depth_first_search(room_state, room_structure, box_mapping, box_swaps=0, last_pull=(-1, -1), ttl=300) 202 | 203 | return best_room, best_room_score, best_box_mapping 204 | 205 | 206 | def depth_first_search(room_state, room_structure, box_mapping, box_swaps=0, last_pull=(-1, -1), ttl=300): 207 | """ 208 | Searches through all possible states of the room. 209 | This is a recursive function, which stops if the tll is reduced to 0 or 210 | over 1.000.000 states have been explored. 211 | :param room_state: 212 | :param room_structure: 213 | :param box_mapping: 214 | :param box_swaps: 215 | :param last_pull: 216 | :param ttl: 217 | :return: 218 | """ 219 | global explored_states, num_boxes, best_room_score, best_room, best_box_mapping 220 | 221 | ttl -= 1 222 | if ttl <= 0 or len(explored_states) >= 300000: 223 | return 224 | 225 | state_tohash = marshal.dumps(room_state) 226 | 227 | # Only search this state, if it not yet has been explored 228 | if not (state_tohash in explored_states): 229 | 230 | # Add current state and its score to explored states 231 | room_score = box_swaps * box_displacement_score(box_mapping) 232 | if np.where(room_state == 2)[0].shape[0] != num_boxes: 233 | room_score = 0 234 | 235 | if room_score > best_room_score: 236 | best_room = room_state 237 | best_room_score = room_score 238 | best_box_mapping = box_mapping 239 | 240 | explored_states.add(state_tohash) 241 | 242 | for action in ACTION_LOOKUP.keys(): 243 | # The state and box mapping need to be copied to ensure 244 | # every action start from a similar state. 245 | room_state_next = room_state.copy() 246 | box_mapping_next = box_mapping.copy() 247 | 248 | room_state_next, box_mapping_next, last_pull_next = \ 249 | reverse_move(room_state_next, room_structure, box_mapping_next, last_pull, action) 250 | 251 | box_swaps_next = box_swaps 252 | if last_pull_next != last_pull: 253 | box_swaps_next += 1 254 | 255 | depth_first_search(room_state_next, room_structure, 256 | box_mapping_next, box_swaps_next, 257 | last_pull, ttl) 258 | 259 | 260 | def reverse_move(room_state, room_structure, box_mapping, last_pull, action): 261 | """ 262 | Perform reverse action. Where all actions in the range [0, 3] correspond to 263 | push actions and the ones greater 3 are simmple move actions. 264 | :param room_state: 265 | :param room_structure: 266 | :param box_mapping: 267 | :param last_pull: 268 | :param action: 269 | :return: 270 | """ 271 | player_position = np.where(room_state == 5) 272 | player_position = np.array([player_position[0][0], player_position[1][0]]) 273 | 274 | change = CHANGE_COORDINATES[action % 4] 275 | next_position = player_position + change 276 | 277 | # Check if next position is an empty floor or an empty box target 278 | if room_state[next_position[0], next_position[1]] in [1, 2]: 279 | 280 | # Move player, independent of pull or move action. 281 | room_state[player_position[0], player_position[1]] = room_structure[player_position[0], player_position[1]] 282 | room_state[next_position[0], next_position[1]] = 5 283 | 284 | # In addition try to pull a box if the action is a pull action 285 | if action < 4: 286 | possible_box_location = change[0] * -1, change[1] * -1 287 | possible_box_location += player_position 288 | 289 | if room_state[possible_box_location[0], possible_box_location[1]] in [3, 4]: 290 | # Perform pull of the adjacent box 291 | room_state[player_position[0], player_position[1]] = 3 292 | room_state[possible_box_location[0], possible_box_location[1]] = room_structure[ 293 | possible_box_location[0], possible_box_location[1]] 294 | 295 | # Update the box mapping 296 | for k in box_mapping.keys(): 297 | if box_mapping[k] == (possible_box_location[0], possible_box_location[1]): 298 | box_mapping[k] = (player_position[0], player_position[1]) 299 | last_pull = k 300 | 301 | return room_state, box_mapping, last_pull 302 | 303 | 304 | def box_displacement_score(box_mapping): 305 | """ 306 | Calculates the sum of all Manhattan distances, between the boxes 307 | and their origin box targets. 308 | :param box_mapping: 309 | :return: 310 | """ 311 | score = 0 312 | 313 | for box_target in box_mapping.keys(): 314 | box_location = np.array(box_mapping[box_target]) 315 | box_target = np.array(box_target) 316 | dist = np.sum(np.abs(box_location - box_target)) 317 | score += dist 318 | 319 | return score 320 | 321 | 322 | TYPE_LOOKUP = { 323 | 0: 'wall', 324 | 1: 'empty space', 325 | 2: 'box target', 326 | 3: 'box on target', 327 | 4: 'box not on target', 328 | 5: 'player' 329 | } 330 | 331 | ACTION_LOOKUP = { 332 | 0: 'push up', 333 | 1: 'push down', 334 | 2: 'push left', 335 | 3: 'push right', 336 | 4: 'move up', 337 | 5: 'move down', 338 | 6: 'move left', 339 | 7: 'move right', 340 | } 341 | 342 | # Moves are mapped to coordinate changes as follows 343 | # 0: Move up 344 | # 1: Move down 345 | # 2: Move left 346 | # 3: Move right 347 | CHANGE_COORDINATES = { 348 | 0: (-1, 0), 349 | 1: (1, 0), 350 | 2: (0, -1), 351 | 3: (0, 1) 352 | } 353 | -------------------------------------------------------------------------------- /gym_sokoban/envs/render_utils.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import pkg_resources 3 | import imageio 4 | 5 | 6 | def room_to_rgb(room, room_structure=None): 7 | """ 8 | Creates an RGB image of the room. 9 | :param room: 10 | :param room_structure: 11 | :return: 12 | """ 13 | resource_package = __name__ 14 | 15 | room = np.array(room) 16 | if not room_structure is None: 17 | # Change the ID of a player on a target 18 | room[(room == 5) & (room_structure == 2)] = 6 19 | 20 | # Load images, representing the corresponding situation 21 | box_filename = pkg_resources.resource_filename(resource_package, '/'.join(('surface', 'box.png'))) 22 | box = imageio.imread(box_filename) 23 | 24 | box_on_target_filename = pkg_resources.resource_filename(resource_package, 25 | '/'.join(('surface', 'box_on_target.png'))) 26 | box_on_target = imageio.imread(box_on_target_filename) 27 | 28 | box_target_filename = pkg_resources.resource_filename(resource_package, '/'.join(('surface', 'box_target.png'))) 29 | box_target = imageio.imread(box_target_filename) 30 | 31 | floor_filename = pkg_resources.resource_filename(resource_package, '/'.join(('surface', 'floor.png'))) 32 | floor = imageio.imread(floor_filename) 33 | 34 | player_filename = pkg_resources.resource_filename(resource_package, '/'.join(('surface', 'player.png'))) 35 | player = imageio.imread(player_filename) 36 | 37 | player_on_target_filename = pkg_resources.resource_filename(resource_package, 38 | '/'.join(('surface', 'player_on_target.png'))) 39 | player_on_target = imageio.imread(player_on_target_filename) 40 | 41 | wall_filename = pkg_resources.resource_filename(resource_package, '/'.join(('surface', 'wall.png'))) 42 | wall = imageio.imread(wall_filename) 43 | 44 | surfaces = [wall, floor, box_target, box_on_target, box, player, player_on_target] 45 | 46 | # Assemble the new rgb_room, with all loaded images 47 | room_rgb = np.zeros(shape=(room.shape[0] * 16, room.shape[1] * 16, 3), dtype=np.uint8) 48 | for i in range(room.shape[0]): 49 | x_i = i * 16 50 | 51 | for j in range(room.shape[1]): 52 | y_j = j * 16 53 | surfaces_id = room[i, j] 54 | 55 | room_rgb[x_i:(x_i + 16), y_j:(y_j + 16), :] = surfaces[surfaces_id] 56 | 57 | return room_rgb 58 | 59 | 60 | def room_to_tiny_world_rgb(room, room_structure=None, scale=1): 61 | 62 | room = np.array(room) 63 | if not room_structure is None: 64 | # Change the ID of a player on a target 65 | room[(room == 5) & (room_structure == 2)] = 6 66 | 67 | wall = [0, 0, 0] 68 | floor = [243, 248, 238] 69 | box_target = [254, 126, 125] 70 | box_on_target = [254, 95, 56] 71 | box = [142, 121, 56] 72 | player = [160, 212, 56] 73 | player_on_target = [219, 212, 56] 74 | 75 | surfaces = [wall, floor, box_target, box_on_target, box, player, player_on_target] 76 | 77 | # Assemble the new rgb_room, with all loaded images 78 | room_small_rgb = np.zeros(shape=(room.shape[0]*scale, room.shape[1]*scale, 3), dtype=np.uint8) 79 | for i in range(room.shape[0]): 80 | x_i = i * scale 81 | for j in range(room.shape[1]): 82 | y_j = j * scale 83 | surfaces_id = int(room[i, j]) 84 | room_small_rgb[x_i:(x_i+scale), y_j:(y_j+scale), :] = np.array(surfaces[surfaces_id]) 85 | 86 | return room_small_rgb 87 | 88 | 89 | def room_to_rgb_FT(room, box_mapping, room_structure=None): 90 | """ 91 | Creates an RGB image of the room. 92 | :param room: 93 | :param room_structure: 94 | :return: 95 | """ 96 | resource_package = __name__ 97 | 98 | room = np.array(room) 99 | if not room_structure is None: 100 | # Change the ID of a player on a target 101 | room[(room == 5) & (room_structure == 2)] = 6 102 | 103 | # Load images, representing the corresponding situation 104 | box_filename = pkg_resources.resource_filename(resource_package, '/'.join(('surface', 'box.png'))) 105 | box = imageio.imread(box_filename) 106 | 107 | box_on_target_filename = pkg_resources.resource_filename(resource_package, 108 | '/'.join(('surface', 'box_on_target.png'))) 109 | box_on_target = imageio.imread(box_on_target_filename) 110 | 111 | box_target_filename = pkg_resources.resource_filename(resource_package, '/'.join(('surface', 'box_target.png'))) 112 | box_target = imageio.imread(box_target_filename) 113 | 114 | floor_filename = pkg_resources.resource_filename(resource_package, '/'.join(('surface', 'floor.png'))) 115 | floor = imageio.imread(floor_filename) 116 | 117 | player_filename = pkg_resources.resource_filename(resource_package, '/'.join(('surface', 'player.png'))) 118 | player = imageio.imread(player_filename) 119 | 120 | player_on_target_filename = pkg_resources.resource_filename(resource_package, 121 | '/'.join(('surface', 'player_on_target.png'))) 122 | player_on_target = imageio.imread(player_on_target_filename) 123 | 124 | wall_filename = pkg_resources.resource_filename(resource_package, '/'.join(('surface', 'wall.png'))) 125 | wall = imageio.imread(wall_filename) 126 | 127 | surfaces = [wall, floor, box_target, box_on_target, box, player, player_on_target] 128 | 129 | # Assemble the new rgb_room, with all loaded images 130 | room_rgb = np.zeros(shape=(room.shape[0] * 16, room.shape[1] * 16, 3), dtype=np.uint8) 131 | for i in range(room.shape[0]): 132 | x_i = i * 16 133 | 134 | for j in range(room.shape[1]): 135 | y_j = j * 16 136 | 137 | surfaces_id = room[i, j] 138 | surface = surfaces[surfaces_id] 139 | if 1 < surfaces_id < 5: 140 | try: 141 | surface = get_proper_box_surface(surfaces_id, box_mapping, i, j) 142 | except: 143 | pass 144 | room_rgb[x_i:(x_i + 16), y_j:(y_j + 16), :] = surface 145 | 146 | return room_rgb 147 | 148 | 149 | def get_proper_box_surface(surfaces_id, box_mapping, i, j): 150 | # not used, kept for documentation 151 | # names = ["wall", "floor", "box_target", "box_on_target", "box", "player", "player_on_target"] 152 | 153 | box_id = 0 154 | situation = '' 155 | 156 | if surfaces_id == 2: 157 | situation = '_target' 158 | box_id = list(box_mapping.keys()).index((i, j)) 159 | elif surfaces_id == 3: 160 | box_id = list(box_mapping.values()).index((i, j)) 161 | box_key = list(box_mapping.keys())[box_id] 162 | if box_key == (i, j): 163 | situation = '_on_target' 164 | else: 165 | situation = '_on_wrong_target' 166 | pass 167 | elif surfaces_id == 4: 168 | box_id = list(box_mapping.values()).index((i, j)) 169 | 170 | surface_name = 'box{}{}.png'.format(box_id, situation) 171 | resource_package = __name__ 172 | filename = pkg_resources.resource_filename(resource_package, '/'.join(('surface', 'multibox', surface_name))) 173 | surface = imageio.imread(filename) 174 | 175 | return surface 176 | 177 | 178 | def room_to_tiny_world_rgb_FT(room, box_mapping, room_structure=None, scale=1): 179 | room = np.array(room) 180 | if not room_structure is None: 181 | # Change the ID of a player on a target 182 | room[(room == 5) & (room_structure == 2)] = 6 183 | 184 | wall = [0, 0, 0] 185 | floor = [243, 248, 238] 186 | box_target = [254, 126, 125] 187 | box_on_target = [254, 95, 56] 188 | box = [142, 121, 56] 189 | player = [160, 212, 56] 190 | player_on_target = [219, 212, 56] 191 | 192 | surfaces = [wall, floor, box_target, box_on_target, box, player, player_on_target] 193 | 194 | # Assemble the new rgb_room, with all loaded images 195 | room_small_rgb = np.zeros(shape=(room.shape[0] * scale, room.shape[1] * scale, 3), dtype=np.uint8) 196 | for i in range(room.shape[0]): 197 | x_i = i * scale 198 | for j in range(room.shape[1]): 199 | y_j = j * scale 200 | 201 | surfaces_id = int(room[i, j]) 202 | surface = np.array(surfaces[surfaces_id]) 203 | if 1 < surfaces_id < 5: 204 | try: 205 | surface = get_proper_tiny_box_surface(surfaces_id, box_mapping, i, j) 206 | except: 207 | pass 208 | room_small_rgb[x_i:(x_i + scale), y_j:(y_j + scale), :] = surface 209 | 210 | return room_small_rgb 211 | 212 | 213 | def get_proper_tiny_box_surface(surfaces_id, box_mapping, i, j): 214 | 215 | box_id = 0 216 | situation = 'box' 217 | 218 | if surfaces_id == 2: 219 | situation = 'target' 220 | box_id = list(box_mapping.keys()).index((i, j)) 221 | elif surfaces_id == 3: 222 | box_id = list(box_mapping.values()).index((i, j)) 223 | box_key = list(box_mapping.keys())[box_id] 224 | if box_key == (i, j): 225 | situation = 'on_target' 226 | else: 227 | situation = 'on_wrong_target' 228 | pass 229 | elif surfaces_id == 4: 230 | box_id = list(box_mapping.values()).index((i, j)) 231 | 232 | surface = [255, 255, 255] 233 | if box_id == 0: 234 | if situation == 'target': 235 | surface = [111, 127, 232] 236 | elif situation == 'on_target': 237 | surface = [6, 33, 130] 238 | elif situation == 'on_wrong_target': 239 | surface = [69, 81, 122] 240 | else: 241 | # Just the box 242 | surface = [11, 60, 237] 243 | 244 | elif box_id == 1: 245 | if situation == 'target': 246 | surface = [195, 127, 232] 247 | elif situation == 'on_target': 248 | surface = [96, 5, 145] 249 | elif situation == 'on_wrong_target': 250 | surface = [96, 63, 114] 251 | else: 252 | surface = [145, 17, 214] 253 | 254 | elif box_id == 2: 255 | if situation == 'target': 256 | surface = [221, 113, 167] 257 | elif situation == 'on_target': 258 | surface = [140, 5, 72] 259 | elif situation == 'on_wrong_target': 260 | surface = [109, 60, 71] 261 | else: 262 | surface = [239, 0, 55] 263 | 264 | elif box_id == 3: 265 | if situation == 'target': 266 | surface = [247, 193, 145] 267 | elif situation == 'on_target': 268 | surface = [132, 64, 3] 269 | elif situation == 'on_wrong_target': 270 | surface = [94, 68, 46] 271 | else: 272 | surface = [239, 111, 0] 273 | 274 | return surface 275 | 276 | 277 | def color_player_two(room_rgb, position, room_structure): 278 | resource_package = __name__ 279 | 280 | player_filename = pkg_resources.resource_filename(resource_package, '/'.join(('surface', 'multiplayer', 'player1.png'))) 281 | player = imageio.imread(player_filename) 282 | 283 | player_on_target_filename = pkg_resources.resource_filename(resource_package, 284 | '/'.join(('surface', 'multiplayer', 'player1_on_target.png'))) 285 | player_on_target = imageio.imread(player_on_target_filename) 286 | 287 | x_i = position[0] * 16 288 | y_j = position[1] * 16 289 | 290 | if room_structure[position[0], position[1]] == 2: 291 | room_rgb[x_i:(x_i + 16), y_j:(y_j + 16), :] = player_on_target 292 | 293 | else: 294 | room_rgb[x_i:(x_i + 16), y_j:(y_j + 16), :] = player 295 | 296 | return room_rgb 297 | 298 | 299 | def color_tiny_player_two(room_rgb, position, room_structure, scale = 4): 300 | 301 | x_i = position[0] * scale 302 | y_j = position[1] * scale 303 | 304 | if room_structure[position[0], position[1]] == 2: 305 | room_rgb[x_i:(x_i + scale), y_j:(y_j + scale), :] = [195, 127, 232] 306 | 307 | else: 308 | room_rgb[x_i:(x_i + scale), y_j:(y_j + scale), :] = [96, 5, 145] 309 | 310 | return room_rgb 311 | 312 | 313 | TYPE_LOOKUP = { 314 | 0: 'wall', 315 | 1: 'empty space', 316 | 2: 'box target', 317 | 3: 'box on target', 318 | 4: 'box not on target', 319 | 5: 'player' 320 | } 321 | 322 | ACTION_LOOKUP = { 323 | 0: 'push up', 324 | 1: 'push down', 325 | 2: 'push left', 326 | 3: 'push right', 327 | 4: 'move up', 328 | 5: 'move down', 329 | 6: 'move left', 330 | 7: 'move right', 331 | } 332 | 333 | # Moves are mapped to coordinate changes as follows 334 | # 0: Move up 335 | # 1: Move down 336 | # 2: Move left 337 | # 3: Move right 338 | CHANGE_COORDINATES = { 339 | 0: (-1, 0), 340 | 1: (1, 0), 341 | 2: (0, -1), 342 | 3: (0, 1) 343 | } 344 | -------------------------------------------------------------------------------- /gym_sokoban/envs/sokoban_env_variations.py: -------------------------------------------------------------------------------- 1 | from .sokoban_env import SokobanEnv 2 | from .sokoban_env_fixed_targets import FixedTargetsSokobanEnv 3 | from .sokoban_env_pull import PushAndPullSokobanEnv 4 | from .sokoban_env_two_player import TwoPlayerSokobanEnv 5 | from .boxoban_env import BoxobanEnv 6 | 7 | 8 | class SokobanEnv1(SokobanEnv): 9 | metadata = { 10 | 'render.modes': ['human', 'rgb_array', 'tiny_human', 'tiny_rgb_array'], 11 | 'render_modes': ['human', 'rgb_array', 'tiny_human', 'tiny_rgb_array'], 12 | } 13 | 14 | def __init__(self, **kwargs): 15 | kwargs['num_boxes'] = kwargs.get('num_boxes', 3) 16 | kwargs['max_steps'] = kwargs.get('max_steps', 200) 17 | super(SokobanEnv1, self).__init__(**kwargs) 18 | 19 | 20 | class SokobanEnv2(SokobanEnv): 21 | metadata = { 22 | 'render.modes': ['human', 'rgb_array', 'tiny_human', 'tiny_rgb_array'], 23 | 'render_modes': ['human', 'rgb_array', 'tiny_human', 'tiny_rgb_array'], 24 | } 25 | 26 | def __init__(self, **kwargs): 27 | kwargs['num_boxes'] = kwargs.get('num_boxes', 5) 28 | kwargs['max_steps'] = kwargs.get('max_steps', 200) 29 | kwargs['num_gen_steps'] = kwargs.get('num_gen_steps', 40) 30 | super(SokobanEnv2, self).__init__(**kwargs) 31 | 32 | 33 | class SokobanEnv_Small0(SokobanEnv): 34 | metadata = { 35 | 'render.modes': ['human', 'rgb_array', 'tiny_human', 'tiny_rgb_array'], 36 | 'render_modes': ['human', 'rgb_array', 'tiny_human', 'tiny_rgb_array'], 37 | } 38 | 39 | def __init__(self, **kwargs): 40 | kwargs['dim_room'] = kwargs.get('dim_room', (7, 7)) 41 | kwargs['max_steps'] = kwargs.get('max_steps', 200) 42 | kwargs['num_boxes'] = kwargs.get('num_boxes', 2) 43 | super(SokobanEnv_Small0, self).__init__(**kwargs) 44 | 45 | 46 | class SokobanEnv_Small1(SokobanEnv): 47 | metadata = { 48 | 'render.modes': ['human', 'rgb_array', 'tiny_human', 'tiny_rgb_array'], 49 | 'render_modes': ['human', 'rgb_array', 'tiny_human', 'tiny_rgb_array'], 50 | } 51 | 52 | def __init__(self, **kwargs): 53 | kwargs['dim_room'] = kwargs.get('dim_room', (7, 7)) 54 | kwargs['max_steps'] = kwargs.get('max_steps', 200) 55 | kwargs['num_boxes'] = kwargs.get('num_boxes', 3) 56 | super(SokobanEnv_Small1, self).__init__(**kwargs) 57 | 58 | 59 | class SokobanEnv_Large0(SokobanEnv): 60 | metadata = { 61 | 'render.modes': ['human', 'rgb_array', 'tiny_human', 'tiny_rgb_array'], 62 | 'render_modes': ['human', 'rgb_array', 'tiny_human', 'tiny_rgb_array'], 63 | } 64 | 65 | def __init__(self, **kwargs): 66 | kwargs['dim_room'] = kwargs.get('dim_room', (13, 11)) 67 | kwargs['max_steps'] = kwargs.get('max_steps', 300) 68 | kwargs['num_boxes'] = kwargs.get('num_boxes', 3) 69 | kwargs['num_gen_steps'] = kwargs.get('num_gen_steps', 43) 70 | super(SokobanEnv_Large0, self).__init__(**kwargs) 71 | 72 | 73 | class SokobanEnv_Large1(SokobanEnv): 74 | metadata = { 75 | 'render.modes': ['human', 'rgb_array', 'tiny_human', 'tiny_rgb_array'], 76 | 'render_modes': ['human', 'rgb_array', 'tiny_human', 'tiny_rgb_array'], 77 | } 78 | 79 | def __init__(self, **kwargs): 80 | kwargs['dim_room'] = kwargs.get('dim_room', (13, 11)) 81 | kwargs['max_steps'] = kwargs.get('max_steps', 300) 82 | kwargs['num_boxes'] = kwargs.get('num_boxes', 4) 83 | kwargs['num_gen_steps'] = kwargs.get('num_gen_steps', 43) 84 | super(SokobanEnv_Large1, self).__init__(**kwargs) 85 | 86 | 87 | class SokobanEnv_Large1(SokobanEnv): 88 | metadata = { 89 | 'render.modes': ['human', 'rgb_array', 'tiny_human', 'tiny_rgb_array'], 90 | 'render_modes': ['human', 'rgb_array', 'tiny_human', 'tiny_rgb_array'], 91 | } 92 | 93 | def __init__(self, **kwargs): 94 | kwargs['dim_room'] = kwargs.get('dim_room', (13, 11)) 95 | kwargs['max_steps'] = kwargs.get('max_steps', 300) 96 | kwargs['num_boxes'] = kwargs.get('num_boxes',5) 97 | kwargs['num_gen_steps'] = kwargs.get('num_gen_steps', 43) 98 | super(SokobanEnv_Large1, self).__init__(**kwargs) 99 | 100 | 101 | class SokobanEnv_Huge0(SokobanEnv): 102 | metadata = { 103 | 'render.modes': ['human', 'rgb_array', 'tiny_human', 'tiny_rgb_array'], 104 | 'render_modes': ['human', 'rgb_array', 'tiny_human', 'tiny_rgb_array'], 105 | } 106 | 107 | def __init__(self, **kwargs): 108 | kwargs['dim_room'] = kwargs.get('dim_room', (13, 13)) 109 | kwargs['max_steps'] = kwargs.get('max_steps', 300) 110 | kwargs['num_boxes'] = kwargs.get('num_boxes', 5) 111 | kwargs['num_gen_steps'] = kwargs.get('num_gen_steps', 50) 112 | super(SokobanEnv_Huge0, self).__init__(**kwargs) 113 | 114 | 115 | class FixedTargets_Env_v0(FixedTargetsSokobanEnv): 116 | metadata = { 117 | 'render.modes': ['human', 'rgb_array', 'tiny_human', 'tiny_rgb_array'], 118 | 'render_modes': ['human', 'rgb_array', 'tiny_human', 'tiny_rgb_array'], 119 | } 120 | 121 | def __init__(self, **kwargs): 122 | kwargs['dim_room'] = kwargs.get('dim_room', (10, 10)) 123 | kwargs['max_steps'] = kwargs.get('max_steps', 150) 124 | kwargs['num_boxes'] = kwargs.get('num_boxes', 3) 125 | kwargs['num_gen_steps'] = kwargs.get('num_gen_steps', 50) 126 | super(FixedTargets_Env_v0, self).__init__(**kwargs) 127 | 128 | 129 | class FixedTargets_Env_v1(FixedTargetsSokobanEnv): 130 | metadata = { 131 | 'render.modes': ['human', 'rgb_array', 'tiny_human', 'tiny_rgb_array'], 132 | 'render_modes': ['human', 'rgb_array', 'tiny_human', 'tiny_rgb_array'], 133 | } 134 | 135 | def __init__(self, **kwargs): 136 | kwargs['dim_room'] = kwargs.get('dim_room', (10, 10)) 137 | kwargs['max_steps'] = kwargs.get('max_steps', 150) 138 | kwargs['num_boxes'] = kwargs.get('num_boxes', 4) 139 | kwargs['num_gen_steps'] = kwargs.get('num_gen_steps', 50) 140 | super(FixedTargets_Env_v1, self).__init__(**kwargs) 141 | 142 | 143 | class FixedTargets_Env_v2(FixedTargetsSokobanEnv): 144 | metadata = { 145 | 'render.modes': ['human', 'rgb_array', 'tiny_human', 'tiny_rgb_array'], 146 | 'render_modes': ['human', 'rgb_array', 'tiny_human', 'tiny_rgb_array'], 147 | } 148 | 149 | def __init__(self, **kwargs): 150 | kwargs['dim_room'] = kwargs.get('dim_room', (7, 7)) 151 | kwargs['max_steps'] = kwargs.get('max_steps', 150) 152 | kwargs['num_boxes'] = kwargs.get('num_boxes', 2) 153 | kwargs['num_gen_steps'] = kwargs.get('num_gen_steps', 50) 154 | super(FixedTargets_Env_v2, self).__init__(**kwargs) 155 | 156 | 157 | class FixedTargets_Env_v3(FixedTargetsSokobanEnv): 158 | metadata = { 159 | 'render.modes': ['human', 'rgb_array', 'tiny_human', 'tiny_rgb_array'], 160 | 'render_modes': ['human', 'rgb_array', 'tiny_human', 'tiny_rgb_array'], 161 | } 162 | 163 | def __init__(self, **kwargs): 164 | kwargs['dim_room'] = kwargs.get('dim_room', (7, 7)) 165 | kwargs['max_steps'] = kwargs.get('max_steps', 150) 166 | kwargs['num_boxes'] = kwargs.get('num_boxes', 3) 167 | kwargs['num_gen_steps'] = kwargs.get('num_gen_steps', 50) 168 | super(FixedTargets_Env_v3, self).__init__(**kwargs) 169 | 170 | 171 | class PushAndPull_Env_v0(PushAndPullSokobanEnv): 172 | metadata = { 173 | 'render.modes': ['human', 'rgb_array', 'tiny_human', 'tiny_rgb_array'], 174 | 'render_modes': ['human', 'rgb_array', 'tiny_human', 'tiny_rgb_array'], 175 | } 176 | 177 | def __init__(self, **kwargs): 178 | kwargs['dim_room'] = kwargs.get('dim_room', (10, 10)) 179 | kwargs['max_steps'] = kwargs.get('max_steps', 150) 180 | kwargs['num_boxes'] = kwargs.get('num_boxes', 3) 181 | kwargs['num_gen_steps'] = kwargs.get('num_gen_steps', 50) 182 | super(PushAndPull_Env_v0, self).__init__(**kwargs) 183 | 184 | 185 | class PushAndPull_Env_v1(PushAndPullSokobanEnv): 186 | metadata = { 187 | 'render.modes': ['human', 'rgb_array', 'tiny_human', 'tiny_rgb_array'], 188 | 'render_modes': ['human', 'rgb_array', 'tiny_human', 'tiny_rgb_array'], 189 | } 190 | 191 | def __init__(self, **kwargs): 192 | kwargs['dim_room'] = kwargs.get('dim_room', (10, 10)) 193 | kwargs['max_steps'] = kwargs.get('max_steps', 150) 194 | kwargs['num_boxes'] = kwargs.get('num_boxes', 4) 195 | kwargs['num_gen_steps'] = kwargs.get('num_gen_steps', 50) 196 | super(PushAndPull_Env_v1, self).__init__(**kwargs) 197 | 198 | 199 | class PushAndPull_Env_v2(PushAndPullSokobanEnv): 200 | metadata = { 201 | 'render.modes': ['human', 'rgb_array', 'tiny_human', 'tiny_rgb_array'], 202 | 'render_modes': ['human', 'rgb_array', 'tiny_human', 'tiny_rgb_array'], 203 | } 204 | 205 | def __init__(self, **kwargs): 206 | kwargs['dim_room'] = kwargs.get('dim_room', (7, 7)) 207 | kwargs['max_steps'] = kwargs.get('max_steps', 150) 208 | kwargs['num_boxes'] = kwargs.get('num_boxes', 2) 209 | kwargs['num_gen_steps'] = kwargs.get('num_gen_steps', 50) 210 | super(PushAndPull_Env_v2, self).__init__(**kwargs) 211 | 212 | 213 | class PushAndPull_Env_v3(PushAndPullSokobanEnv): 214 | metadata = { 215 | 'render.modes': ['human', 'rgb_array', 'tiny_human', 'tiny_rgb_array'], 216 | 'render_modes': ['human', 'rgb_array', 'tiny_human', 'tiny_rgb_array'], 217 | } 218 | 219 | def __init__(self, **kwargs): 220 | kwargs['dim_room'] = kwargs.get('dim_room', (7, 7)) 221 | kwargs['max_steps'] = kwargs.get('max_steps', 150) 222 | kwargs['num_boxes'] = kwargs.get('num_boxes', 3) 223 | kwargs['num_gen_steps'] = kwargs.get('num_gen_steps', 50) 224 | super(PushAndPull_Env_v3, self).__init__(**kwargs) 225 | 226 | 227 | class PushAndPull_Env_v4(PushAndPullSokobanEnv): 228 | metadata = { 229 | 'render.modes': ['human', 'rgb_array', 'tiny_human', 'tiny_rgb_array'], 230 | 'render_modes': ['human', 'rgb_array', 'tiny_human', 'tiny_rgb_array'], 231 | } 232 | 233 | def __init__(self, **kwargs): 234 | kwargs['dim_room'] = kwargs.get('dim_room', (13, 11)) 235 | kwargs['max_steps'] = kwargs.get('max_steps', 300) 236 | kwargs['num_boxes'] = kwargs.get('num_boxes', 4) 237 | kwargs['num_gen_steps'] = kwargs.get('num_gen_steps', 50) 238 | super(PushAndPull_Env_v4, self).__init__(**kwargs) 239 | 240 | 241 | class PushAndPull_Env_v5(PushAndPullSokobanEnv): 242 | metadata = { 243 | 'render.modes': ['human', 'rgb_array', 'tiny_human', 'tiny_rgb_array'], 244 | 'render_modes': ['human', 'rgb_array', 'tiny_human', 'tiny_rgb_array'], 245 | } 246 | 247 | def __init__(self, **kwargs): 248 | kwargs['dim_room'] = kwargs.get('dim_room', (13, 11)) 249 | kwargs['max_steps'] = kwargs.get('max_steps', 300) 250 | kwargs['num_boxes'] = kwargs.get('num_boxes', 5) 251 | kwargs['num_gen_steps'] = kwargs.get('num_gen_steps', 50) 252 | super(PushAndPull_Env_v5, self).__init__(**kwargs) 253 | 254 | 255 | class TwoPlayer_Env0(TwoPlayerSokobanEnv): 256 | metadata = { 257 | 'render.modes': ['human', 'rgb_array', 'tiny_human', 'tiny_rgb_array'], 258 | 'render_modes': ['human', 'rgb_array', 'tiny_human', 'tiny_rgb_array'], 259 | } 260 | 261 | def __init__(self, **kwargs): 262 | kwargs['dim_room'] = kwargs.get('dim_room', (7, 7)) 263 | kwargs['max_steps'] = kwargs.get('max_steps', 200) 264 | kwargs['num_boxes'] = kwargs.get('num_boxes', 2) 265 | super(TwoPlayer_Env0, self).__init__(**kwargs) 266 | 267 | 268 | class TwoPlayer_Env1(TwoPlayerSokobanEnv): 269 | metadata = { 270 | 'render.modes': ['human', 'rgb_array', 'tiny_human', 'tiny_rgb_array'], 271 | 'render_modes': ['human', 'rgb_array', 'tiny_human', 'tiny_rgb_array'], 272 | } 273 | 274 | def __init__(self, **kwargs): 275 | kwargs['dim_room'] = kwargs.get('dim_room', (7, 7)) 276 | kwargs['max_steps'] = kwargs.get('max_steps', 200) 277 | kwargs['num_boxes'] = kwargs.get('num_boxes', 3) 278 | super(TwoPlayer_Env1, self).__init__(**kwargs) 279 | 280 | 281 | class TwoPlayer_Env2(TwoPlayerSokobanEnv): 282 | metadata = { 283 | 'render.modes': ['human', 'rgb_array', 'tiny_human', 'tiny_rgb_array'], 284 | 'render_modes': ['human', 'rgb_array', 'tiny_human', 'tiny_rgb_array'], 285 | } 286 | 287 | def __init__(self, **kwargs): 288 | kwargs['dim_room'] = kwargs.get('dim_room', (10, 10)) 289 | kwargs['max_steps'] = kwargs.get('max_steps', 200) 290 | kwargs['num_boxes'] = kwargs.get('num_boxes', 3) 291 | super(TwoPlayer_Env2, self).__init__(**kwargs) 292 | 293 | 294 | class TwoPlayer_Env3(TwoPlayerSokobanEnv): 295 | metadata = { 296 | 'render.modes': ['human', 'rgb_array', 'tiny_human', 'tiny_rgb_array'], 297 | 'render_modes': ['human', 'rgb_array', 'tiny_human', 'tiny_rgb_array'], 298 | } 299 | 300 | def __init__(self, **kwargs): 301 | kwargs['dim_room'] = kwargs.get('dim_room', (10, 10)) 302 | kwargs['max_steps'] = kwargs.get('max_steps', 200) 303 | kwargs['num_boxes'] = kwargs.get('num_boxes', 4) 304 | super(TwoPlayer_Env3, self).__init__(**kwargs) 305 | 306 | 307 | class TwoPlayer_Env4(TwoPlayerSokobanEnv): 308 | metadata = { 309 | 'render.modes': ['human', 'rgb_array', 'tiny_human', 'tiny_rgb_array'], 310 | 'render_modes': ['human', 'rgb_array', 'tiny_human', 'tiny_rgb_array'], 311 | } 312 | 313 | def __init__(self, **kwargs): 314 | kwargs['dim_room'] = kwargs.get('dim_room', (13, 11)) 315 | kwargs['max_steps'] = kwargs.get('max_steps', 200) 316 | kwargs['num_boxes'] = kwargs.get('num_boxes', 3) 317 | super(TwoPlayer_Env4, self).__init__(**kwargs) 318 | 319 | 320 | 321 | class TwoPlayer_Env5(TwoPlayerSokobanEnv): 322 | metadata = { 323 | 'render.modes': ['human', 'rgb_array', 'tiny_human', 'tiny_rgb_array'], 324 | 'render_modes': ['human', 'rgb_array', 'tiny_human', 'tiny_rgb_array'], 325 | } 326 | 327 | def __init__(self, **kwargs): 328 | kwargs['dim_room'] = kwargs.get('dim_room', (13, 11)) 329 | kwargs['max_steps'] = kwargs.get('max_steps', 200) 330 | kwargs['num_boxes'] = kwargs.get('num_boxes', 4) 331 | super(TwoPlayer_Env5, self).__init__(**kwargs) 332 | 333 | class Boxban_Env0(BoxobanEnv): 334 | metadata = { 335 | 'render.modes': ['human', 'rgb_array', 'tiny_human', 'tiny_rgb_array'], 336 | 'render_modes': ['human', 'rgb_array', 'tiny_human', 'tiny_rgb_array'], 337 | } 338 | 339 | def __init__(self, **kwargs): 340 | kwargs['max_steps'] = kwargs.get('max_steps', 200) 341 | kwargs['difficulty'] = kwargs.get('difficulty', 'unfiltered') 342 | kwargs['split'] = kwargs.get('split', 'train') 343 | super(Boxban_Env0, self).__init__(**kwargs) 344 | 345 | class Boxban_Env0_val(BoxobanEnv): 346 | metadata = { 347 | 'render.modes': ['human', 'rgb_array', 'tiny_human', 'tiny_rgb_array'], 348 | 'render_modes': ['human', 'rgb_array', 'tiny_human', 'tiny_rgb_array'], 349 | } 350 | 351 | def __init__(self, **kwargs): 352 | kwargs['max_steps'] = kwargs.get('max_steps', 200) 353 | kwargs['difficulty'] = kwargs.get('difficulty', 'unfiltered') 354 | kwargs['split'] = kwargs.get('split', 'valid') 355 | super(Boxban_Env0_val, self).__init__(**kwargs) 356 | 357 | class Boxban_Env0_test(BoxobanEnv): 358 | metadata = { 359 | 'render.modes': ['human', 'rgb_array', 'tiny_human', 'tiny_rgb_array'], 360 | 'render_modes': ['human', 'rgb_array', 'tiny_human', 'tiny_rgb_array'], 361 | } 362 | 363 | def __init__(self, **kwargs): 364 | kwargs['max_steps'] = kwargs.get('max_steps', 200) 365 | kwargs['difficulty'] = kwargs.get('difficulty', 'unfiltered') 366 | kwargs['split'] = kwargs.get('split', 'test') 367 | super(Boxban_Env0_test, self).__init__(**kwargs) 368 | 369 | class Boxban_Env1(BoxobanEnv): 370 | metadata = { 371 | 'render.modes': ['human', 'rgb_array', 'tiny_human', 'tiny_rgb_array'], 372 | 'render_modes': ['human', 'rgb_array', 'tiny_human', 'tiny_rgb_array'], 373 | } 374 | 375 | def __init__(self, **kwargs): 376 | kwargs['max_steps'] = kwargs.get('max_steps', 200) 377 | kwargs['difficulty'] = kwargs.get('difficulty', 'medium') 378 | super(Boxban_Env1, self).__init__(**kwargs) 379 | 380 | class Boxban_Env1_val(BoxobanEnv): 381 | metadata = { 382 | 'render.modes': ['human', 'rgb_array', 'tiny_human', 'tiny_rgb_array'], 383 | 'render_modes': ['human', 'rgb_array', 'tiny_human', 'tiny_rgb_array'], 384 | } 385 | 386 | def __init__(self, **kwargs): 387 | kwargs['max_steps'] = kwargs.get('max_steps', 200) 388 | kwargs['difficulty'] = kwargs.get('difficulty', 'medium') 389 | kwargs['split'] = kwargs.get('split', 'valid') 390 | super(Boxban_Env1_val, self).__init__(**kwargs) 391 | --------------------------------------------------------------------------------