Javascript 如何修复扫雷舰中超过的最大调用堆栈大小

Javascript 如何修复扫雷舰中超过的最大调用堆栈大小,javascript,html,css,minesweeper,Javascript,Html,Css,Minesweeper,我今天用纯js和css制作了一个扫雷舰。单击一个块时,使用递归打开其他块。首先,我将其用于10x10板。它工作得非常好。但是现在当我做了一个50x50板。这是错误的 未捕获范围错误:超过最大调用堆栈大小 这是我的完整代码。这很重要,但您只需关注递归调用的openBlock函数。50x50板上只有10个地雷。因此,在几乎所有情况下,除了地雷之外,所有区块都应该开放。但由于错误,某些块未打开 /-1=>我的 //0=>一个不接触任何地雷的区块 //1=>块接触1地雷 //等 //辅助方法 //qu

我今天用纯js和css制作了一个扫雷舰。单击一个块时,使用递归打开其他块。首先,我将其用于
10x10
板。它工作得非常好。但是现在当我做了一个
50x50
板。这是错误的

未捕获范围错误:超过最大调用堆栈大小

这是我的完整代码。这很重要,但您只需关注递归调用的
openBlock
函数。
50x50
板上只有10个地雷。因此,在几乎所有情况下,除了地雷之外,所有区块都应该开放。但由于错误,某些块未打开

/-1=>我的
//0=>一个不接触任何地雷的区块
//1=>块接触1地雷
//等
//辅助方法
//querySelector和querySelectorAll的缩写
const qs=str=>document.querySelector(str);
const qsa=str=>document.querySelectorAll(str);
//创建元素的步骤
常量ce=({tag,style,…rest})=>{
常量元素=document.createElement(标记);
如果(休息){
for(让k在rest中){
元素[k]=剩余[k];
}
}
返回元素;
};
常量main=qs(“#main”);
//主要变量
让len,wid,矿山,街区;
让isplay=true;
//帮助数据
让touchingBlockArr=[
[1, 0],
[0, 1],
[1, 1],
[1, -1]
];
touchingBlockArr=touchingBlockArr.concat(
touchingBlockArr.map(x=>x.map(a=>a))
);
//对象为不同的数字指定颜色。
常数colorObj={
“-1”:“红色”,
0:“灰色”,
1:“蓝色”,
2:“橙色”,
3:“绿色”,
4:“紫色”,
5:“栗色”
};
//创建新游戏的函数。
函数newGame(l,w,m=10){
len=l;
wid=w;
地雷=m;
main.innerHTML=“”;
游戏=[];
区块=[];
createBoard();
}
//创建板
函数createBoard(){
for(设i=0;i{
if(块[i+y]&块[i+y][j+x]){
return+块[i+y][j+x].dataset.value=-1;
}
}).长度;
}
val=val==未定义?-1:val;
块[i][j].setAttribute(“数据值”,val);
块[i][j].style.color=colorObj[val];
}
}
}
函数openSingleBlock(td){
让val=+td.dataset.value;
如果(val==-1){
}否则{
td.innerHTML=val | |“”;
}
td.classList.add(“打开”);
}
//单击鼠标左键时
函数onBlockClick(){
if(this.classList.contains(“flagged”))返回false;
让val=+this.dataset.value;
//如果我的被点击了。
如果(val==-1){
openSingleBlock(this);
}
//空块
else if(val==0){
openBlock(this);
openSingleBlock(this);
}
//用于接触地雷的石块。
否则{
openSingleBlock(this);
}
}
//递归打开块的函数
函数openBlock(td){
常量[x,y]=td.id.split(“,”).map(编号);
//如果块不是空的,则不要继续。
如果(+td.dataset.value!==0),则返回false;
设touchingBlocks=touchingBlockArr.map([dx,dy])=>[x+dx,dy+y]);
开放单块(td);
touchingBlocks.forEach([x,y])=>{
//检查块是否超出范围的步骤
如果(块[x]==未定义)返回false;
如果(块[x][y]==未定义)返回false;
设val=+块[x][y].dataset.value;
设td=块[x][y];
//不是地雷
如果(val!=-1){
//不要碰我的,不要打开,不要标记。
如果(
val==0&&
!td.classList.contains(“已打开”)
) {
openBlock(td);
}
//触碰地雷
否则{
开放单块(td);
}
}
});
}
新游戏(50,50)
正文{
字体系列:草书;
}
.街区{
高度:10px;
宽度:10px;
文本对齐:居中;
边框:1px纯黑;
背景颜色:浅灰色;
滤光片:亮度(0.8);
光标:指针;
字体大小:0.25rem;
盒影:1px 1px c10px黑色;
背景尺寸:包含;
}
.block:悬停{
滤光片:亮度(1);
}
.打开{
背景色:rgb(255、255、255);
滤光片:亮度(1);
}
#主要{
边界塌陷:塌陷;
}
.我的{
背景图片:url(mine.jpg);
背景尺寸:包含;
}
.标记{
背景图片:url(flag.png);
背景尺寸:包含;
}

通常,解决递归导致堆栈溢出的最简单方法是不使用递归

在这种情况下,可以使用以下算法:

function openBlocks(startingBlock) {
    let blocksToOpen = [startingBlock];

    while (blocksToOpen.length) {
        let nextBlock = blocksToOpen.pop();

        if (!nextBlock.classList.contains("opened")) {
            // openBlock returns an array of empty neighbors that are not
            // yet open
            let additionalBlocksToOpen = openBlock(nextBlock);

            if (additionalBlocksToOpen.length) {
                blocksToOpen = [...blocksToOpen, ...additionalBlocksToOpen];
            }
        }
    }
}
当用户点击一个空区块时(此处,“空区块”指没有矿井和相邻矿井的区块):

  • 将块推到空堆栈中
  • 当堆栈为非空时:
  • 弹出