首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >用Python填充迷宫的死胡同

用Python填充迷宫的死胡同
EN

Code Review用户
提问于 2015-09-21 14:41:26
回答 1查看 1.7K关注 0票数 2

我读过维基百科关于迷宫算法的文章,我决定写我自己的迷宫解算器。它使用死胡同填充,因为它似乎很容易实现(似乎,我说)。

我编写的代码相当庞大,包括文档和测试在内的179行代码,但我喜欢它解决了我的简单示例。

我在我的代码中包含了一些ASCII动画代码,因为看到一个迷宫以编程方式得到解决是令人难以置信的满意的。

代码语言:javascript
复制
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()
EN

回答 1

Code Review用户

发布于 2015-09-21 17:11:04

以下是一些一般性意见:

  • 所有函数都采用maze参数。这个共享状态是一个迹象,表明您可以创建一个类并在其上定义方法,而不是不断地传递状态。
  • 您的许多代码都很忙,而且很难阅读。我在下面的反馈中提供了一些更具可读性的版本的示例;尽量不要将这么多的内容打包到一行中。
  • 您可以采用一种有趣的方法将结果打印到stdout。在迭代之间打印50条新行将确保以前的迭代从屏幕上消失(虽然不在我的显示器上;我有一个纵向显示),但是一个行为良好的程序不应该在我的终端窗口上乱涂乱画。我已经有一段时间没有使用它了,但是有一些像祝福这样的模块可以让您修改已经打印出来的内容。你可能还想看一下托马斯·巴林格( Thomas )的“PyCon talk 终端低语”,它有很多类似的聪明之处。
  • 有时候,您的代码会偏离PEP 8风格的指南。你也许该好好想想。

关于个别职能的一些详细反馈意见:

  • 您的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变量。迷宫的其余部分已经复制到了新的,所以这里没有更多的工作要做:只是提前退出并返回新的。
票数 3
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/105268

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档