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

地图瓦片修改算法无限递归:成因排查与修复方案

问题分析与修复

核心错误点

  1. 错误的入口条件限制
    propagateChanges函数中原本用rDist <3 && cDist <3 && (rDist + cDist <3)判断是否处理当前瓦片,这个条件基于初始调用的瓦片坐标(而非中心坐标)做距离限制,完全违背“远离中心才递增”的逻辑,导致大量应处理的瓦片被跳过,同时不该处理的瓦片被错误触发递归。

  2. 相邻瓦片的中心距离判断复用当前值
    处理相邻瓦片时,复用当前瓦片的centerCoords(是否在中心4步内),而非单独检查相邻瓦片的位置。这会导致当前瓦片不在中心范围内,但相邻瓦片在中心范围内时,错误触发相邻瓦片的递归调用。

  3. 对当前瓦片的无意义递归
    无论当前瓦片是否已达最大值4,只要不在中心范围内就会递归调用自身,导致瓦片到4之后仍会触发一次递归,使depth超过4并触发overflow标记。

  4. isFurtherFromAll函数的逻辑漏洞
    原函数仅通过compared.r + compared.c ===0排除距离完全相同的情况,但当相邻瓦片在某一方向更远、另一方向更近但总曼哈顿距离相等时,会错误允许递归;同时未以曼哈顿距离总和判断是否更远,逻辑不严谨。

修复后的代码

修改后的JavaScript部分

const rows = document.getElementsByTagName('tr');
var tiles = [];
for (var r = 0; r < rows.length; r++) {
  tiles[r] = [];
  for (var c = 0; c < rows[r].cells.length; c++) {
    tiles[r][c] = {
      r: r,
      c: c,
      tile: rows[r].cells[c]
    };
  }
}

function getTile(r, c) {
  if (r > -1 && r < 4 && c > -1 && c < 5) {
    return tiles[r][c];
  }
}

function propagateChanges(currentTile, depth) {
  if (depth > maxDepth) {
    maxDepth = depth;
  }
  // 超过最大允许深度,直接返回
  if (depth > 4) {
    overflowed = true;
    return;
  }

  const val = Number(currentTile.tile.textContent);
  // 瓦片值不符合条件,直接返回
  if (val >= 4 || val <= 0) {
    return;
  }

  const currentCenterCoords = isCenterWithin4Tiles(currentTile.r, currentTile.c);
  // 当前瓦片在中心4步内,不需要递增
  if (currentCenterCoords) {
    return;
  }

  // 递增瓦片值
  currentTile.tile.textContent = val + 1;
  const newVal = val + 1;
  // 只有当新值还没到4时,才继续递归当前瓦片
  if (newVal < 4) {
    setTimeout(() => {
      propagateChanges(currentTile, depth + 1);
    }, 1000);
  }

  // 处理相邻瓦片
  const adjacents = [
    getTile(currentTile.r - 1, currentTile.c),
    getTile(currentTile.r + 1, currentTile.c),
    getTile(currentTile.r, currentTile.c - 1),
    getTile(currentTile.r, currentTile.c + 1)
  ];

  for (let i = 0; i < adjacents.length; i++) {
    const adjTile = adjacents[i];
    if (!adjTile) continue;

    const adjVal = Number(adjTile.tile.textContent);
    // 相邻瓦片值不符合条件,跳过
    if (adjVal >= 4 || adjVal <= 0) continue;

    const adjCenterCoords = isCenterWithin4Tiles(adjTile.r, adjTile.c);
    // 相邻瓦片在中心4步内,跳过
    if (adjCenterCoords) continue;

    // 检查相邻瓦片是否比当前瓦片离所有中心更远
    if (isFurtherFromAll(adjTile, currentTile, currentCenterCoords)) {
      setTimeout(() => {
        propagateChanges(adjTile, depth + 1);
      }, 1000);
    }
  }
}

function locateCenter(currentTile, R, C, depth) {
  if (depth > maxDepth) {
    maxDepth = depth;
  }
  if (depth > 4) {
    overflowed = true;
    return;
  }

  const val = Number(currentTile.tile.textContent);
  const rDist = Math.abs(currentTile.r - R);
  const cDist = Math.abs(currentTile.c - C);

  if (val > 0 && (rDist < 4 && cDist < 4 && (rDist + cDist < 4))) {
    const adjacents = [
      getTile(currentTile.r - 1, currentTile.c),
      getTile(currentTile.r + 1, currentTile.c),
      getTile(currentTile.r, currentTile.c - 1),
      getTile(currentTile.r, currentTile.c + 1)
    ];
    let hasLowerNumberedNeighbor = false;

    for (let i = 0; i < adjacents.length; i++) {
      const adjTile = adjacents[i];
      if (!adjTile) continue;

      const adjVal = Number(adjTile.tile.textContent);
      if (adjVal === 0) {
        return;
      } else if (adjVal < 4 && adjVal < val) {
        hasLowerNumberedNeighbor = true;
        locateCenter(adjTile, R, C, depth + 1);
      }
    }

    if (!hasLowerNumberedNeighbor) {
      propagateChanges(currentTile, 0);
    }
  }
}

function isCenterWithin4Tiles(r, c) {
  const coords = [];
  // 检查曼哈顿距离<=3的区域(即4步以内)
  for (let rAway = -3; rAway <= 3; rAway++) {
    for (let cAway = -3; cAway <= 3; cAway++) {
      if (Math.abs(rAway) + Math.abs(cAway) > 3) continue;
      const targetR = r + rAway;
      const targetC = c + cAway;
      if (targetR < 0 || targetR >= 4 || targetC < 0 || targetC >=5) continue;
      const val = tiles[targetR][targetC].tile.textContent;
      if (val === '0') {
        coords.push({ r: targetR, c: targetC });
      }
    }
  }
  return coords.length > 0 ? coords : false;
}

function isFurtherFromAll(next, current, comparisons) {
  // 遍历所有中心,检查next的曼哈顿距离是否都比current远
  for (const center of comparisons) {
    const currentDist = Math.abs(current.r - center.r) + Math.abs(current.c - center.c);
    const nextDist = Math.abs(next.r - center.r) + Math.abs(next.c - center.c);
    // 如果next距离任何一个中心更近或相等,则返回false
    if (nextDist <= currentDist) {
      return false;
    }
  }
  return true;
}

var maxDepth = 0;
var overflowed = false;

function progressReport() {
  console.log("Maximum depth reached in recursive function: " + maxDepth);
  if (overflowed) {
    console.warn("OVERFLOW");
  } else {
    setTimeout(progressReport, 3000);
  }
}
progressReport();

var clicked = false;
document.getElementById("clickHere").addEventListener('click', function() {
  if (!clicked) {
    clicked = true;
    tiles[0][4].tile.textContent = '4';
    locateCenter(tiles[0][4], 0, 4, 0);
  }
});

CSS部分

table {
  font-size: 18px;
}

table td {
  width: 20px;
  height: 20px;
}

HTML部分

<body>
  <table>
    <tr>
      <td>2</td>
      <td>2</td>
      <td>2</td>
      <td>1</td>
      <td id="clickHere">0</td>
    </tr>
    <tr>
      <td>1</td>
      <td>1</td>
      <td>2</td>
      <td>2</td>
      <td>1</td>
    </tr>
    <tr>
      <td>0</td>
      <td>0</td>
      <td>1</td>
      <td>2</td>
      <td>2</td>
    </tr>
    <tr>
      <td>0</td>
      <td>0</td>
      <td>1</td>
      <td>2</td>
      <td>3</td>
    </tr>
  </table>
</body>

修复说明

  1. 移除错误的入口条件:propagateChanges现在直接检查瓦片值和中心距离,不再依赖初始调用坐标的限制。
  2. 独立检查相邻瓦片的中心距离:每个相邻瓦片都单独调用isCenterWithin4Tiles,确保只有不在中心范围内的瓦片才会被处理。
  3. 终止当前瓦片的递归条件:只有当瓦片递增后的值小于4时,才继续递归当前瓦片,避免无意义的调用。
  4. 简化isFurtherFromAll逻辑:直接比较曼哈顿距离的总和,确保相邻瓦片确实比当前瓦片离所有中心更远,彻底避免循环递归。

内容的提问来源于stack exchange,提问作者Quack E. Duck

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.27 21:57:01