在线迷宫游戏,如何让 Claude 理解并优化现有库

周末花时间用 Cursor 和 Claude3.5 基于已有的一个迷宫生成库,写了一个完整的迷宫游戏。可以在 迷宫游戏,在线迷宫生成 体验,整体功能还是比较齐全的,支持生成各种类型的迷宫地图,支持滑动鼠标来在线玩迷宫游戏。

在线迷宫游戏,生成各种迷宫地图

之前让 Claude 写代码的时候,用到的都是一些比较有名的库,这些库本身也有详细的文档。AI 训练的时候已经知道这些库的用法,所以在写代码的时候,可以很顺利的写出来。但是这次写迷宫游戏的时候,用的是一个比较小众的迷宫生成库,这个库本身没有文档,AI 模型中也没有这个库的知识,所以怎么让 AI 用这个库来完成我的任务,是个挑战。

直接给 AI “看” 代码

既然是 AI 从来不知道的库,那么最简单的方法当然就是直接给 AI “看” 代码。把迷宫基础库一整个拷到项目中,然后先一个个文件给 AI ,让它总结下代码的基本作用。这样自己也可以快速的了解下这个库的实现原理。

其实从后面的使用过程来说,只有自己对整个库的实现有个大概了解,才能在关键时候给 AI 提供正确的提示,解决遇到的一些问题

把库简单喂给 AI 后,就可以开始写代码了。先把需求告诉 Claude,AI 很快就给了第一个版本。不得不说,整体效果还是可以的,比预期好一点。当然也有不少问题,下面分享 2 个比较典型的问题,相信每个使用 AI 写代码的人都遇见过。

Claude 幻觉:编造一个参数名

这里在生成迷宫的时候,是支持设置随机种子的。对于同一个随机种子和地图配置,每次生成的迷宫都是一样的。但是在 Claude 实现的版本中,设置了一个随机种子,但每次生成的迷宫总是不一样的。

这种问题最简单就是加日志来调试了,其实现在的 Claude3.5 智能很多,会主动提醒可以加日志排查问题。有时候也会直接帮你在相应位置加日志,然后你可以把日志输出给 AI,AI 会根据日志来调整代码。

这里在库代码中,在使用随机种子前加了日志,发现每次输出的种子都在变,并没有用到我们设置的种子。看起来我们设置迷宫参数部分有问题啊,相关代码如下:

  const config = {
    element: canvasRef.current,
    grid: {
      cellShape: settings.shape,
      width: parseInt(settings.width),
      height: parseInt(settings.height),
      layers: parseInt(settings.layers),
    },
    algorithm: settings.algorithm,
    exitConfig: settings.exitConfig || EXITS_HARDEST,
    random: numericSeed,
    lineWidth: 2,
  };

随机数的参数用的是 random,但看了下库中的代码,发现用的是 randomSeed。

这就是典型的 AI 幻觉问题了,虽然最近版本的 AI 比刚出来的时候好了很多,但幻觉问题依然存在。特别是如果训练的时候没有见过这个库,AI 编造参数名和函数名还是比较常见的问题。不过这种问题比较容易解决,稍微深入下代码,就能发现这类问题。

Claude 逻辑推理缺陷

第二个问题更常见,就是典型的代码逻辑问题,这类问题更难解决。就算是目前写代码体感最强的 Claude3.5,在代码比较长的时候,一些简单的逻辑实现起来也会有各种缺陷

在提供的迷宫库中,有方法可以从迷宫的起点开始,找到可以达到终点的路径。在本页面的实现中,我想让 AI 提供一个显示路径的按钮,点击后能显示完整的路径。

Claude3.5 给出了一个不错的实现,添加按钮,然后实现按钮相关的回调,看起来很完美。但运行后,发现并没有生成路径,看了下控制台,也没发现什么报错。

然后就又得调试了,在 AI 的辅助下,很容易就知道这里迷宫库生成路径相关的实现在哪里。接着在生成路径的代码中添加日志,发现生成的路径没问题,但迷宫中就是没有渲染出来

迷宫中没有显示路径

既然路径生成没问题,那问题就出在迷宫的渲染上了。这里看了下迷宫生成的时候,调用 maze.render() 来渲染。那生成好的路径,是不是也应该调用 maze.render() 来渲染呢?问了下 Claude,给出了肯定的回答,接着又让它解释了下这里整体的渲染实现逻辑。

日常使用中,时不时会遇见类似问题,AI 自己并不能找到解决方法。需要你去想方法调试,然后给出一些思路,之后 AI 就能事后诸葛亮,帮你完成收尾工作了。

Claude3.5 优化现有项目

上面的两个问题还比较好解决,毕竟都是用现有迷宫库的功能。如果我让 AI 来实现一些迷宫库不支持的功能,AI 能实现吗?

现有的迷宫库中,如果开始找路径的话,需要通过方向键来控制,或者通过鼠标点击来指定要移动的路径。对于复杂的迷宫,这样一直点鼠标有点累。能不能跟随我鼠标的移动,来控制在迷宫中探索的方向呢

鼠标悬浮移动简单版本

Claude3.5 给的第一个版本,还是利用了现有的迷宫库接口。把原来点击的移动逻辑,换成了现在鼠标悬浮移动的移动逻辑。每次鼠标移动的时候,计算鼠标位置,然后用 getClosestDirectionForClick 来计算要移动的方向。核心代码如下:

const x = event.clientX - rect.left;
const y = event.clientY - rect.top;

const clickEvent = {
  rawCoords: [x, y]
};

const direction = maze.getClosestDirectionForClick(playState.currentCell, clickEvent);

Claude 实现的时候,为了防止每次鼠标移动都触发移动,还加了一个时间间隔。每隔 200ms,才触发一次移动。

这种实现体验起来并不好,有时候在同一个格子,鼠标移动的时候可能不小心抖了下,就会让迷宫角色移动到相反方向的格子,控制起来并不流畅~

鼠标悬浮移动优化版本

最理想的实现方法,应该是每次鼠标位置变化的时候,判断鼠标在迷宫的哪个格子上。如果当鼠标移动到了当前格子旁边的相邻格子,并且路径可达,就移动到鼠标在的新格子上。但是整个迷宫库的实现中,并没有现成的方法,可以将鼠标位置转换为迷宫格子

其实仔细想了下,迷宫库中应该有这部分的转换逻辑在,毕竟每次点击的时候也会定位到格子上,只是这部分逻辑没有暴露出接口来。所以直接给 Claude 迷宫的部分代码,然后问 AI 这里是怎么将页面位置转换为迷宫的格子。

Claude 还是可以的,一下子就找到相关代码。在 maze.js 中,每种形状的网格都有自己的点击事件处理逻辑,用于将画布坐标转换为网格坐标。例如对于方形网格:

defaultDrawingSurface.on(EVENT_CLICK, event => {
    const coords = [Math.floor(event.x), Math.floor(event.y)];
    if (grid.getCellByCoordinates(coords)) {
        eventTarget.trigger(EVENT_CLICK, {
            coords,
            rawCoords: [event.rawX, event.rawY],
            shift: event.shift,
            alt: event.alt
        });
    }
});

其他几种形状的网格,也有类似的转换逻辑。当然因为网络形状的复杂性,这里的转换也相应的复杂很多。

既然找到了每个形状地图的转换逻辑,只需要将这部分抽离出来,暴露出一个对外接口。就可以在鼠标悬浮移动中用这个逻辑来判断鼠标在迷宫的哪个格子上。

鼠标悬浮移动优化版本

优化后,体验就好了很多,可以流畅的跟随鼠标移动来控制迷宫中的移动。

Claude3.5 迷宫游戏总结

作为一个不会写前端的开发,有了 AI 的帮助,很快就在一个小众开源迷宫库的基础上,写了一个完整的迷宫游戏。这中间少不了 AI 的辅助,不然自己连 JavaScript 语法都不会,写起来难免会慢很多。对于开源的maze 库,如果不是 AI 帮忙解读,自己可能也会被其中的部分实现细节卡住。

当然整个实现过程中,还是有不少需要自己思考以及调试的地方。这里的调试思路是编程的核心思想,也是通用的编程能力。虽然大部分代码是 Claude3.5 写的,但整个过程还是有锻炼到编程能力。

最后,欢迎来体验在线迷宫游戏,在线迷宫生成