
本文介绍一种基于一维数组和方向偏移的通用获胜检测算法,适用于任意尺寸棋盘和连珠长度(如8×8棋盘上检测5子同色对角线、横线或竖线),避免嵌套循环边界错误,提升代码可读性与鲁棒性。
在实现大型变体井字棋(如8×8五子连珠)时,传统二维遍历易因索引越界、方向遗漏或逻辑耦合导致漏判——尤其在检测主对角线(↘)、副对角线(↙)、横向(→)和纵向(↓) 四类获胜模式时。原问题中尝试用双重循环枚举对角线起点,却因 col 变量未重置、步长固定、未覆盖所有起始位置而失败。
更优解是采用“以落子点为中心,按方向动态扩展” 的策略:每次玩家点击后,仅检查该位置可能引发获胜的4个方向(右、下、右下、左下),并预先判断该方向是否具备足够空间容纳5子序列(即边界可行性校验)。核心思想是将二维坐标映射为一维索引(index = row * side + col),再通过偏移量(offset) 表示方向向量:
- 向右 → offset = 1
- 向下 → offset = side(即8)
- 右下 → offset = side + 1(即9)
- 左下 → offset = side - 1(即7)
以下为关键逻辑的精简实现(兼容Java 8+):
private static final int SIDE = 8;
private static final int TO_WIN = 5;
private static int[] board = new int[SIDE * SIDE]; // 0=empty, 1=red, 2=green, 3=blue
// 检查某次落子(index)是否构成获胜
private static int checkWin(int index) {
if (board[index] == 0) return 0;
int row = index / SIDE;
int col = index % SIDE;
// 预先计算各方向是否可达(避免越界)
boolean canRight = col + TO_WIN <= SIDE;
boolean canDown = row + TO_WIN <= SIDE;
boolean canRightDown = canRight && canDown;
boolean canLeftDown = col + 1 >= TO_WIN && canDown; // left-down: need at least TO_WIN cols from col
// 四方向逐一验证
if (canRight && checkDirection(index, 1, TO_WIN)) return board[index];
if (canDown && checkDirection(index, SIDE, TO_WIN)) return board[index];
if (canRightDown && checkDirection(index, SIDE+1, TO_WIN)) return board[index];
if (canLeftDown && checkDirection(index, SIDE-1, TO_WIN)) return board[index];
return 0;
}
// 沿指定offset偏移,检查连续TO_WIN个格子是否均为同一玩家
private static boolean checkDirection(int start, int offset, int count) {
int player = board[start];
for (int i = 1; i < count; i++) {
int next = start + i * offset;
// 边界二次防护(虽已预判,但强健性建议保留)
if (next < 0 || next >= board.length || board[next] != player) {
return false;
}
}
return true;
}✅ 优势总结:
- 零冗余遍历:仅检查落子点关联的4个方向,时间复杂度 O(1)(常数级);
- 边界安全:通过 canXXX 布尔标志提前剪枝,杜绝 ArrayIndexOutOfBoundsException;
- 高可扩展性:修改 SIDE 和 TO_WIN 即可适配其他规格(如15×15五子棋、10×10六子棋);
- 逻辑解耦:方向偏移抽象为整数,易于添加新规则(如马步、环形等非标准模式)。
⚠️ 注意事项:
- 若使用 Color.RED/GREEN/BLUE 直接比较背景色(如原始代码),请确保按钮背景未被UI委托或抗锯齿干扰——推荐改用状态枚举或整型标记(如 board[i] = PLAYER_RED)替代 getBackground(),避免因渲染差异导致误判;
- 多线程环境下需对 board 数组加锁,或采用 AtomicIntegerArray;
- 调试时可用 printout() 辅助可视化(参考答案中已提供清晰的字符化棋盘输出)。
此方案摒弃了易错的多重嵌套循环,转而用数学化偏移与函数式思维构建获胜判定,既保证正确性,又为后续AI评估、胜负回放等功能预留良好接口。










