Kotlin扫雷项目setMines()方法疑似无限循环致运行超时
扫雷项目15秒超时无限循环问题修复
报错信息
项目运行时触发测试超时:
In this test, the program is running for a long time, more than 15 seconds. Most likely, the program has gone into an infinite loop.
核心故障点
你推测的setMines()确实是死循环主要触发点,同时代码中存在多处配套逻辑错误共同导致异常:
- 随机数范围错误:
Random.nextInt(0, SIZE - 1)的上界是排他值,SIZE=9时上界传8,实际生成的坐标范围是0~7,永远取不到最后一行/列的坐标8,可放雷位置从81个缩减到64个。 - 安全区判断逻辑错误:放雷时排除首次点击位置的条件写为
x != notMine["x"] && y != notMine["y"],会排除所有和首次点击同x或同y的整行整列格子,可放雷位置进一步缩减,当用户输入雷数大于剩余可放位置数时,循环永远找不到符合条件的空位,minesToSet无法递减到0,直接触发死循环。 - 集合引用错误:
markedCells、unExploredCells直接赋值为mines,三个变量指向同一个Set对象,后续标记、探索操作会直接篡改地雷集合,导致胜负判断、放雷逻辑完全混乱。 - 辅助逻辑错误:
toggleMarking中多余的y坐标偏移、notHasMinesAround()返回值写反、空白格探索仅展开一层不递归,都会导致游戏逻辑异常,间接触发循环无法退出。
无死循环的地雷放置实现
替换原有随机重试的放雷逻辑,采用「合法位置预生成+洗牌取位」方案,从根源上避免随机重复选点导致的无限循环,时间复杂度更稳定:
private fun setMines() { // 预先生成所有允许放雷的坐标,仅排除首次点击的单个格子 val availablePositions = mutableListOf<Pair<Int, Int>>() val safeX = notMine["x"] val safeY = notMine["y"] for (x in 0 until SIZE) { for (y in 0 until SIZE) { if (x != safeX || y != safeY) { availablePositions.add(x to y) } } } // 洗牌后直接取前N个位置放雷,不存在重复选点问题 availablePositions.shuffle() val minePositions = availablePositions.take(numberOfMines) // 批量设置地雷与周围数字 for ((x, y) in minePositions) { val cell = this[x][y] cell.makeMine() mines.add(cell) // 遍历3x3邻域更新数字 for (row in x - 1..x + 1) { for (col in y - 1..y + 1) { if (row in 0 until SIZE && col in 0 until SIZE) { this[row][col].add() } } } } }
必须同步修复的配套问题
- 修正集合初始化,三个集合独立创建,不要引用同一个对象:
private val mines = mutableSetOf<Cell>() private val markedCells = mutableSetOf<Cell>() private val unExploredCells = mutableSetOf<Cell>() - 删除
toggleMarking函数中多余的val y = y - 1行,传入坐标已提前做过偏移处理。 - 修正
Cell类中周围雷数判断逻辑:fun notHasMinesAround(): Boolean = number == 0 - 修正空白格探索逻辑,改为递归展开所有连通的无雷空白格:
fun explore(x: Int, y: Int) { val cell = this[x][y] if (cell.isExplored || cell.isMarked) return cell.explore() unExploredCells.remove(cell) // 仅当当前格周围无雷时,才递归展开邻域格子 if (cell.isNotMine() && cell.notHasMinesAround()) { for (row in x - 1..x + 1) { for (col in y - 1..y + 1) { if (row in 0 until SIZE && col in 0 until SIZE && (row != x || col != y)) { explore(row, col) } } } } } - 调整main函数流程,删除第一次输入前多余的
println(Board(SIZE)),避免打印未初始化的空棋盘。
内容的提问来源于stack exchange,提问作者halotukozak
相关产品推荐
相关产品推荐

