封神冰川的最深处,冰封的宝箱被六道同心的火焰陷阱圆环重重守护。每一圈陷阱看似密不透风,却都藏着一道缺口(opening)——只要你能沿着圆周找到它,就能一层层向内推进,直抵中心的宝藏。可这里的空中守卫盯得极紧:不许跳跃、不许召唤、不许放置标记、不许使用魔法,任何违规都会招致惩罚。你能做的,只有用最纯粹的移动与几何计算,在火焰的缝隙中舞出一条生路。本单元只有一关,却把陷阱规避与几何路径规划这两项 AI 导航的硬核技能演绎到了极致。
findHazards() 获取全部陷阱数组;用 distanceTo 计算与陷阱的距离并找出最近的那个;理解陷阱的攻击范围(trapAttackRange)并始终保持在其之外;用极坐标与三角函数沿圆周规划路线;通过沿圆周扫描 + 安全阈值判断找到每一圈的缺口,层层向内安全抵达宝箱。这些正是 AI 在带危险源的环境中做路径规划的核心方法。
与上一单元的方格迷宫不同,这一关的迷宫是圆形的:陷阱以中心宝箱 center 为圆心,排成六个同心圆(concentric circles),每个圆的半径依次递减。这种结构决定了我们不能再用"上下左右走格子"的思路,而要改用圆周运动——沿着某一圈的半径转圈,去寻找那圈上唯一的缺口。
每一圈陷阱都有一个缺口。缺口的位置就是那圈上"离陷阱最远"的地方——当你站在缺口正对面时,最近的火焰会明显远离你。抓住这个特征,策略就清晰了:沿着当前半径的圆周一点点转动,持续测量到最近陷阱的距离,一旦距离超过某个安全阈值,就说明缺口到了,立刻沿半径向内跨进下一圈。
hero.findHazards() 是本关的核心感知工具——它返回一个数组,包含场上所有的火焰陷阱("fire-traps")。数组里的每个元素都是一个陷阱对象,带有 pos(位置,含 x / y)等属性。有了这份"陷阱清单",你就能计算它们的位置、距离,进而规划安全路线。
# 获取所有火焰陷阱组成的数组 hazards = hero.findHazards() # 遍历每个陷阱,读取它的位置 for h in hazards: hx = h.pos.x # 陷阱的 x 坐标 hy = h.pos.y # 陷阱的 y 坐标
findHazards 专门返回环境陷阱(如火焰陷阱),而不是会走动的敌人。陷阱通常固定不动,但有一个"攻击范围"——一旦你踏入,它就会被触发。把陷阱当作"静态危险源"来处理,是路径规划里非常常见的建模方式。
每个火焰陷阱都有一个攻击范围 trapAttackRange(本关约为 3 米)。只要你移动到距陷阱小于这个范围的地方,陷阱就会被脚步声激活并喷出火焰。因此规避的本质就是:始终让自己与任何陷阱的距离大于攻击范围。关卡还给了 radiusStep(圆环半径间隔)、center(圆心)等常量,帮你推算每一圈的几何位置。
# 关卡预置的常用常量 trapAttackRange = 3 # 陷阱攻击范围:靠近 3 米内会触发 radiusStep = 10 # 相邻两圈之间的半径间隔 center = {"x": 68, "y": 68} # 圆心(宝箱位置)
hero.distanceTo(target) 返回英雄当前位置到目标之间的直线距离。它背后其实就是欧几里得距离公式——两点坐标差的平方和再开方。有了它,我们就能量化"我离危险有多近",从而做出"走还是不走"的判断。这是所有基于距离的规避决策的基础。
# 计算到某个陷阱的距离 for h in hazards: d = hero.distanceTo(h) if d < trapAttackRange: # 太危险!这个位置会触发陷阱 pass
要判断当前位置是否安全,并不需要逐一比较所有陷阱——只要盯住离你最近的那个即可。因为如果连最近的陷阱都在安全距离之外,其余更远的自然更安全。hero.findNearest(hero.findHazards()) 就能从陷阱数组里挑出最近的一个,再用 distanceTo 测它的距离,就得到了"当前最危险的距离"。
# 关键片段:找到最近的陷阱并测距 nearest = hero.findNearest(hero.findHazards()) if hero.distanceTo(nearest) >= threshold: # 最近的陷阱都够远 → 此处正对缺口,可以向内走 pass
要沿圆周移动,笛卡尔坐标(x, y)不好使,极坐标(角度 + 半径)才是天生的语言。给定圆心 (cx, cy)、半径 R 和角度 angle,圆上任意一点都能算出来。让角度从 0 逐渐增大到 360,点就会沿圆周转一整圈——这正是我们"绕圈找缺口"所需要的运动。
# 圆心与半径设定 cx = 68 cy = 68 rad = Math.PI * 2 / 360 # 角度转弧度的系数(1度对应的弧度)
Math.cos / Math.sin 接受的是弧度而非角度。一整圈 360 度等于 2π 弧度,所以 1 度 = 2π / 360 弧度。代码里先算出这个系数 rad,用角度乘以它就得到弧度。忘了这步转换是最常见的坑。
把极坐标转回英雄能用的 (x, y),靠的就是三角函数:x = cx + cos(角) × 半径,y = cy + sin(角) × 半径。本关的巧妙之处在于同时算两个半径的点:当前圈的外半径 R 上的点 (X, y) 用来绕圈行走;比它小一圈的内半径 iR 上的点 (ix, iy) 则是"缺口对齐后向内跨进"的落脚点。
# 关键片段:同时算当前圈与内圈的点坐标 angle = (angle + 2) % 360 # 角度每次+2度,绕圈扫描 X = cx + Math.cos(angle * rad) * R # 当前圈上点的 x y = cy + Math.sin(angle * rad) * R # 当前圈上点的 y ix = cx + Math.cos(angle * rad) * iR # 内圈同角度点的 x iy = cy + Math.sin(angle * rad) * iR # 内圈同角度点的 y
+2 度。步长越小,绕圈越平滑、越不容易错过狭窄的缺口,但移动次数更多、更慢;步长越大越快,却可能"跳过"缺口。这是精度与效率的权衡——几乎所有采样式算法都要面对它。
规避的核心动作是"沿当前圈扫描":让角度每次增加一点,英雄就沿圆周挪一小步,每挪一步都用 findNearest + distanceTo 测一下离最近陷阱的距离。绝大多数位置都紧贴火环(距离小),只有走到缺口正对面时,最近陷阱才会陡然变远——这就是"该向内走"的信号。
判断"是否到了缺口",靠的是一个安全阈值 threshold(本关约 5.8,略大于攻击范围)。当最近陷阱的距离 >= threshold 时,说明当前角度正对缺口且向内走也不会踩到火——于是移动到内圈的点 (ix, iy),圈数减一,进入下一层继续扫描。如此循环六次,当所有圈都穿过后,直接走向圆心的宝箱。
# 关键片段:阈值判断 + 向内推进(结构示意) hero.moveXY(X, y) # 沿当前圈走到扫描点 nearest = hero.findNearest(hero.findHazards()) if hero.distanceTo(nearest) >= threshold: hero.moveXY(ix, iy) # 缺口!向内跨进下一圈 circle -= 1 # 剩余圈数减一 break # 跳出本圈扫描,换到内圈重新绕
threshold 要设得比 trapAttackRange 稍大,留出安全余量。如果阈值太小、几乎等于攻击范围,英雄可能在"擦边"处向内走时正好蹭到火焰而触发陷阱;太大又可能永远等不到满足条件的位置、卡在原地绕不停。调对这个阈值,是通关的关键。
这一节聚焦本关的策略、思路与算法设计,只展示用于说明的关键片段,完整通关程序请你自己动手完成。
本关的整体策略可以概括为一句话:从最外圈开始,沿圆周绕行扫描,一旦探到缺口就向内跨一圈,重复直到抵达圆心。由于禁止一切"取巧"手段(跳跃、召唤、标记、魔法),唯一可行的就是老老实实用 moveXY 沿几何路线走,并用 findHazards + distanceTo 时刻感知火焰、绕开攻击范围。关卡把陷阱布成同心圆,恰好把问题化归为"在每一圈上找那个距离陷阱最远的角度"。
先把迷宫的几何参数固化成常量:圆心 (cx, cy) = (68, 68)、单圈半径基数 r、总圈数 circle = 5(从外向内递减)、微调量 diff 与安全阈值 threshold。角度转弧度系数 rad = Math.PI * 2 / 360。这些常量把"迷宫长什么样"和"怎么走"清晰地分开,便于调参。
# 关键片段:几何常量 r = 18 # 半径步长基数 diff = 0.5 # 落点微调,避免贴边触发 threshold = 5.8 # 安全阈值(略大于攻击范围) circle = 5 # 待穿越的圈数(由外向内) cx = 68 cy = 68 rad = Math.PI * 2 / 360 # 1度对应的弧度
最外层用 while True 循环,每次处理"当前这一圈"。开头先判断:如果 circle 已经为 0(所有圈都穿完),就直接 moveXY 到圆心宝箱并 break 结束。否则,根据当前 circle 算出这一圈的外半径 R 和向内一圈的内半径 iR = R - r,准备开始沿这圈扫描。
# 关键片段:外层循环——逐圈处理 while True: if not circle: # 圈数为0 → 全部穿过 hero.moveXY(68, 67) # 直取中心宝箱 break R = r * circle + r / 2 - diff # 当前圈的外半径 iR = R - r # 向内一圈的半径 # ……进入内层:沿这圈扫描找缺口……
内层同样是循环,让 angle 每次 +2 度绕圈。每个角度都算出当前圈点 (X, y) 与内圈点 (ix, iy),先 moveXY(X, y) 走到扫描位置,再用 findNearest 找最近陷阱、distanceTo 测距。若距离 ≥ 阈值,说明正对缺口:立刻 moveXY(ix, iy) 向内跨进,circle -= 1,break 回到外层去处理内一圈;否则继续增大角度扫描下一个点。
# 关键片段:内层扫描——找到缺口就向内 while True: angle = (angle + 2) % 360 X = cx + Math.cos(angle * rad) * R y = cy + Math.sin(angle * rad) * R ix = cx + Math.cos(angle * rad) * iR iy = cy + Math.sin(angle * rad) * iR hero.moveXY(X, y) nearest = hero.findNearest(hero.findHazards()) if hero.distanceTo(nearest) >= threshold: hero.moveXY(ix, iy) # 缺口对齐,向内一层 circle -= 1 break
moveXY 到最外圈的一个安全起点(如 128 - r/2 - diff 处),再开始绕圈。先站到正确的半径上,再沿圆周扫描——这个"先就位、再扫描"的两步式起手,能让后续循环逻辑保持干净统一。
diff(0.5)。它的作用是让落点稍微"内缩"一点,避免因浮点误差恰好停在陷阱攻击范围的边界上而意外触发。处理连续几何时,预留一点安全余量是非常实用的工程技巧。
Math.cos/sin 要弧度,直接传角度会算出完全错误的坐标,英雄乱跑撞火。threshold 太小会贴边触发陷阱,太大会永远找不到缺口卡死在原地。circle -= 1,会在同一圈无限绕圈出不去。findNearest 盯最近的那个,而不是随便挑一个测距。trapAttackRange,擦边即触发。diff 微调,浮点误差可能让你恰好停在危险边界上。恭喜你穿越了封神冰川最凶险的火焰圆环,取得了中心的宝藏!这一关虽只有一个,却把 AI 导航中最实用的两大技能锤炼得淋漓尽致。你学会了用 findHazards() 一次性获取全部陷阱,用 distanceTo 和 findNearest 计算并盯住最近的危险,理解了陷阱的攻击范围并始终保持在安全距离之外;更掌握了用极坐标 + 三角函数沿圆周做几何路线规划,通过沿圆周扫描 + 安全阈值判断精准定位每一圈的缺口、逐层向内推进。从"感知危险"到"量化距离",再到"几何规划"与"阈值决策",你已经亲手实现了一套完整的避障导航逻辑——这正是机器人与自动驾驶背后的核心思想。来回顾一下核心概念:
| 核心概念 | 关键点 | 对应写法 |
|---|---|---|
| 获取陷阱 | 返回陷阱数组 | hero.findHazards() |
| 攻击范围 | 靠近即触发 | trapAttackRange |
| 计算距离 | 欧几里得直线距离 | hero.distanceTo(h) |
| 找最近陷阱 | 只盯最迫近威胁 | findNearest(findHazards()) |
| 极坐标 | 角度+半径描述圆 | angle, R |
| 三角函数算点 | cos管x,sin管y | cx+cos(a)*R, cy+sin(a)*R |
| 沿圆周扫描 | 角度递增找缺口 | angle = (angle+2)%360 |
| 阈值判断 | 够远才向内跨 | distanceTo >= threshold |
懂得"感知危险、量化距离、几何规划、留足余量"——在火焰缝隙中舞出安全路径的智慧,正是智能体驾驭危险世界的通行证。
封神冰川的圆环试炼圆满完成,恭喜你走完这段冰与火的算法之旅!🎯🔥❄️
扣哒世界 编程知识 · 计算机科学 6(封神冰川):第十七单元
扣哒世界教研团队