【问题标题】:Issue with Eller's algorithm - maze generation埃勒算法的问题 - 迷宫生成
【发布时间】:2018-10-02 12:32:37
【问题描述】:

我正在尝试使用Eller's algorithm 创建一个迷宫。 Internet 上没有太多关于此特定算法的信息。所以我对这个算法有一些困难,因为有些事情我并不完全理解。但无论如何,这就是我现在所拥有的:

class Cell:
    def __init__(self, row, col, number, right_wall, bottom_wall):
        self.row = row
        self.col = col
        self.number = number    # defines which set this block is in
        self.right_wall = right_wall
        self.bottom_wall= bottom_wall

# every block includes 5x5 px white space + 1px on it's left and 1px on it's bottom for walls (if the block has ones)
def create_block(row, col, right_wall, bottom_wall):
    for i in range(row-2, row+4):   # since the path is 5px wide
        for j in range(col-2, col+4):   # i go to a central pixel of block's white space and make it thick
            maze[i, j] = [255, 255, 255]    # in other words i draw 2px border around central pixel
    if right_wall:  # if the block has wall on it's right
        create_right_wall(row, col, 1)  # draw right wall with a function
    if bottom_wall: # if the block has wall on it's bottom
        create_bottom_wall(row, col ,1) # draw bottom wall with a function


def create_right_wall(row, col, color):
    if color == 0:  # if color parameter = 0
        for i in range(row-2, row+4):
            maze[i, col+3] = [0, 0, 0]  # I draw (create) black wall
    else:
        for i in range(row-2, row+4):
            maze[i, col+3] = [255, 255, 255]    # I draw white wall (if I need to delete a wall)


def create_bottom_wall(row, col, color):
    if color == 0:
        for i in range(col-2, col+4):
            maze[row+3, i] = [0, 0, 0]
        if row + 4 < maze_height and maze[row+4, col-3][0] == 0:    # sometimes there's 1px gap between bottom walls
            maze[row+3, col-3] = [0, 0, 0]  # so I fill it with black pixel
    else:
        for i in range(col-2, col+4):
            maze[row+3, i] = [255, 255, 255]    # draws white wall (deleting wall)


def creating():
    current_point = [3, 3]  # where the top-left block appears ([3,3] is for the block's center)

    set_count = 1   # to have unique set numbers, it increases every time after it has been used

    for row in range(height):   # going from top to bottom
        # I print some unnecessary information just to know what's happening
        print("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - row", row) # current row being created
        if row == 0:    # if it's the first row
            for col in range(width):
                create_block(current_point[0], current_point[1], False, False)

                blocks[row].append(Cell(current_point[0], current_point[1], set_count, False, False))
                set_count += 1  # since the set number has been used, the next block will have another one

                current_point[1] += 6   # the center of the next block is 6px away from the center of the current one

        elif row == height - 1: # if it's the last row
            for i in range(width):
                create_block(current_point[0], current_point[1], False, False)
                blocks[row].append(Cell(current_point[0], current_point[1], blocks[row-1][i].number, False, True))

                current_point[1] += 6

                # I don't know why I do this. Just trying to create the last line correctly
                if (not blocks[row-1][i].bottom_wall and not blocks[row-1][i + 1].bottom_wall) and \
                        (blocks[row-1][i].number == blocks[row-1][i + 1].number):
                    create_right_wall(blocks[row][i].row, blocks[row][i].col, 0)
            break   # since it's the last row, don't do anything else



        else:
            for col in range(width):
                create_block(current_point[0], current_point[1], False, False)

                print("block on top has set:", blocks[row-1][col].number, end=" ")

                if blocks[row-1][col].bottom_wall:  # if upper block has bottom wall
                    blocks[row].append(Cell(current_point[0], current_point[1], set_count, False, False))
                    print("upper block has a bottom wall, so set for the current block is", set_count)
                    set_count += 1
                else:   # if not, the current block's set will be the same as for the upper one
                    blocks[row].append(Cell(current_point[0], current_point[1],
                                            blocks[row-1][col].number, False, False))
                    print("current block set", blocks[row-1][col].number)

                current_point[1] += 6

            # just to show set numbers for current row
            for i in blocks[row]:
                print(i.number, end=" ")


        # putting a wall between blocks of the same set (we don't want to have loops in our maze)
        for i in range(len(blocks[row]) - 1):
            if blocks[row][i].number == blocks[row][i+1].number:
                blocks[row][i].right_wall = True
                create_right_wall(blocks[row][i].row, blocks[row][i].col, 0)
                print("put a wall between", i+1, "и", i+2, "because", blocks[row][i].number, "=",\
                      blocks[row][i+1].number)


        for i in range(len(blocks[row]) - 1):
            if random.choice([0, 1]) == 0 and blocks[row][i].number != blocks[row][i+1].number:
                blocks[row][i + 1].number = blocks[row][i].number
                print("connect block", i + 1, "and", i + 2)
            else:
                blocks[row][i].right_wall = True
                create_right_wall(blocks[row][i].row, blocks[row][i].col, 0)


        # to know what set nu,bers we have in the current row
        sets_in_row = []
        for i in blocks[row]:
            print(i.number, end=" ")
            sets_in_row.append(i.number)

        sets_in_row = sorted(set(sets_in_row), key=lambda x: sets_in_row.index(x))
        print(sets_in_row)


        current_bl = 0  # current block in a
        for mn in sets_in_row:  # for every set number in a row
            current_mn_length = sum([p.number == mn for p in blocks[row]])  # how many blocks has this set number
            if current_mn_length > 1: # if the current set has more than 1 block
                quantity = random.randrange(1, current_mn_length)   # random number of bottom walls
                # whick blocks in the current set will have a bottom wall
                bloxxxx = random.sample(list(range(current_bl, current_bl + current_mn_length)), quantity)

                # just to know how it's going
                print("\nblock:")
                for y in range(current_bl, current_bl + current_mn_length):
                    print("pos:", y + 1, end=" ")
                    print("  num:", blocks[row][y].number,)


                print("bottom walls for")
                for i in bloxxxx:
                    print(i+1, end=" ")

                print()

                for b in bloxxxx:
                    blocks[row][b].bottom_wall = True
                    create_bottom_wall(blocks[row][b].row, blocks[row][b].col, 0)

                current_bl += current_mn_length

            else:
                print("\n set length of", current_bl + 1, "=", current_mn_length, "so no bottom wall\n")
                current_bl += current_mn_length

        current_point[0] += 6   # go to the center of the next row block
        current_point[1] = 3    # go to the center of the first block of the next row


while True:
    width = int(input("Width: "))
    height = int(input("height: "))

    maze_width = width * 6 + 1
    maze_height = height * 6 + 1

    maze = np.full((maze_height, maze_width, 3), 0, dtype=np.uint8)
    paths = []
    all_positions = width * height
    break

blocks = [[] for h in range(height)]

creating()

for h in range(maze_height):
    maze[h][maze_width-1] = [0, 0, 0]

for w in range(maze_width):
    maze[maze_height-1][w] = [0, 0, 0]

img = Image.fromarray(maze, 'RGB')
img.save('maze.png')
img.show()

我已尝试解释每一行,以便您了解我的代码中发生了什么。我知道我有很多不必要的代码。那是因为我尝试了很多方法来生成正确的迷宫。

问题是迷宫并不总是正确生成。例如,这个有循环。但它不应该。

看看这个 这个有循环和隔离块。那是因为设定的数字。在这个迷宫中,第二行有这些集合:

我随机连接块后,一行2被22隔开。所以第二个块不是集合2中唯一的一个。

请帮我修复我的代码。如果您对我的代码有任何疑问,我会尽力为您提供更多相关信息。

【问题讨论】:

  • 我没有时间筛选你所有的代码,但如果你需要提示,看起来最后一个违规是在创建底墙。从提供的链接中:“如果一个单元格是其集合中的唯一成员,则不要创建底墙”您为第 22 组中的单元格创建了一个底墙。找出原因。
  • 当您随机水平连接单元格时,您只需更改右侧的设置编号以匹配左侧的设置。您必须更改该集合中的所有个单元格,而不仅仅是一个。

标签: python python-3.x maze


【解决方案1】:

我刚刚遇到了同样的问题!

经过长时间的思考,我得出的结论是,网上提供的教程都没有正确解释“连接集”方面!这就是以与您展示的方式相同的方式创建循环的原因。

让我在这里尝试简化算法步骤:

  1. 将一排分成不同的组,放置右墙。
  2. 为每组至少制作一个底部开口。
  3. 用相同的集合分隔单元格以防止循环。
  4. 从第 1 步开始

现在需要考虑一个关键方面:集合是一行中标有相同“数字”(代表一个集合)的所有单元格的总和。 所以如果你有:

    | 1   1   1 | 2 | 1 |

集合 1 是所有有 1 的单元格,包括最后一个。 所以如果你想做一个单一的向下打开,你也可以像这样选择打开最后一个单元格底部:

    | 1   1   1 | 2 | 1 |
    |-----------| ↓ | ↓ |

这很好用,因为第 1 组至少有一个开口。

现在循环的问题是当你加入集合时:

    | 1   1   1 | 2 | 1 |
    | ↓ |-------| ↓ | ↓ | ← let's make 2 down openings for set 1
    | 1   4   5 |(2   1)| ← let's say you want to merge set 1 and 2
    | 2   4   5 | 2   2 | ← also change the set of the first cell!

您需要将行中所有单元格的集合更改为被替换的相同集合!

这在教程中从未显示,但在算法解释中说明!

这样,“第一个单元格与最后两个单元格向上连接”的信息在您下迷宫时会保留下来,如果第 2 组向下蔓延到第三个单元格,则会提示您放一堵墙保留这样的循环:

    | 1   1   1   1   1 |
    | ↓ | ↓ | ↓ |---| ↓ |
    | 1 | 1 | 1 | 2 | 1 |
    | ↓ |-------| ↓ | ↓ |
    | 1   4   4 |(2   1)|
    | 2 | 4   4 | 2   2 |
    | ↓ |---| ↓ | ↓ |---|
    |(2   5)  4 | 2   6 |
    | 2   2 | 4 | 2 | 6 |
    |---| ↓ | ↓ | ↓ | ↓ |
    | 7  (2   4)|(2   6)|
    | 7 | 2   2 | 2   2 |
    | ↓ |---| ↓ | ↓ |---|
    |(7   8)  2  (2   9)| ← here you HAVE to put a wall between the two 2!
    | 7   7 | 2 | 2   2 |
    |---| ↓ | ↓ |-------|
    | a   7   2   b   c | ← last row connect all sets
    | a   a   a   a   a |

生成的迷宫会是这样的:

    _____________________
    |                   |
    |   |   |   |---|   |
    |   |   |   |   |   |
    |   |-------|   |   |
    |   |       |       |
    |   |---|   |   |---|
    |       |   |   |   |
    |---|   |   |   |   |
    |   |       |       |
    |   |---|   |   |---|
    |       | Y |     X |
    |---|   |   |-------|
    |                   |
    |-------------------|

看看为什么没有环路,X 仍然与迷宫的其余部分相连,因为在最后一步中,我们在第 2 组中的 Y 单元上做了一个向下的开口!

如果我们没有正确合并集合,结果会是这样:

    _____________________
    |                   |
    |   |   |   |---|   |
    |   |   |   |   |   |
    |   |-------|   |   |
    |   |       |       |
    |   |---|   |   |---|
    |       |   |   |   |
    |---|   |   |   |   |
    |   |       |       |
    |   |---|   |   |---|
    |       | 1   2   2 | ← 1 and 2 are actually in the same set!
    |---|   |   |-------|
    |                   |
    |-------------------|

希望这会有所帮助,虽然已经很晚了!

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-11-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多