
Double-Choco是一款由Nikoli杂志推出的棋盘益智游戏。其核心玩法是在一个由白色和灰色单元格组成的二维网格上,通过绘制实线将网格划分为若干个“块”。每个块必须包含一对形状(大小和形态)相同、颜色相反(白色和灰色)的区域,其中一个区域可以是另一个区域的旋转或镜像。某些区域可能包含一个数字,表示该颜色在该块中的单元格数量。
自动生成此类谜题面临的主要挑战在于:
为了有效地表示棋盘状态和进行区域操作,我们定义一个cell对象作为棋盘的基本单元。每个cell对象应包含其在网格中的位置信息、颜色、边界信息以及是否已被处理等状态。
let cell = {
x: Number, // 单元格的X坐标
y: Number, // 单元格的Y坐标
color: "white" | "gray", // 单元格的颜色
number: null | Number, // 如果有数字提示,则为数字,否则为null
top: true | false, // 上边界是否有实线(true表示有,false表示没有,即与上方单元格连通)
bottom: true | false, // 下边界是否有实线
left: true | false, // 左边界是否有实线
right: true | false, // 右边界是否有实线
taken: false, // 标记该单元格是否已被某个块占用(已处理)
block: [] // 用于存储该单元格所属的块中所有单元格的引用(或坐标)
};数据结构解释:
棋盘本身可以表示为一个二维数组,其中每个元素都是一个cell对象:let cells = Array(Y).fill(0).map(() => Array(X).fill(0));
在谜题生成过程中,我们需要频繁地识别出由连通单元格组成的区域,即“块”。这对于验证新放置的区域是否形成有效块、检查剩余空间是否可填充等至关重要。这里介绍一个基于深度优先搜索(DFS)或广度优先搜索(BFS)思想的递归算法。
take函数是块提取的核心。它以一个未被处理的单元格为起点,递归地探索所有与之连通的单元格,并将它们标记为已处理,同时收集到同一个块中。
/**
* 递归地遍历并标记连通的单元格,将其归属于同一个块。
* @param {object} currentCell - 当前正在处理的单元格。
* @param {object} blockOriginCell - 启动本次块提取的起始单元格,用于标识这个块的归属。
* @param {Array<Array<object>>} cells - 整个棋盘的二维单元格数组。
* @param {number} boardWidth - 棋盘宽度。
* @param {number} boardHeight - 棋盘高度。
*/
function take(currentCell, blockOriginCell, cells, boardWidth, boardHeight) {
// 边界检查:确保当前单元格在棋盘范围内
if (!currentCell || currentCell.taken) {
return;
}
currentCell.taken = true; // 标记当前单元格为已处理
blockOriginCell.block.push(currentCell); // 将当前单元格添加到起始单元格的块列表中
const { x, y } = currentCell;
// 向上探索
if (!currentCell.top && y > 0) {
take(cells[y - 1][x], blockOriginCell, cells, boardWidth, boardHeight);
}
// 向下探索
if (!currentCell.bottom && y < boardHeight - 1) {
take(cells[y + 1][x], blockOriginCell, cells, boardWidth, boardHeight);
}
// 向左探索
if (!currentCell.left && x > 0) {
take(cells[y][x - 1], blockOriginCell, cells, boardWidth, boardHeight);
}
// 向右探索
if (!currentCell.right && x < boardWidth - 1) {
take(cells[y][x + 1], blockOriginCell, cells, boardWidth, boardHeight);
}
}take函数工作原理:
要从整个棋盘中提取所有独立的块,我们需要遍历所有单元格,并对未被处理的单元格调用take函数。
/**
* 从整个棋盘中提取所有独立的块。
* @param {Array<Array<object>>} cells - 整个棋盘的二维单元格数组。
* @param {number} boardWidth - 棋盘宽度。
* @param {number} boardHeight - 棋盘高度。
* @returns {Array<Array<object>>} 包含所有提取出的块的数组,每个块是一个单元格数组。
*/
function extractAllBlocks(cells, boardWidth, boardHeight) {
// 重置所有单元格的 taken 状态和 block 数组,以确保每次提取都是新的。
// 在实际生成过程中,可能只需要在特定阶段(如验证)进行提取,无需每次都重置。
cells.flat().forEach(cell => {
cell.taken = false;
cell.block = []; // 清空之前的块信息
});
const allBlocks = [];
for (let y = 0; y < boardHeight; y++) {
for (let x = 0; x < boardWidth; x++) {
const currentCell = cells[y][x];
if (!currentCell.taken) {
// 如果当前单元格未被处理,则它是一个新块的起点
take(currentCell, currentCell, cells, boardWidth, boardHeight);
// 此时 currentCell.block 已经包含了这个新块的所有单元格
if (currentCell.block.length > 0) {
allBlocks.push(currentCell.block);
}
}
}
}
return allBlocks;
}流程解释:
通过这种方式,extractAllBlocks函数能够识别并返回棋盘上所有独立的连通区域(块)。
上述的cell数据结构和extractAllBlocks函数是构建Double-Choco谜题生成器的基础工具。以下是将其整合到完整生成流程中的思路:
初始化棋盘:
迭代填充棋盘:
添加数字提示:
本文提供了一个构建Double-Choco谜题生成器的基础框架,重点介绍了cell数据结构和基于递归的块提取算法。通过将这些基础工具与高级的形状匹配、回溯和违规检查逻辑相结合,开发者可以构建出能够自动生成可解Double-Choco谜题的系统。理解并有效利用这些数据结构和算法,是实现复杂棋盘游戏自动生成功能的关键。
以上就是Double-Choco 谜题生成:高效数据结构与算法实践的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号