【问题标题】:Generating all unique crossword puzzle grids生成所有独特的填字游戏网格
【发布时间】:2011-12-01 00:37:19
【问题描述】:

我想生成特定网格大小的所有唯一填字游戏网格(4x4 是一个很好的大小)。所有可能的谜题,包括非唯一的谜题,都由一个具有网格区域长度的二进制字符串表示(在 4x4 的情况下为 16),因此所有可能的 4x4 谜题都由范围 0 内所有数字的二进制形式表示到 2^16。

生成这些很容易,但我很好奇是否有人有一个好的解决方案来以编程方式消除无效和重复的案例。例如,所有具有单列或单行的谜题在功能上都是相同的,因此消除了这 8 个案例中的 7 个。此外,根据填字游戏惯例,所有方格必须是连续的。我已经成功删除了所有重复的结构,但我的解决方案需要几分钟才能执行,并且可能并不理想。我不知道如何检测连续性,所以如果有人对此有想法,将不胜感激。

我更喜欢 Python 中的解决方案,但可以使用您喜欢的任何语言编写。如果有人愿意,我可以发布我的 python 代码来生成所有网格并删除重复项,虽然它可能很慢。

【问题讨论】:

  • 方格可以“聚集在一起”吗?例如。 65535(所有方格都使用)是有效的 4x4 网格吗?
  • @douplep:这绝对是一个有效的网格。不过,我不太确定我理解您的意思 - 邻接规则意味着所有有效的网格必须聚集在一起。
  • 另一个问题:单列是否与单行相同。 IE。两个相互旋转/反射的网格是否相同?
  • 根据原始帖子,这些将是重复的:> 所有单列或单行的谜题在功能上都是相同的,因此消除了这 8 个案例中的 7 个。

标签: crossword


【解决方案1】:

免责声明:除了所有测试之外,大部分未经测试确实会通过过滤掉一些网格来产生影响,并且修复了一些发现的错误。当然可以优化。

def is_valid_grid (n):
    row_mask = ((1 << n) - 1)
    top_row  = row_mask << n * (n - 1)

    left_column  = 0
    right_column = 0

    for row in range (n):
        left_column  |= (1 << (n - 1)) << row * n
        right_column |= 1 << row * n

    def neighborhood (grid):
        return (((grid   & ~left_column)  << 1)
                | ((grid & ~right_column) >> 1)
                | ((grid & ~top_row)      << n)
                | (grid                   >> n))

    def is_contiguous (grid):
        # Start with a single bit and expand with neighbors as long as
        # possible.  If we arrive at the starting grid then it is
        # contiguous, else not.
        part = (grid ^ (grid & (grid - 1)))
        while True:
            expanded = (part | (neighborhood (part) & grid))
            if expanded != part:
                part = expanded
            else:
                break

        return part == grid

    def flip_y (grid):
        rows = []
        for k in range (n):
            rows.append (grid & row_mask)
            grid >>= n

        for row in rows:
            grid = (grid << n) | row

        return grid

    def rotate (grid):
        rotated = 0
        for x in range (n):
            for y in range (n):
                if grid & (1 << (n * y + x)):
                    rotated |= (1 << (n * x + (n - 1 - y)))

        return rotated

    def transform (grid):
        yield flip_y (grid)

        for k in range (3):
            grid = rotate (grid)
            yield grid
            yield flip_y (grid)

    def do_is_valid_grid (grid):
        # Any square in the topmost row?
        if not (grid & top_row):
            return False

        # Any square in the leftmost column?
        if not (grid & left_column):
            return False

        # Is contiguous?
        if not is_contiguous (grid):
            return False

        # Of all transformations, we pick only that which gives the
        # smallest number.
        for transformation in transform (grid):
            # A transformation can produce a grid without a square in the topmost row and/or leftmost column.
            while not (transformation & top_row):
                transformation <<= n

            while not (transformation & left_column):
                transformation <<= 1

            if transformation < grid:
                return False

        return True

    return do_is_valid_grid

def valid_grids (n):
    do_is_valid_grid = is_valid_grid (n)
    for grid in range (2 ** (n * n)):
        if do_is_valid_grid (grid):
            yield grid

for grid in valid_grids (4):
    print grid

【讨论】:

  • 这看起来很棒!我现在正在重构它,这样我就可以打印出所有有效的、唯一的网格来进行人工检查。我还没有业力,但如果可以的话,我会投票赞成。
  • 我认为你现在也可以投票。无论如何,不​​幸的是,这并不完全正确。连续性测试被打破。如果我能想出一个,我会用正确的测试进行编辑。
  • 它确实得出了一些不连续的结果,但它是否也过滤掉了有效的网格?出于我的目的,我可以手动筛选出一些无效但我不能添加那些被任意删除的内容
  • 现在看起来很棒,但它似乎缺少一些有效的网格......例如,一个带有一个正方形的网格。
  • 你确定吗?随着后面的修复,is_valid_grid (4) (0x8000) 在此处计算为True。选择最上面的行/最左边的列可能不是最好的选择。我想您可以将其切换为底行/最右边的列,然后网格 1 应该变为有效。请注意,现在应该在三个地方进行切换:变量定义、基础网格测试和转换后测试(加上转换移位)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-05-22
  • 1970-01-01
  • 2022-07-18
相关资源
最近更新 更多