我正在尝试使用 JavaScript 制作扫雷,我想编写一些代码,以便当您显示没有相邻地雷的图块时,它会显示其旁边的图块。如果这些瓷砖中的任何一个也没有相邻的地雷,它们也会暴露其相邻的瓷砖。这应该继续下去,直到您发现一片没有相邻地雷的瓷砖区域以及它们旁边的所有瓷砖为止。
以下是单击图块时运行的函数。
mines
是一个二维数组。如果值为 1,则存在地雷。
minesNearby
是一个 2D 数组,其中每个值都是该位置图块旁边的地雷数量。
checkedTiles
是一个二维数组。如果值为 1,则已单击图块。
findNeighbours
返回给定图块周围所有图块位置的数组。 (例如 [[19,5]、[19,4]、[19,3]、[18,3]...)
updateTile
更新 HTML 中的图块 div。
function openTile(y,x) {
console.log(`opening tile y ${y} and x ${x}`)
checkedTiles[y][x] = 1;
updateTile(y,x);
if (mines[y][x] == 1) {
// death
} else if (minesNearby[y][x] == 0) {
findNeighbours(y,x).forEach((tile,index) => {
console.log(`${tile[0]}+${tile[1]}`)
if (checkedTiles[tile[0]][tile[1]] == 0) {
openTile(tile[0],tile[1])
};
});
}
}
这对我来说看起来不错,但我确信我缺少一些东西。 当您单击没有相邻地雷的图块时,它有时会显示左上角的图块,有时不会显示任何内容。 (左上角的图块甚至不是
indNeighbours
数组中的第一个)。
如果左上角没有相邻地雷的瓷砖较多,则可能会出现露出瓷砖的对角线。
我该如何使其正常工作?
按照 zer00ne 在评论中的要求,如果没有 最小可重现示例,你的问题很难回答。您的函数看起来不错,但我建议您将
checkedTiles[][]
的检查移至函数的开头,而不是在循环遍历邻居期间。像这样的东西:
function openTile(y, x) {
// Check here!
if(checkedTiles[y][x]) return;
console.log(`opening tile y ${y} and x ${x}`);
checkedTiles[y][x] = 1;
updateTile(y, x);
if(mines[y][x] == 1) {
// death
} else if(minesNearby[y][x] == 0) {
findNeighbors(y, x).forEach(([y, x], index)) {
console.log(`${y}+${x}`);
openTile(y, x);
}
}
}
如果之前打开了图块,这将阻止任何代码运行,这可能是问题所在 - 取决于我们看不到的
updateTile()
函数。
我推荐这个,因为我最终制作了一个与你类似的工作函数。缩小后,看起来非常相似:
function openTile(mine) {
if(mine.opened) return;
mine.opened = true;
if(mine.mine) {
// death
} else {
if(mine.nearby == 0) {
mine.neighbors.forEach(neighbor => neighbor.open());
}
}
}
如果没有有关您的代码的更多信息,我只能为您提供帮助,但如果有帮助的话,我已经在此处包含了我的工作示例。它使用对象数组而不是数字数组,但函数名称和参数应该看起来相似。希望这对您有帮助。
var mines, size = 10, explosive = 0.25;
function start() {
mines = [];
for(var i = 0; i < size; i++) {
mines[i] = [];
for(var n = 0; n < size; n++) {
mines[i][n] = {
x: n,
y: i,
mine: Math.random() <= explosive,
opened: false,
get nearby() { return minesNearby(this.x, this.y); },
get neighbors() { return findNeighbors(this.x, this.y); },
open: function(initial) { openTile(this.x, this.y, initial); }
}
}
}
}
function restart() {
var game = document.querySelector('#game')
game.innerHTML = '';
game.classList.remove('won');
start();
createTiles();
}
function createTiles() {
for(var i = 0; i < size; i++) {
for(var n = 0; n < size; n++) {
var tile = document.createElement('button');
tile.mine = mines[i][n];
tile.mine.tile = tile;
tile.addEventListener('click', function() { this.mine.open(); }, { once: true });
document.querySelector('#game').append(tile);
}
}
}
function minesNearby(x, y) {
return findNeighbors(x, y).filter(v => v.mine).length;
}
function findNeighbors(x, y) {
var neighbors = [];
for(var i = Math.max(0, y-1); i < y+2 && i < mines.length; i++) {
for(var n = Math.max(0, x-1); n < x+2 && n < mines[i].length; n++) {
neighbors.push(mines[i][n]);
}
}
return neighbors;
}
function openTile(x, y, initial = true) {
var mine = mines[y][x];
if(mine.opened) return;
mine.opened = true;
mine.tile.classList.add('open');
if(mine.mine) {
mine.tile.innerHTML = '※';
mine.tile.classList.add('mine');
openAll();
if(initial) setTimeout(() => alert('You exploded!'), 10);
} else {
if(mine.nearby == 0) {
mine.neighbors.forEach(neighbor => neighbor.open());
}
mine.tile.innerText = mine.nearby;
mine.tile.style.color = `hsl(${120-120*(mine.nearby/8)}, 100%, 20%)`;
}
if(initial && didWin()) {
document.querySelector('#game').classList.add('won');
openAll();
setTimeout(() => alert('You win!'), 10);
}
}
function openAll() {
mines.forEach(row => row.forEach(mine => mine.open(false)));
}
function didWin() {
return mines.every(row => row.every(mine => (mine.opened && !mine.mine) || (!mine.opened && mine.mine)));
}
start();
addEventListener('DOMContentLoaded', () => {
document.querySelector('#restart').addEventListener('click', restart);
createTiles();
})
#game {
--size: 2rem;
display: flex;
margin: 2px;
width: calc(var(--size) * 10 + 18px);
flex-wrap: wrap;
gap: 2px;
button {
flex-basis: var(--size);
appearance: none;
width: var(--size);
height: var(--size);
max-height: var(--size);
padding: 0;
border: calc(var(--size) / 6) outset #EEE;
background: #EEE;
font-size: calc(var(--size) / 2);
color: black;
overflow: hidden;
}
button#restart {
width: auto;
}
button:hover:not(:active, .open) {
background: #F0F0F0;
border-color: #E0E0E0;
}
button:is(:active, .open) {
border-style: inset;
}
.mine {
background: #700;
color: white;
}
&.won .mine {
background: #070;
}
}
<div id='game'></div>
<button id='restart'>Restart</button>