Skip to content

fix: 修复小游戏状态、推箱子生成与围棋搜索和 GPU 降级 - #5

Merged
Barbatosishere merged 2 commits into
mainfrom
fix/global-gameplay-regressions
Oct 9, 2026
Merged

Barbatosishere merged 2 commits into
mainfrom
fix/global-gameplay-regressions

Conversation

@Barbatosishere

@Barbatosishere Barbatosishere commented Oct 8, 2026 •

Copy link
Copy Markdown
Owner

小游戏在快速转向、重开、退出确认后切换窗口以及迷宫追逐时可能出现错误状态;推箱子随机生成也可能产生无解关卡。围棋单次搜索会优先展开 PASS,缺少可选 JNA 依赖时 OpenCL 清理异常会阻止失败冷却生效。本 PR 修复这些可复现问题,并补充算法回归测试。

修复内容

  • 贪吃蛇按上一次实际移动方向拒绝反向输入,修复左右转向判断;贪吃蛇和俄罗斯方块重开时清零 tick 计数,使新局分别等待完整的 4/9 tick 后首次移动或下落。
  • 迷宫实际走到缓存下一格后推进路径,包含追击及沿路径的随机游走;偏离缓存起点时重新寻路,修复停步、跨格跳跃和沿路径游走后多余的寻路。
  • 记忆反应和记忆翻牌的退出确认在窗口失焦后继续保持暂停,等待用户显式恢复。
  • 推箱子从已完成盘面反向拉箱打乱,保留可逆解法、箱子与目标数量;障碍从剩余空位中不重复抽取,避免随机重试耗尽后少放。生成重试有上限,失败时使用可解的一箱关卡。
  • 围棋在开局低预算搜索中先展开棋盘落子,同时保留 PASS 候选及已有终局停手行为。
  • 缺少 JNA 时跳过不存在的原生资源清理,使 OpenCL 失败记录与冷却正常生效并降级 CPU。

最新验证

  • Java 21 / Minecraft 1.21.1 / NeoForge 21.1.241:gradlew test --rerun build --no-build-cache --no-daemon --offline --max-workers=1 --console=plain 通过,34 套件、240 项回归,失败/错误/跳过均为 0。
  • 障碍数量缺陷:受控随机数连续选择同一坐标,旧代码请求两个障碍但只放一个;修复后用例通过。最新 JAR 执行 100,000 次生成盘面、障碍数量及边界检查和 2,000 张前四关地图独立 BFS 求解,全部通过,兜底 0 次。
  • 真实客户端:五项针对性检查通过,包括随机沿缓存下一格移动后的复用、偏离路径后的重算、人工断开地图观察、300 张迷宫共 300,000 步移动,以及推箱子界面 48 张地图独立求解。沿路径游走用例在修复前失败、修复后通过。
  • 新增回归证明 PASS 放在候选列表首/中/尾时原实现均可优先落子;使用真实 OpenCL 句柄模拟仅库/上下文/队列/程序四个部分初始化阶段,清理检查通过;缺失 JNA 的隔离类加载器用例通过。
  • 成品内容审计和 git diff --check 通过;本地针对性测试、报告和原始日志在忽略的 .bench/pr5-review-20261009/ 保存。

最新成品 SHA256:31356784a00da537ae4ee3f8ad99fd8e074ce50a15e4ca22bb5cd49bdc2d05aa。

审查建议评估

建议 处理依据
障碍放置重试耗尽会少放 已受控复现并修复,改为不重复抽取空位
随机游走也应推进缓存 已复现缓存浪费并修复;仅实际到达下一节点时推进,偏离路径时重算
无路径时应遵守冷却 人工断开图可复现即时重算,但机器人给出的替代表达式仍在 null 时无条件重算,无法达到其目标。正常生成图保证连通;本次保留现有失败重试语义,未来动态墙玩法需要按目标/地图变化设计失败缓存
生成时运行独立求解器 保留反向合法操作保证可解及测试中的 BFS。抽样第 10/25 关各耗时约 1.7/1.9 秒即达到 20 万状态预算,逐次放入主线程会增加卡顿;预算耗尽不代表无解
拉箱增加显式坐标检查 外围墙与玩家仅能处于内部空位保证身后坐标最多到边界格;十万次生成及极端关卡测试通过,保留现有不变量
PASS 固定在列表末尾 三个列表位置的新用例全部通过,逻辑检查的是当前待取动作是否 PASS,无需修改
cl 为空提前返回可能漏清理 cl 在原生句柄创建前赋值;后续部分失败时 cl 非空,已有代码会清理。四阶段真实句柄测试及缺库用例通过,无需修改

此前全局压力基线

初始修复提交 7323a16 的成品此前通过 20,760 次重复测试方法检查、16 workers 下 640 万次联机会话调用逐项断言、2,000 局斗地主、128 局象棋及 120 局五子棋。RTX 2060 的 10 轮压力通过:30,720 次并发推理、60 次原生上下文生命周期、360 个变长批次和 120 次训练更新与 CPU 比较一致。32 款游戏共 640 次创建/关闭的界面弱引用全部回收;9 款游戏正常节奏运行 1,800 tick、89.957 秒,无声道耗尽。

以上全局压力属于初始修复提交的验证;最新追加改进执行了本节上方的完整回归、生成压力和迷宫客户端检查,没有重复全部旧全局负载。测试未覆盖数小时耐久运行、真实双客户端网络对局或 KataGo/Pikafish 外部引擎。

long now = System.currentTimeMillis();
boolean shouldRecalculate = ghostPath.isEmpty() || (now - lastPathFindTime > PATH_FIND_INTERVAL);
// 游走后旧路径的起点不再是当前位置,必须重新寻路,避免跳回旧路径。
boolean shouldRecalculate = ghostPath == null || ghostPath.isEmpty()

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SUGGESTION: ghostPath == null check bypasses PATH_FIND_INTERVAL cooldown when no path exists

If findPath() returns null (no path found), ghostPath becomes null. The ghostPath == null condition makes shouldRecalculate true every ghost move tick, ignoring the 1-second PATH_FIND_INTERVAL cooldown. This could cause excessive pathfinding attempts in disconnected areas (though the maze generator ensures connectivity, so this is unlikely in practice).

Consider: boolean shouldRecalculate = ghostPath == null || ghostPath.isEmpty() || ghostPath.get(0)[0] != ghostX || ghostPath.get(0)[1] != ghostY || (ghostPath != null && now - lastPathFindTime > PATH_FIND_INTERVAL);


Reply with @kilocode-bot fix it to have Kilo Code address this issue.

int[] nextStep = ghostPath.get(1);
ghostX = nextStep[0];
ghostY = nextStep[1];
ghostPath.remove(0); // 消耗已离开的节点,让缓存起点跟随鬼魂。

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SUGGESTION: ghostPath.remove(0) only called in chase branch, not random walk branch

When the ghost takes a random walk (50% chance), the cached path is not consumed. The path's first element becomes stale (no longer matches ghost position), forcing a recalculation on the next chase attempt. This is functionally correct but wastes the cached path. Consider consuming the path step in both branches or clearing the path on random walk.


Reply with @kilocode-bot fix it to have Kilo Code address this issue.

private static char[][] generateOnce(int size, int boxCount, int obstacleCount,
int levelNum, Random random) {
char[][] grid = emptyGrid(size);
for (int i = 0; i < obstacleCount; i++) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WARNING: Obstacle placement silently fails after 30 retries per obstacle

The inner loop attempts up to 30 times to find an empty cell for each obstacle. If the grid becomes crowded (many obstacles + boxes), it may fail to place all requested obstacles and continues silently. This results in fewer obstacles than obstacleCount specifies, making levels easier than intended, especially at higher levels.

Consider logging a warning or reducing obstacleCount dynamically if placement fails.


Reply with @kilocode-bot fix it to have Kilo Code address this issue.

playerY = nextY;
}

// 保留全部目标和箱子,要求开局至少有一个箱子未归位。

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SUGGESTION: Validation only checks for off-target box ($), not actual solvability

The generator returns the grid if any $ exists, assuming the reverse-pull algorithm guarantees solvability. While the reverse-pull approach mathematically produces solvable levels, the validation doesn't independently verify this (the test hasPushSolution does). Adding a solver check here would catch any algorithmic bugs, though it would increase generation time.


Reply with @kilocode-bot fix it to have Kilo Code address this issue.

char floor = grid[playerY][playerX] == '*' ? '.' : ' ';
int boxX = playerX - DX[direction];
int boxY = playerY - DY[direction];
char behind = grid[boxY][boxX];

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SUGGESTION: Array access grid[boxY][boxX] without explicit bounds check

boxX/boxY are calculated as playerX - DX[direction] / playerY - DY[direction]. Since player only moves to ' ' or '.' (never borders), and borders are walls ('#'), boxX/boxY can reach border indices (0 or size-1) but not exceed them. The access is safe but relies on maze structure invariants. An explicit bounds check or assertion would improve robustness.


Reply with @kilocode-bot fix it to have Kilo Code address this issue.

// a cold or one-iteration search stop immediately, regardless of
// the policy. Give a board move the first visit before endgame;
// subsequent visits can still evaluate and select PASS normally.
if ((node.children == null || node.children.isEmpty())

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SUGGESTION: Assumes PASS is the last element in untriedMoves

The logic checks isPass(node.untriedMoves.get(index)) where index = size - 1, assuming PASS was appended last. If future changes reorder untriedMoves (e.g., policy-based sorting moves PASS elsewhere), this could expand PASS prematurely or skip the board-move preference logic. Consider finding PASS by predicate instead of positional assumption.


Reply with @kilocode-bot fix it to have Kilo Code address this issue.

if (closed) return;
closed = true;
available = false;
// No native handles exist when loading JNA itself failed. Avoid

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SUGGESTION: Early return if cl == null may leak partially initialized resources

If cl was successfully loaded but a later init() step failed (context, queue, program creation), cl is non-null but other native handles may be null. The early return skips freeActs(), freeWeights(), and kernel/program/queue/context cleanup. Since cl is set first in init(), this scenario is unlikely, but a try/finally in init() or tracking initialization state would be more robust.


Reply with @kilocode-bot fix it to have Kilo Code address this issue.

@kilo-code-bot

kilo-code-bot Bot commented Oct 8, 2026 •

Copy link
Copy Markdown

Code Review Summary

Status: No Issues Found | Recommendation: Merge

Files Reviewed (5 files)
  • src/main/java/com/wzz/game_console/client/screens/games/MazeGameScreen.java - 0 issues
  • src/main/java/com/wzz/game_console/client/screens/games/SokobanLevelGenerator.java - 0 issues
  • src/test/java/com/wzz/game_console/client/screens/games/SokobanLevelGeneratorTest.java - 0 issues
  • src/test/java/com/wzz/game_console/client/screens/games/gogame/MCTSGoAIRegressionTest.java - 0 issues
  • src/test/java/com/wzz/game_console/client/screens/games/gogame/OpenCLLifecycleRegressionTest.java - 0 issues
Previous Review Summary (commit 7323a16)

Current summary above is authoritative. Previous snapshots are kept for context only.

Previous review (commit 7323a16)

Status: 8 Issues Found | Recommendation: Address before merge

Overview

Severity Count
CRITICAL 0
WARNING 1
SUGGESTION 7
Issue Details (click to expand)

WARNING

File Line Issue
src/main/java/com/wzz/game_console/client/screens/games/SokobanLevelGenerator.java 34 Obstacle placement silently fails after 30 retries per obstacle

SUGGESTION

File Line Issue
src/main/java/com/wzz/game_console/client/screens/games/MazeGameScreen.java 346 ghostPath == null check bypasses PATH_FIND_INTERVAL cooldown when no path exists
src/main/java/com/wzz/game_console/client/screens/games/MazeGameScreen.java 366 ghostPath.remove(0) only called in chase branch, not random walk branch
src/main/java/com/wzz/game_console/client/screens/games/SokobanLevelGenerator.java 87 Validation only checks for off-target box ($), not actual solvability
src/main/java/com/wzz/game_console/client/screens/games/SokobanLevelGenerator.java 73 Array access grid[boxY][boxX] without explicit bounds check
src/main/java/com/wzz/game_console/client/screens/games/gogame/MCTSGoAI.java 805 Assumes PASS is the last element in untriedMoves
src/main/java/com/wzz/game_console/client/screens/games/gogame/OpenCLBackend.java 634 Early return if cl == null may leak partially initialized resources
Files Reviewed (14 files)
  • src/main/java/com/wzz/game_console/client/screens/games/MazeGameScreen.java - 2 issues
  • src/main/java/com/wzz/game_console/client/screens/games/MemoryCardScreen.java - 0 issues
  • src/main/java/com/wzz/game_console/client/screens/games/MemoryGameScreen.java - 0 issues
  • src/main/java/com/wzz/game_console/client/screens/games/SnakeDirection.java - 0 issues
  • src/main/java/com/wzz/game_console/client/screens/games/SnakeGameScreen.java - 0 issues
  • src/main/java/com/wzz/game_console/client/screens/games/SokobanLevelGenerator.java - 3 issues
  • src/main/java/com/wzz/game_console/client/screens/games/SokobanScreen.java - 0 issues
  • src/main/java/com/wzz/game_console/client/screens/games/TetrisGameScreen.java - 0 issues
  • src/main/java/com/wzz/game_console/client/screens/games/gogame/MCTSGoAI.java - 1 issue
  • src/main/java/com/wzz/game_console/client/screens/games/gogame/OpenCLBackend.java - 1 issue
  • src/test/java/com/wzz/game_console/client/screens/games/MiniGameRegressionTest.java - 0 issues
  • src/test/java/com/wzz/game_console/client/screens/games/SokobanLevelGeneratorTest.java - 0 issues
  • src/test/java/com/wzz/game_console/client/screens/games/gogame/MCTSGoAIRegressionTest.java - 0 issues
  • src/test/java/com/wzz/game_console/client/screens/games/gogame/OpenCLMissingJnaRegressionTest.java - 0 issues

Fix these issues in Kilo Cloud


Reviewed by free · Input: 150.5K · Output: 16.2K · Cached: 90.7K

@Barbatosishere
Barbatosishere merged commit 3ac38ec into main Oct 9, 2026
2 checks passed
@Barbatosishere
Barbatosishere deleted the fix/global-gameplay-regressions branch October 16, 2026 04:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant