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

如何获取数组中的周边对象?及P5.js实现Connect4游戏四子连线获胜机制的问题排查

嘿,我来帮你搞定这两个技术问题!

1. 如何获取数组中的周边对象?

首先得明确,“周边对象”一般指二维数组中某个元素的8邻域元素(上下左右+四个对角线),当然你也可以根据需求只取4邻域(上下左右)。这里给你一套通用的实现思路:

  • 先处理边界判断:避免访问数组越界,比如要获取grid[y][x]的周边元素,得先确认y-1 >=0(上方存在)、y+1 < 总行数(下方存在)、x-1 >=0(左方存在)、x+1 < 总列数(右方存在),对角线则是这些条件的组合。
  • 用方向数组简化代码:把所有可能的方向写成一个数组,遍历这个数组就能一次性收集所有有效周边元素,不用写一堆if判断。

举个实用的代码例子:

function getSurroundingElements(grid, targetY, targetX) {
  const totalRows = grid.length;
  const totalCols = grid[0].length;
  // 定义8个方向:[y偏移量, x偏移量]
  const directions = [[-1,-1], [-1,0], [-1,1], [0,-1], [0,1], [1,-1], [1,0], [1,1]];
  const surroundingItems = [];

  for (const [dy, dx] of directions) {
    const newY = targetY + dy;
    const newX = targetX + dx;
    // 检查坐标是否在数组有效范围内
    if (newY >= 0 && newY < totalRows && newX >= 0 && newX < totalCols) {
      surroundingItems.push(grid[newY][newX]);
    }
  }

  return surroundingItems;
}

调用这个函数就能拿到目标位置所有有效的周边元素啦。如果只需要4邻域,删掉方向数组里的对角线方向就行。

2. Connect4游戏四子连线获胜判定问题

先分析你当前代码的几个核心问题:

  • 重复计数逻辑混乱:你同时检查左右、上下的正反方向,导致同一条连线被多次累加计数,比如水平方向的棋子会被左右两边的计数重复加,导致数值不准。
  • 状态未重置:connectedBlocksX/Y这些计数变量没有在每次检查前重置,会一直累积之前的数值,后续判断完全失效。
  • 递归落子的冗余检查:你在drop的递归过程中多次调用checkWin,其实只需要在棋子最终落定的位置检查一次就够了。

改进后的实现方案

核心思路是:每次落子后,从落定的位置出发,沿着4组对立方向(水平、垂直、两个对角线)分别遍历,统计同色棋子的总长度,只要任意一组达到4就判定获胜。

步骤1:替换checkWin函数

把你原来的checkWin换成这个更清晰的版本:

function checkWin(x, y) {
  const currentColor = grid[y][x].color;
  if (currentColor === "gray") return; // 空位置不用检查

  // 定义4组对立方向:每组包含两个相反的方向
  const directionPairs = [
    [[0, -1], [0, 1]],   // 左 ↔ 右(水平)
    [[-1, 0], [1, 0]],   // 上 ↔ 下(垂直)
    [[-1, -1], [1, 1]],  // 左上 ↔ 右下
    [[-1, 1], [1, -1]]   // 右上 ↔ 左下
  ];

  for (const [dirA, dirB] of directionPairs) {
    let count = 1; // 初始计数为当前棋子本身

    // 遍历第一个方向,统计同色棋子数
    let nextY = y + dirA[0];
    let nextX = x + dirA[1];
    while (nextY >= 0 && nextY < rows && nextX >= 0 && nextX < cols && grid[nextY][nextX].color === currentColor) {
      count++;
      nextY += dirA[0];
      nextX += dirA[1];
    }

    // 遍历对立方向,继续统计同色棋子数
    nextY = y + dirB[0];
    nextX = x + dirB[1];
    while (nextY >= 0 && nextY < rows && nextX >= 0 && nextX < cols && grid[nextY][nextX].color === currentColor) {
      count++;
      nextY += dirB[0];
      nextX += dirB[1];
    }

    // 只要任意一组方向的总长度≥4,就判定获胜
    if (count >= 4) {
      winner = currentColor;
      return; // 找到获胜条件后直接返回,不用继续检查
    }
  }
}

步骤2:简化drop函数的落子逻辑

你原来的递归落子逻辑太复杂,改成直接找到当前列最底部的空位置,落子后只检查一次获胜:

drop(){
  const colIndex = (this.x - 5) / 50;
  let targetRow = null;

  // 从底部往上找当前列第一个空位置
  for(let y = rows - 1; y >= 0; y--){
    if(!grid[y][colIndex].isFilled){
      targetRow = y;
      break;
    }
  }

  if(targetRow === null) return; // 当前列已满,无法落子

  // 设置落子的状态和颜色
  grid[targetRow][colIndex].isFilled = true;
  grid[targetRow][colIndex].color = turn === 0 ? "red" : "yellow";
  
  // 只在落定的位置检查一次获胜
  checkWin(colIndex, targetRow);

  // 切换玩家回合
  turn = turn === 0 ? 1 : 0;
}

额外优化:简化网格初始化

你原来手动写6行grid[y] = [0,...]可以改成一行:

var grid = Array(rows).fill().map(() => Array(cols).fill(0));

这样修改后,获胜判定逻辑就准确啦,而且代码可读性和维护性都提升了不少。

内容的提问来源于stack exchange,提问作者Maddox Fox

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.30 10:42:28