我读过维基百科关于迷宫算法的文章,我决定写我自己的迷宫解算器。它使用死胡同填充,因为它似乎很容易实现(似乎,我说)。
我编写的代码相当庞大,包括文档和测试在内的179行代码,但我喜欢它解决了我的简单示例。
我在我的代码中包含了一些ASCII动画代码,因为看到一个迷宫以编程方式得到解决是令人难以置信的满意的。
import doctest
import time
BLOCKED = 1
EMPTY = 0
SMALL_MAZE = [
[1, 1, 1, 1, 1],
[0, 0, 1, 1, 1],
[1, 0, 0, 0, 0],
[1, 1, 0, 1, 1],
[1, 1, 1, 1, 1] ]
BIG_TEXT_MAZE = """
**************************
* *
** ********************* *
* * *
* **** ****************
* *
**************************
"""
SMALL_TEXT_MAZE = """
******************
** **** *** ****
**** * *
** ** **** *****
**** ** ** **
* **** *****
*******
******************
"""
def maze_repr(maze):
"""
Prints a nice textual represenatation of a maze.
'*' indicates a wall, ' ' a corridor.
>>> print(maze_repr( [ [1, 1, 1, 1], [0, 0, 1, 1], [1, 0, 0, 0], [1, 1, 1, 1] ]))
****
**
*
****
"""
string_maze = ""
for y in range(len(maze)):
for x in range(len(maze[0])):
string_maze += "*" if maze[y][x] else ' '
string_maze += "\n"
return string_maze[:-1]
def read_maze(text_maze):
"""
The opposite of `maze_repr`, given a textual maze, builds
a list of lists from it.
>>> read_maze("****\\n **\\n* \\n****")
[[1, 1, 1, 1], [0, 0, 1, 1], [1, 0, 0, 0], [1, 1, 1, 1]]
"""
return list(map((lambda row: [1 if square == '*' else 0 for square in row]),
(i for i in text_maze.split("\n") if i)))
def nears(curr_x, curr_y, maze):
"""
Returns the squares from which you may go doing one
step in any of the four cardinal directions
(NORD, EST, WEST, SOUTH).
The result consists of:
* 2 squares if (x, y) is on the corner,
* 3 squares if (x, y) is on the side,
* 4 squares otherwise.
>>> list(nears(1, 0, [ [2, 1, 3], [1, 4, 6] ]))
[3, 4, 2]
"""
for (x, y) in ( (curr_x + 1, curr_y), (curr_x, curr_y + 1),
(curr_x - 1, curr_y), (curr_x, curr_y - 1)):
try:
if x >= 0 and y >= 0:
yield maze[y][x]
except IndexError:
pass
def is_dead_end(x, y, maze):
"""
Is the square at (x, y) of the maze a dead end?
A wall can not be a dead end.
Interenstingly enough, using 2 or 1 instead of 3
creates some nice chambers in the labirith instead of solving it.
A square circled by 4 walls is also a dead end, in the case of
solitary closed inacessible rooms in the labirinth.
>>> is_dead_end(2, 1, read_maze(SMALL_TEXT_MAZE))
True
"""
if maze[y][x] == BLOCKED:
return False
return list(nears(x, y, maze)).count(BLOCKED) in (3, 4)
def fill_one_dead_end(maze):
"""
Fills the first encountered dead end of the maze.
>>> print(maze_repr(fill_one_dead_end(SMALL_MAZE)))
*****
***
*
*****
*****
"""
new = maze[:]
found_dead_end = False
for y in range(len(maze)):
for x in range(len(maze[0])):
if (not found_dead_end) and is_dead_end(x, y, maze):
found_dead_end = True
new[y][x] = BLOCKED
else:
new[y][x] = maze[y][x]
return new
def has_at_least_one_dead_end(maze):
"""
Does the maze have at least one dead end?
>>> has_at_least_one_dead_end(read_maze(BIG_TEXT_MAZE))
True
"""
for y in range(len(maze)):
for x in range(len(maze[0])):
if is_dead_end(x, y, maze):
return True
return False
def solve_maze(maze, logging = False):
"""
Solves mazes where the corridors are one wide,
filling all the dead ends.
>>> maze = read_maze(SMALL_TEXT_MAZE)
>>> print(maze_repr(solve_maze(maze)))
******************
******************
**** **********
**** ** **********
**** ** **********
** **********
*******
******************
>>> maze = read_maze(BIG_TEXT_MAZE)
>>> print(maze_repr(solve_maze(maze)))
**************************
**************************
**************************
* ****************
* **** ****************
* ****
**************************
"""
if logging:
print(maze_repr(maze), end="\n\n")
while has_at_least_one_dead_end(maze):
maze = fill_one_dead_end(maze)
if logging:
print("\n"*50)
print(maze_repr(maze), end="\n\n")
time.sleep(0.2)
return maze
def main():
solve_maze(read_maze(SMALL_TEXT_MAZE), logging = True)
solve_maze(read_maze(BIG_TEXT_MAZE), logging = True)
doctest.testmod()
if __name__ == "__main__":
main()发布于 2015-09-21 17:11:04
以下是一些一般性意见:
maze参数。这个共享状态是一个迹象,表明您可以创建一个类并在其上定义方法,而不是不断地传递状态。关于个别职能的一些详细反馈意见:
maze_repr()函数有点烦琐。您可以使用一些str.join()s: row_strings = []表示迷宫中的行:row_strings.append(‘..join(’*‘如果另有’)返回‘\n’..join(Row_strings),这也避免了在末尾删除尾行。(顺便说一句,我建议去掉str.strip()的尾随空格,而不是取一片。)而且,repr(obj)通常意味着“一个字符串,我可以eval()来获得一个等价的实例”;这是接近的,但并不完全是您在这里所做的。read_maze()函数在单行上做得太多了。阅读和解压非常困难;为了便于阅读,请将其放在更多的行上。在text_maze.splitlines()中,您的代码有一个干净的、分隔的版本: rows = []:如果没有行: rows.append(1如果char =‘*’0表示字符在行)返回行,我更喜欢str.splitlines()而不是.split('\n')。我认为它的可读性更强,而且我的可移植性更强(对于'\n'不是行尾的平台)。nears()函数基本上是很好的,只是我会更改docstring,这样方向的顺序与返回的顺序相同。保持一致是件好事。
fill_one_dead_end函数中,只要找到一个死胡同,就可以立即返回,并删除found_dead_end变量。迷宫的其余部分已经复制到了新的,所以这里没有更多的工作要做:只是提前退出并返回新的。https://codereview.stackexchange.com/questions/105268
复制相似问题