├── 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 | |  |  |  |
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 |  |
23 | | FixedTarget-Sokoban-v1 | 10x10 | 160x160 | 4 |  |
24 | | FixedTarget-Sokoban-v2 | 7x7 | 112x112 | 2 |  |
25 | | FixedTarget-Sokoban-v3 | 7x7 | 112x112 | 3 |  |
26 |
27 |
--------------------------------------------------------------------------------
/docs/variations/README.md:
--------------------------------------------------------------------------------
1 | # Variations
2 |
3 | | Fixed Targets | Two Player | Push&Pull |
4 | | :---: | :---: | :---:
5 | |  |  |  |
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 |  | 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 |  | 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 |  | 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 | |  |  |  |
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 |  |
38 | | PushAndPull-Sokoban-v1 | 10x10 | 160x160 | 4 |  |
39 | | PushAndPull-Sokoban-v2 | 7x7 | 112x112 | 2 |  |
40 | | PushAndPull-Sokoban-v3 | 7x7 | 112x112 | 3 |  |
41 | | PushAndPull-Sokoban-v4 | 13x11 | 208x176 | 4 |  |
42 | | PushAndPull-Sokoban-v5 | 13x11 | 208x176 | 5 |  |
43 |
44 |
45 |
--------------------------------------------------------------------------------
/docs/variations/Boxoban.md:
--------------------------------------------------------------------------------
1 | # Variation: Boxoban
2 |
3 | | Example Game 1 |
4 | | :---: |
5 | |  |
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 |  |
32 | | Boxoban-Test-v0 | unfiltered | 10x10 | 112x112 | 4 |  |
33 | | Boxoban-Val-v0 | unfiltered | 10x10 | 112x112 | 4 |  |
34 | | Boxoban-Train-v1 | medium | 10x10 | 112x112 | 4 |  |
35 | | Boxoban-Val-v1 | medium | 10x10 | 112x112 | 4 |  |
36 |
--------------------------------------------------------------------------------
/docs/variations/TwoPlayer.md:
--------------------------------------------------------------------------------
1 | # Variation: Two Player
2 |
3 | | Example Game 1 | Example Game 2 | Example Game 3 |
4 | | :---: | :---: | :---:
5 | |  |  |  |
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 |  |
41 | | TwoPlayer-Sokoban-v1 | 7x7 | 160x160 | 3 |  |
42 | | TwoPlayer-Sokoban-v2 | 10x10 | 112x112 | 3 |  |
43 | | TwoPlayer-Sokoban-v3 | 10x10 | 112x112 | 4 |  |
44 | | TwoPlayer-Sokoban-v4 | 13x11 | 160x160 | 3 |  |
45 | | TwoPlayer-Sokoban-v5 | 13x11 | 160x160 | 4 |  |
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 | |  |  |  |
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 |  |  |
37 | | Floor | Empty |  |  |
38 | | Box Target | Empty |  |  |
39 | | Box | Off Target |  |  |
40 | | Box | On Target |  |  |
41 | | Player | Off Target |  |  |
42 | | Player | On Target |  |  |
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 |
89 |
90 |
92 | Figure 2: Masks for creating a topology 93 |
94 |
111 |
112 |