在二维数组中生成具有 N 个图块的实体形状

Generating a solid shape with N tiles in a 2D array

我正在制作一个小行星生成器,它创建一个 bool 的二维数组。 生成器采用 int 参数 size,该参数应确定二维数组中有多少 True 个单元格。

如何保证输出数组中没有空洞并且单元格数量正确True

我看到了问题 Randomly Generating Clusters in a 2d Array,但我想不出一种方法将其应用到我的用例中,因为我需要知道必须生成的图块数量。

在下面的代码中,我随机放置了图块,然后使用元胞自动机进行平滑处理并确保没有孔洞,但保持正确数量的 True 单元格是个问题,特别是因为随机取出True 满足正确大小的单元格可能会产生孔洞。

def create_shape(size, seed):
    # init rng with seed
    rng = random.Random(seed)
    # initial grid values empty and full mass left
    # make the grid size by size so any shape could fit
    grid = [[False for x in range(size)] for y in range(size)]
    mass_remaining = size
    # guarantee the center is something
    center = size // 2
    grid[center][center] = True
    mass_remaining -= 1 # remember to reduce available mass
    # generate random values
    for x in range(size):
        for y in range(size):
            # skip the already filled in center
            if x == y == center:
                continue
            # assign random value
            value = bool(rng.randint(0, 1))
            grid[y][x] = value
            # remember to reduce mass
            if value: 
                mass_remaining -= 1
    # smoothen things out with cellular automata neighbor checking
    for x in range(size):
        for y in range(size):
            # skip the center
            if x == y == center:
                continue
            # get neighbors
            # set neighbors is the count of neighbors set to True
            set_neighbors = 0
            for i in range(-1, 2):
                for j in range(-1, 2):
                    # skip counting self
                    if i == j == 0:
                        continue
                    nx, ny = x + i, y + j
                    if 0 <= nx < size and 0 <= ny < size:
                        # only get in-range cells
                        if grid[ny][nx]:
                            set_neighbors += 1
            # more than 3 -> become True, less than 3 -> become False
            if set_neighbors > 3:
                grid[y][x] = True
                mass_remaining -= 1
            elif set_neighbors < 3:
                grid[y][x] = False
                mass_remaining += 1
            else:
                # otherwise leave it the same
                pass
    # find out how well the mass is staying "in-budget"
    print(mass_remaining)
    return grid

该函数经常 print 列出不同剩余质量数量的整个范围,例如,"debt" 中有 -14 或额外有 42。如果函数正常工作,我希望输出为 0

比如输出这样...

create_shape(8) ->

[ 0, 0, 0, 0, 0, 0,
  0, 1, 1, 0, 0, 0,
  0, 1, 1, 1, 0, 0,
  0, 1, 1, 1, 1, 0,
  0, 1, 1, 1, 1, 0 ]

...是实心的,但设置的图块太多。

您的问题没有唯一明确的答案,尤其是当基本任务 ("generate 2D asteroid shapes") 未明确说明且从根本上说是主观的时候。当然,原则上你总是可以生成一个 N 瓷砖实体形状,例如从左上角开始,从左到右和自上而下添加瓷砖,直到你有 N 个,但最终的形状可能不是很逼真或 "nice-looking" 小行星.

因此,我不会详细描述单个算法,而只是建议一些应该有效的方法,让您选择最适合您的方法:

  • 从单个中心图块开始,随机添加与现有图块相邻的新图块。在添加每一块瓷砖之前,检查添加它不会在小行星内部留下一个洞;如果可以,请选择另一块瓷砖。 (尽管有多种方法可以优化它,但连通性检查可能是该算法中成本最高的部分。特别是,您可以从仅检查新图块的现有直接邻居开始;如果它们都是连续的,则新图块无法桥接边缘的两个独立部分。)

  • 同上,但将连通性检查延迟到结束。如果发现任何洞,请将小行星边缘的瓷砖移到内部以填充它们。

  • 应用一个midpoint displacement algorithm to a circle. That is to say, use the algorithm to generate a random array of radii (with the same radius at each end) and then use those radii as distances from an arbitrarily chosen center point to the surface of your asteroid, as if you were plotting a radar graph. This won't give you an exact area of N tiles, but you can always scale the radii up or down until you get the size you want. The resulting shape will always be star-convex,这样就没有漏洞了。 (这可能最适用于相当大的 N。这种方案的一个优点是它也将以一种相当直接和有效的方式推广到 3D 形状:从随机化开始多面体并将中点位移应用于面。)

  • 使用任何 通常 生成 hole-free 小行星的算法。然后检查是否有漏洞,如果有,则重新启动。只要重启的概率够低,这个rejection sampling的方法还是比较有效的。

top-down 技术将是: