diff --git a/README.EN.md b/README.EN.md new file mode 100644 index 0000000..211e8d5 --- /dev/null +++ b/README.EN.md @@ -0,0 +1,259 @@ +A sokoban game solver +=========================== +This project proposed a AI solver for sokoban (japanese for warehouse keeper) which is a difficult computational problem. The algorithm being used consisted of BFS (breadth first search), DFS (depth first search), UCS (uniform cost search) and A* (A star search). + +## ToC + +* [0. Getting started](#0) +* [1. Overview](#1) +* [2. Evaluation](#2) + + +## 0. Overview + +1. The following libraries are needed:`sys`,`collections`,`numpy`,`heapq`,`time`. +2. (`numpy` requires separate installation, while the other ones are installed with a default Python installation). + +3. Run `sokoban.py` in your local environment. + +### Usage + +``` +$ python sokoban.py --help +``` +``` +Usage: sokoban.py [options] + +Options: + -h, --help show this help message and exit + -l SOKOBANLEVELS, --level=SOKOBANLEVELS + level of game to play (test1-10.txt, level1-5.txt) + -m AGENTMETHOD, --method=AGENTMETHOD + research method (bfs, dfs, ucs, astar) +``` + +`-l`:There are several set of maps included. Include ".txt" in the parameter. +`-m`:Valid options include `bfs`,`dfs`,`ucs`, and `astar`. + +### Examples + +``` +$ python sokoban.py -l test1.txt -m bfs +``` +``` +rUUdRdrUUluL +Runtime of bfs: 0.15 second. +``` +The first line indicates the action taken by the actor,where `u`,`d`,`l`,`r indicate up, down, left, or right movements, +The corresponding capital letter indicates the movement in corresponding direction WHILE PUSHING AGAINST THE BOS. +The second line includes the total run time. + + +## 1. Overview + +Sokoban is the [Box Pusher Game](https://www.mathsisfun.com/games/sokoban.html),where a player moves acrosds a map and pushes +boxes to designated spots on the map. + +### Example(`level1.txt`) + +**Visualized:** + +![](./img/level1.png) + +**Input:** + +``` + ##### +### # +#.&B # +### B.# +#.##B # +# # . ## +#B XBB.# +# . # +######## +``` + +Where `#` represent the wall,`.` is the destination(s),`B` is the box(es)(`X` indicates that it's already in place), +`&` is the actor/protagonist(`%` if overlaps with one of the destination),and whitespaces are unoccupied spaces the actor can move into. + +### Method + +搜索算法在这里的使用,简单理解就是当前状态下推箱者采取一个可能动作后,产生下一状态,以此类推,直到最终状态为结束状态。既然用到的是搜索算法,首先要确定的是state space graph(SSG)和search tree(ST)。如果直接把输入的整个地图作为SSG存储在数据结构中,会使内存的占用随搜索的进行而飙升,所以必须重新定义新的SSG,既能减少空间占用,又能代表足够的信息。在推箱子这个问题中,其实关键的部分仅为箱子的位置和人的位置,因为墙和目的地的位置都是不变的。所以定义SSG的样式如下所示,其中第一个tuple代表当前人的坐标,第二个tuple代表当前箱子的坐标。 + +``` +((2, 2), ((2, 3), (3, 4), (4, 4), (6, 1), (6, 4), (6, 5))) +``` + +而ST就是当前SSG基于推箱者可行的动作进行分枝,每一个动作都会导致新的不同的SSG产生,最终形成很多个branch和node。只要所有箱子的坐标和目的地的坐标完全契合,就说明游戏结束(胜利)。但推箱子游戏,往往会把箱子推到一些位置,比如说死角,这种局面其实也会导致游戏结束(失败),就没有继续走下去的必要了。因此,利用这些导致死局的pattern可以帮助我们修剪树(prune tree),使分裂的node减少许多,进而减少内存的占用。下图展示一些死局的pattern,以一个箱子为中心,如果周围一圈内出现这些情况,则说明该当前态没有再分裂下去的必要了。 + +![](./img/dead_patterns.jpg) + +另外为了避免推箱者做无意义的移动,比如不推箱子来回晃悠,我们要让每次分枝后的SSG与同一条分枝上的SSG不重复,即某个“人的位置和所有箱子的位置”不能重复出现。 + +然后就是算法的部分,BFS和DFS就不多说了,一个“每条路都走一小步慢慢走”,一个”一条路走到死再走下一条“。而UCS加入了cost function,就是”挑最节省成本的路走“,这里的cost function定义为:到目当前状态为止,所有没推着箱子走的步数。这样能够激励推箱者尽可能做有意义的移动,而不是不推箱子逛该。最后A*,在cost function上加入heuristic function,目的是激励推箱者不单去推箱子,并且还要把箱子推往目的地。这里的heuristic function用的是manhatten distance,定义为:到目当前状态为止,所有箱子按顺序排列的位置和所有目的地按顺序排列的位置,两两间的manhatten distance总和。 + + +## 2. Evaluated + +* BFS: + +``` +$ python sokoban.py -l test1.txt -m bfs +rUUdRdrUUluL +Runtime of bfs: 0.15 second. + +$ python sokoban.py -l test1.txt -m bfs +rUUdRdrUUluL +Runtime of bfs: 0.13 second. + +$ python sokoban.py -l test2.txt -m bfs +UUddrrrUU +Runtime of bfs: 0.01 second. + +$ python sokoban.py -l test3.txt -m bfs +LrdrddDLdllUUdR +Runtime of bfs: 0.27 second. + +$ python sokoban.py -l test4.txt -m bfs +llldRRR +Runtime of bfs: 0.01 second. + +test5.txt: more than 1 minute. + +$ python sokoban.py -l test6.txt -m bfs +dlluRdrUUUddrruuulL +Runtime of bfs: 0.02 second. + +$ python sokoban.py -l test7.txt -m bfs +LUUUluRddddLdlUUUUluR +Runtime of bfs: 1.25 second. + +$ python sokoban.py -l test8.txt -m bfs +llDDDDDDldddrruuLuuuuuuurrdLulDDDDDDlllddrrUdlluurRdddrruuLUUUUUUluRdddddddrddlluUdlluurRdrUUUUUU +Runtime of bfs: 0.30 second. + +$ python sokoban.py -l level1.txt -m bfs +RurrddddlDRuuuuLLLrdRDrddlLdllUUdR +Runtime of bfs: 35.04 second. +``` + +* DFS: + +``` +$ python sokoban.py -l test1.txt -m dfs +rrUrdllluRRlldrrrUlllururrDLrullldRldrrUruLrdllldrrdrUlllurrurDluLrrdllldrrdrUU +Runtime of dfs: 0.09 second. + +$ python sokoban.py -l test2.txt -m dfs +rrrUlldlUrrrUlldrrdllluU +Runtime of dfs: 0.01 second. + +$ python sokoban.py -l test3.txt -m dfs +rrdldrdllDRlldlUrrrdrruLrdllLrrrululldRllldRlurrrdrruLrdllUrrdllLrrrullllldRRRlllurrrrrdLrulllllUdrrrrrdlLrrullllldrRRlllurrrrrdLrullluRldrrrdlLrrullllldrRRlllurrrrrdLrulllururulluLrrrdlddldrrrdlLrrullllldrRRlllurrrrrdLrulUlldrrrdlLrrullllldrRRlllurrrrrdLrulllurrUldrdrdlLrrullllldrRRlllurrrrrdLrulllurrululurrDDldldrrrdlLrrulllururDlldrdrruLrdllLrrrululldRllldRlurrrdLrrruLrdlllulldRRRlllurrurrdrdLrulL +Runtime of dfs: 0.36 second. + +$ python sokoban.py -l test4.txt -m dfs +rrrdllllLrrrrrdllllllluRRRR +Runtime of dfs: 0.01 second. + +test5.txt: more than 1 minute. + +$ python sokoban.py -l test6.txt -m dfs +rrdlllluRRlldrrrruLrdllUUUddrrdlllluuuurRlldddrrrruuuLL +Runtime of dfs: 0.02 second. + +$ python sokoban.py -l test7.txt -m dfs +rdllllurRlldrrrruulLrrdLrdllllurRlldrrrruullLrrrdLrdllllurRlldrrrruulluulldDrrrrdLrdllllUrdrrrulLrrdlllluUrrrrdllLrrrullllUdrrrrdllldlUrrrrulluululldRRllurrrrrdddllldrrrdlllluUrrrruuullllldrDDrrrrdllldlUrrrrulluUlllurrRllldrrrddrrdllllUrrrruuuLrdddllllUdrruuluRllldRRllurrrDllddrrrruuuLrdddlllluurrDDrrdlLrrdlllluRRlldrrrruulllluuruRllldrrrddrrdLrdllllurRlldrrrruuuuuLrdddlllldrrdrUrdllllurrulluuruRllldrrrddlldrrrruLrdllllurRlldrrrruuuuLrdddLrdlllluuuruRllldrrrdDrruuuLrdddlllluuruRllldrrrddrrdlLrrdlllluurrrruuuLrdddllllddrrrrullLrrrdllllUrrrrulluulldDrrrruuulLrrdddlllluulurRRllldrrrddrrdllldlUrrrruuuuLrdddllldrrrdlllluUrrrruuulLrrdddlluulllurRRllldrrrddrrdlllluUdrrrruuuLrdddlllluUruRldrddrrdlllluuuluR +Runtime of dfs: 0.78 second. + +$ python sokoban.py -l test8.txt -m dfs +llDlurrrdLrullldRDDDDDlllddrrdrruuLrddllulluurrruuuuulurrrdLrullldRdddddlllddrrUruLruuuuulurrrdLrullDDDDDDlddlluuRRllddrrdrruuLrddllulluurrDrUldrrddllUlluurrrUdlllddrrUruLruUddldrrddllulluuRlddrrdrruuluLruuUdddldrrddllulluuRlddrrdrruuluLruuuUddddldrrddllulluuRlddrrdrruuluLruuuuUluRldrdddddldrrddllulluuRRllddrrdrruulUUUUUU +Runtime of dfs: 0.11 second. + +level1.txt: more than 1 minute. +``` + +* UCS: + +``` +$ python sokoban.py -l test1.txt -m ucs +rURdrUUlLdlU +Runtime of ucs: 0.09 second. + +$ python sokoban.py -l test2.txt -m ucs +UUddrrrUU +Runtime of ucs: 0.01 second. + +$ python sokoban.py -l test3.txt -m ucs +LrdrddDLdllUUdR +Runtime of ucs: 0.17 second. + +$ python sokoban.py -l test4.txt -m ucs +llldRRR +Runtime of ucs: 0.01 second. + +test5.txt: more than 1 minute. + +$ python sokoban.py -l test6.txt -m ucs +dlluRdrUUUddrruuulL +Runtime of ucs: 0.02 second. + +$ python sokoban.py -l test7.txt -m ucs +LUUUluRddddLdlUUUUluR +Runtime of ucs: 0.94 second. + +$ python sokoban.py -l test8.txt -m ucs +llDDDDDDldddrruuLuuuuuuurrdLulDDDDDDlllddrrUdlluurRdddrruuLUUUUUUluRdddddddrddlluUdlluurRdrUUUUUU +Runtime of ucs: 0.31 second. + +$ python sokoban.py -l level1.txt -m ucs +RurrddddlDRuuuuLLLrdRDrddlLdllUUdR +Runtime of ucs: 33.22 second. +``` + +* A star + +``` +$ python sokoban.py -l test1.txt -m astar +rUUdRdrUUluL +Runtime of astar: 0.01 second. + +$ python sokoban.py -l test2.txt -m astar +UUddrrrUU +Runtime of astar: 0.01 second. + +$ python sokoban.py -l test3.txt -m astar +LrdrddDLdllUUdR +Runtime of astar: 0.01 second. + +$ python sokoban.py -l test4.txt -m astar +llldRRR +Runtime of astar: 0.00 second. + +$ python sokoban.py -l test5.txt -m astar +uruLdlUURUdRdrUUllLdlU +Runtime of astar: 0.11 second. + +$ python sokoban.py -l test6.txt -m astar +dlluRdrUUUddrruuulL +Runtime of astar: 0.02 second. + +$ python sokoban.py -l test7.txt -m astar +LUUUluRddddLdlUUUUluR +Runtime of astar: 0.16 second. + +$ python sokoban.py -l test8.txt -m astar +llDDDDDDldddrruuLuuuuuuurrdLulDDDDDDlllddrrUdlluurRdddrruuLUUUUUUluRdddddddrddlluUdlluurRdrUUUUUU +Runtime of astar: 0.33 second. + +$ python sokoban.py -l level1.txt -m astar +RurrdLLLrrrdddlDRlLdllUUdRRurruuulldRDrddL +Runtime of astar: 0.85 second. +``` + +表现最好的是A*。BFS、UCS和A*输出的推箱者动作一致,DFS虽然也能找到解决方式但会出现逛该现象,显然其他方法会让结果更简单一些。后续可以提升的方面有: + +* 定义更简单的SSG +* 更加全面的dead pattern +* 更加适合的cost function和heuristic function diff --git a/sokobanLevels/windFantasyNexusElvesFoggyMine1.txt b/sokobanLevels/windFantasyNexusElvesFoggyMine1.txt new file mode 100644 index 0000000..5064440 --- /dev/null +++ b/sokobanLevels/windFantasyNexusElvesFoggyMine1.txt @@ -0,0 +1,9 @@ + #### + #### # +## B # +# # #B# +#.&.. ## +## # # # +## B # +## ##### +#####