export const patterns = { five: new RegExp('11111'), blockfive: new RegExp('211111|111112'), four: new RegExp('011110'), blockFour: new RegExp('10111|11011|11101|211110|211101|211011|210111|011112|101112|110112|111012'), three: new RegExp('011100|011010|010110|001110'), blockThree: new RegExp('211100|211010|210110|001112|010112|011012'), two: new RegExp('001100|011000|000110|010100|001010'), } export const shapes = { FIVE: 5, BLOCK_FIVE: 50, FOUR: 4, FOUR_FOUR: 44, // 双冲四 FOUR_THREE: 43, // 冲四活三 THREE_THREE: 33, // 双三 BLOCK_FOUR: 40, THREE: 3, BLOCK_THREE: 30, TWO_TWO: 22, // 双活二 TWO: 2, NONE: 0, }; export const performance = { five: 0, blockFive: 0, four: 0, blockFour: 0, three: 0, blockThree: 0, two: 0, none: 0, total: 0, } // 使用字符串匹配的方式实现的形状检测,速度较慢,但逻辑比较容易理解 export const getShape = (board, x, y, offsetX, offsetY, role) => { const opponent = -role; let emptyCount = 0; let selfCount = 1; let opponentCount = 0; let shape = shapes.NONE; // 跳过为空的节点 if (board[x + offsetX + 1][y + offsetY + 1] === 0 && board[x - offsetX + 1][y - offsetY + 1] === 0 && board[x + 2 * offsetX + 1][y + 2 * offsetY + 1] === 0 && board[x - 2 * offsetX + 1][y - 2 * offsetY + 1] === 0 ) { return [shapes.NONE, selfCount, opponentCount, emptyCount]; } // two 类型占比超过一半,做一下优化 // 活二是不需要判断特别严谨的 for (let i = -3; i <= 3; i++) { if (i === 0) continue; const [nx, ny] = [x + i * offsetX + 1, y + i * offsetY + 1]; if (board[nx] === undefined || board[nx][ny] === undefined) continue; const currentRole = board[nx][ny]; if (currentRole === 2) { opponentCount++; } else if (currentRole === role) { selfCount++; } else if (currentRole === 0) { emptyCount++; } } if (selfCount === 2) { if (!opponentCount) { return [shapes.TWO, selfCount, opponentCount, emptyCount]; } else { return [shapes.NONE, selfCount, opponentCount, emptyCount]; } } // two 类型优化结束,不需要的话可以在直接删除这一段代码不影响功能 // three类型大约占比有20%,也优化一下 emptyCount = 0; selfCount = 1; opponentCount = 0; let resultString = '1'; for (let i = 1; i <= 5; i++) { const [nx, ny] = [x + i * offsetX + 1, y + i * offsetY + 1]; const currentRole = board[nx][ny]; if (currentRole === 2) resultString += '2'; else if (currentRole === 0) resultString += '0'; else resultString += currentRole === role ? '1' : '2'; if (currentRole === 2 || currentRole === opponent) { opponentCount++; break; } if (currentRole === 0) { emptyCount++; } if (currentRole === role) { selfCount++; } } for (let i = 1; i <= 5; i++) { const [nx, ny] = [x - i * offsetX + 1, y - i * offsetY + 1]; const currentRole = board[nx][ny]; if (currentRole === 2) resultString = '2' + resultString; else if (currentRole === 0) resultString = '0' + resultString; else resultString = (currentRole === role ? '1' : '2') + resultString; if (currentRole === 2 || currentRole === opponent) { opponentCount++; break; } if (currentRole === 0) { emptyCount++; } if (currentRole === role) { selfCount++; } } if (patterns.five.test(resultString)) { shape = shapes.FIVE; performance.five++; performance.total++; } else if (patterns.four.test(resultString)) { shape = shapes.FOUR; performance.four++; performance.total++; } else if (patterns.blockFour.test(resultString)) { shape = shapes.BLOCK_FOUR; performance.blockFour++; performance.total++; } else if (patterns.three.test(resultString)) { shape = shapes.THREE; performance.three++; performance.total++; } else if (patterns.blockThree.test(resultString)) { shape = shapes.BLOCK_THREE; performance.blockThree++; performance.total++; } else if (patterns.two.test(resultString)) { shape = shapes.TWO; performance.two++; performance.total++; } // 尽量减少多余字符串生成 if (selfCount <= 1 || resultString.length < 5) return [shape, selfCount, opponentCount, emptyCount]; return [shape, selfCount, opponentCount, emptyCount]; } const countShape = (board, x, y, offsetX, offsetY, role) => { const opponent = -role; let innerEmptyCount = 0; // 棋子中间的内部空位 let tempEmptyCount = 0; let selfCount = 0; let totalLength = 0; let sideEmptyCount = 0; // 边上的空位 let noEmptySelfCount = 0, OneEmptySelfCount = 0; // right for (let i = 1; i <= 5; i++) { const [nx, ny] = [x + i * offsetX + 1, y + i * offsetY + 1]; const currentRole = board[nx][ny]; if (currentRole === 2 || currentRole === opponent) { break; } if (currentRole === role) { selfCount++; sideEmptyCount = 0; if (tempEmptyCount) { innerEmptyCount += tempEmptyCount; tempEmptyCount = 0; } if (innerEmptyCount === 0) { noEmptySelfCount++; OneEmptySelfCount++; } else if (innerEmptyCount === 1) { OneEmptySelfCount++; } } totalLength++; if (currentRole === 0) { tempEmptyCount++; sideEmptyCount++; } if (sideEmptyCount >= 2) { break; } } if (!innerEmptyCount) OneEmptySelfCount = 0; return { selfCount, totalLength, noEmptySelfCount, OneEmptySelfCount, innerEmptyCount, sideEmptyCount }; } // 使用遍历位置的方式实现的形状检测,速度较快,大约是字符串速度的2倍 但理解起来会稍微复杂一些 export const getShapeFast = (board, x, y, offsetX, offsetY, role) => { // 有一点点优化效果:跳过为空的节点 if (board[x + offsetX + 1][y + offsetY + 1] === 0 && board[x - offsetX + 1][y - offsetY + 1] === 0 && board[x + 2 * offsetX + 1][y + 2 * offsetY + 1] === 0 && board[x - 2 * offsetX + 1][y - 2 * offsetY + 1] === 0 ) { return [shapes.NONE, 1]; } let selfCount = 1; let totalLength = 1; let shape = shapes.NONE; let leftEmpty = 0, rightEmpty = 0; // 左右边上的空位 let noEmptySelfCount = 1, OneEmptySelfCount = 1; const left = countShape(board, x, y, -offsetX, -offsetY, role); const right = countShape(board, x, y, offsetX, offsetY, role); selfCount = left.selfCount + right.selfCount + 1; totalLength = left.totalLength + right.totalLength + 1; noEmptySelfCount = left.noEmptySelfCount + right.noEmptySelfCount + 1; OneEmptySelfCount = Math.max(left.OneEmptySelfCount + right.noEmptySelfCount, left.noEmptySelfCount + right.OneEmptySelfCount) + 1; rightEmpty = right.sideEmptyCount; leftEmpty = left.sideEmptyCount; if (totalLength < 5) return [shape, selfCount]; // five if (noEmptySelfCount >= 5) { if (rightEmpty > 0 && leftEmpty > 0) { return [shapes.FIVE, selfCount]; } else { return [shapes.BLOCK_FIVE, selfCount]; } } if (noEmptySelfCount === 4) { // 注意这里的空位判断条件, 右边有有两种,分别是 XX空 和 XX空X,第二种情况下,虽然 rightEmpty 可能不是true,也是符合的,通过 OneEmptySelfCount > noEmptySelfCount 来判断 if ((rightEmpty >= 1 || right.OneEmptySelfCount > right.noEmptySelfCount) && (leftEmpty >= 1 || left.OneEmptySelfCount > left.noEmptySelfCount)) { // four return [shapes.FOUR, selfCount]; } else if (!(rightEmpty === 0 && leftEmpty === 0)) { // block four return [shapes.BLOCK_FOUR, selfCount]; } } if (OneEmptySelfCount === 4) { return [shapes.BLOCK_FOUR, selfCount]; } // three if (noEmptySelfCount === 3) { if ((rightEmpty >= 2 && leftEmpty >= 1) || (rightEmpty >= 1 && leftEmpty >= 2)) { return [shapes.THREE, selfCount]; } else { return [shapes.BLOCK_THREE, selfCount]; } } if (OneEmptySelfCount === 3) { if ((rightEmpty >= 1 && leftEmpty >= 1)) { return [shapes.THREE, selfCount]; } else { return [shapes.BLOCK_THREE, selfCount]; } } if ((noEmptySelfCount === 2 || OneEmptySelfCount === 2) && totalLength > 5) { // two shape = shapes.TWO; } return [shape, selfCount]; } export const isFive = (shape) => { return shape === shapes.FIVE || shape === shapes.BLOCK_FIVE; }; export const isFour = (shape) => { return shape === shapes.FOUR || shape === shapes.BLOCK_FOUR; }; export const getAllShapesOfPoint = (shapeCache, x, y, role) => { const roles = role ? [role] : [1, -1]; const result = []; for (const r of roles) { for (const d of [0, 1, 2, 3]) { const shape = shapeCache[r][d][x][y]; if (shape > 0) { result.push(shape); } } } return result; }