如何设计拼图游戏的颜色聚类算法?实现七巧板式色块聚合
背景:拼图网格尺寸可为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种数量各不相同),再从随机种子点出发,按相邻优先规则生长色块,确保每个颜色形成单一连通区域,实现类似七巧板的聚合效果。
具体步骤
分配色块数量
- 根据网格总单元格数,生成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,后续通过算法聚合)。
- 数量分配后按从大到小排序,优先处理大色块,避免小色块被挤压无法形成连通区域。
初始化种子点
- 创建空白网格,为每种颜色随机挑选未被占据的单元格作为种子点,种子点尽量分散(可通过计算种子点间的最小距离,避免过于集中)。
区域生长逻辑
- 对每种颜色,从种子点开始,向上下左右相邻的空白单元格扩展:
- 每次优先选择周围已填充单元格最多的空白位置(保证色块紧凑,避免条状延伸);
- 每填充一个单元格,将其加入当前色块的单元格列表,继续寻找新的相邻空白;
- 直到该颜色的单元格数量达到预分配值。
- 若生长过程中找不到可扩展的空白单元格,重新更换种子点重试。
- 对每种颜色,从种子点开始,向上下左右相邻的空白单元格扩展:
优化调整
- 生长完成后,检查所有色块是否为连通区域,若存在分散的同色单元格,将其与主色块合并(调整相邻单元格的颜色);
- 可选:对色块边缘进行平滑处理,提升视觉聚合度。
代码实现要点(伪代码)
// 生成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
相关产品推荐
相关产品推荐

