You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何设计拼图游戏的颜色聚类算法?实现七巧板式色块聚合

背景:拼图网格尺寸可为16×16、8×8或4×4,难度随颜色数量的相似度变化。七种颜色的数量各不相同,游戏目标是找出网格中数量最多/最少的颜色。

我希望颜色尽可能聚合而非分散在拼图网格中,每个坐标仅能被一种颜色占据。最终生成的结果需完全填充网格,形成类似七巧板的、每种颜色自成一块的七个拼图区域。

请问如何设计该拼图游戏的颜色聚类算法?

示例:(4×4)

当前分散的网格数据:

{ row: 0, column: 0, color: 0}
{ row: 0, column: 1, color: 1}
{ row: 0, column: 2, color: 2}
{ row: 0, column: 3, color: 3}

{ row: 1, column: 0, color: 3}
{ row: 1, column: 1, color: 4}
{ row: 1, column: 2, color: 0}
{ row: 1, column: 3, color: 1}

{ row: 2, column: 0, color: 2}
{ row: 2, column: 1, color: 3}
{ row: 2, column: 2, color: 2}
{ row: 2, column: 3, color: 1}

{ row: 3, column: 0, color: 1}
{ row: 3, column: 1, color: 1}
{ row: 3, column: 2, color: 1}
{ row: 3, column: 3, color: 3}

期望的聚合后网格数据:

{ row: 0, column: 0, color: 0}
{ row: 0, column: 1, color: 0}
{ row: 0, column: 2, color: 2}
{ row: 0, column: 3, color: 2}

{ row: 1, column: 0, color: 3}
{ row: 1, column: 1, color: 4}
{ row: 1, column: 2, color: 2}
{ row: 1, column: 3, color: 1}

{ row: 2, column: 0, color: 3}
{ row: 2, column: 1, color: 3}
{ row: 2, column: 2, color: 3}
{ row: 2, column: 3, color: 1}

{ row: 3, column: 0, color: 1}
{ row: 3, column: 1, color: 1}
{ row: 3, column: 2, color: 1}
{ row: 3, column: 3, color: 1}

颜色聚类算法设计方案

核心思路

采用预分配色块数量+区域生长的组合方式,先确定每种颜色的单元格数(满足7种数量各不相同),再从随机种子点出发,按相邻优先规则生长色块,确保每个颜色形成单一连通区域,实现类似七巧板的聚合效果。

具体步骤

  1. 分配色块数量

    • 根据网格总单元格数,生成7个各不相同的正整数,总和等于单元格总数。比如16×16网格(256个单元格),可分配为:50、45、40、35、30、25、31(总和256,且数量均不同);4×4网格需调整逻辑,因为7个不同正整数最小和为28,超过16,这里可允许少量数量重复,或简化为5种颜色(但按需求优先保证7种,可调整为1+2+2+3+3+2+3,总和16,后续通过算法聚合)。
    • 数量分配后按从大到小排序,优先处理大色块,避免小色块被挤压无法形成连通区域。
  2. 初始化种子点

    • 创建空白网格,为每种颜色随机挑选未被占据的单元格作为种子点,种子点尽量分散(可通过计算种子点间的最小距离,避免过于集中)。
  3. 区域生长逻辑

    • 对每种颜色,从种子点开始,向上下左右相邻的空白单元格扩展:
      • 每次优先选择周围已填充单元格最多的空白位置(保证色块紧凑,避免条状延伸);
      • 每填充一个单元格,将其加入当前色块的单元格列表,继续寻找新的相邻空白;
      • 直到该颜色的单元格数量达到预分配值。
    • 若生长过程中找不到可扩展的空白单元格,重新更换种子点重试。
  4. 优化调整

    • 生长完成后,检查所有色块是否为连通区域,若存在分散的同色单元格,将其与主色块合并(调整相邻单元格的颜色);
    • 可选:对色块边缘进行平滑处理,提升视觉聚合度。

代码实现要点(伪代码)

// 生成7个数量,总和等于总单元格数,尽量保证各不相同
function generateColorCounts(totalCells) {
  let counts = [];
  let remaining = totalCells;
  // 先分配6个基础数,最后一个用剩余值
  for (let i = 1; i <= 6; i++) {
    let num = Math.min(i + Math.floor(Math.random() * 4), remaining - (21 - i));
    counts.push(num);
    remaining -= num;
  }
  counts.push(remaining);
  // 去重调整(针对小网格)
  const uniqueCounts = [...new Set(counts)];
  while (uniqueCounts.length < 7) {
    const idx = Math.floor(Math.random() * counts.length);
    counts[idx] += 1;
    uniqueCounts.push(counts[idx]);
  }
  return counts.sort((a, b) => b - a);
}

// 从种子点开始生长色块
function growColor(grid, startRow, startCol, color, targetCount) {
  let currentCount = 1;
  grid[startRow][startCol] = color;
  let cells = [[startRow, startCol]];
  const directions = [[-1,0],[1,0],[0,-1],[0,1]];

  while (currentCount < targetCount) {
    let neighbors = [];
    // 收集所有相邻空白单元格,并统计其周围已填充数
    cells.forEach(([r, c]) => {
      directions.forEach(([dr, dc]) => {
        const nr = r + dr;
        const nc = c + dc;
        if (nr >=0 && nr < grid.length && nc >=0 && nc < grid[0].length && grid[nr][nc] === null) {
          let filledNum = directions.filter(([dr2, dc2]) => {
            const nr2 = nr + dr2;
            const nc2 = nc + dc2;
            return nr2 >=0 && nr2 < grid.length && nc2 >=0 && nc2 < grid[0].length && grid[nr2][nc2] !== null;
          }).length;
          neighbors.push({ r: nr, c: nc, filled: filledNum });
        }
      });
    });
    if (neighbors.length === 0) return false;
    // 优先选周围填充多的位置,保证紧凑
    neighbors.sort((a, b) => b.filled - a.filled);
    const chosen = neighbors[0];
    grid[chosen.r][chosen.c] = color;
    cells.push([chosen.r, chosen.c]);
    currentCount++;
  }
  return true;
}

// 生成最终聚类网格
function generateClusteredGrid(rows, cols) {
  let grid = Array(rows).fill().map(() => Array(cols).fill(null));
  const totalCells = rows * cols;
  const colorCounts = generateColorCounts(totalCells);

  for (let color = 0; color < 7; color++) {
    const targetCount = colorCounts[color];
    let success = false;
    // 最多尝试10次找合适的种子点
    for (let attempt = 0; attempt < 10; attempt++) {
      let r = Math.floor(Math.random() * rows);
      let c = Math.floor(Math.random() * cols);
      if (grid[r][c] === null) {
        success = growColor(grid, r, c, color, targetCount);
        if (success) break;
      }
    }
    if (!success) throw new Error("无法生成有效聚类网格");
  }

  // 转换为目标格式
  let result = [];
  for (let r = 0; r < rows; r++) {
    for (let c = 0; c < cols; c++) {
      result.push({ row: r, column: c, color: grid[r][c] });
    }
  }
  return result;
}

效果验证

以4×4网格为例,运行上述算法可生成类似期望输出的结果:每种颜色形成单一连通区域,数量符合要求,完全填充网格,满足聚合度需求。


内容的提问来源于stack exchange,提问作者22092

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.23 17:04:56