Ruby quiz的第98题让写一个A*寻路程序。Daniel Martin提供了一个不到两百行的解答。如果简化一下,完全可以在一百行以内实现。
在Daniel的程序里,do_quiz_solution()是个外壳,它做三件事:找到起止点的坐标(@start和@goal),把puzzle解析成数值矩阵(存在@terrain中),把puzzle转换成没有空格的规范形式存进instr以便后面利用它把路径打印出来。
核心的部分是do_find_path()。这里需要用到优先队列PriorityQueue。优先队列的元素也是一个复合结构:[优先级, [当前考察的点, 到达该点的最佳路径, 目前花费的代价]]。
下面来看看如何实现优先队列:
优先队列可以看成一个半序的容器,它每次抛出优先级最低(或最高)的元素。优先队列一般是用堆(一种满足特定规则的完全二叉树)来实现的,但这里因为优先队列不是重点,Daniel利用Ruby内置的sort函数实现了一个简单,而且性能不咋地的优先队列。
@list是个数组。每次在add新元素时,会把优先级信息也一并记录下来。另外,为了区分相同优先级的元素,add()还会悄悄把该元素是第几个加入队列的元素也写进去,即
然后sort!,保证数组按优先级进行排列。
接着来看看估价函数,它用来评估当前所在的位置到目标点的尽可能大的下界(最好比实际代价小)。在这里,由于允许走斜线,只需考虑当前点与目标点横纵坐标差的最大值即可。
还有一个辅助函数spotfrom(),用来生成当前点周围没访问过的邻居。
最后,我们来看看它是怎么把杂乱的字符串规整成标准形式的,以及又是如何依据规范字符地图生成二位数值矩阵的。
逐行分析输入的字符串:
- 遇到由纯空白符构成的行(/^/s*$/)就跳过;
- 替换掉所有非.@~X*^符号; ;
- 逐字分析
-
- 根据题设在数值矩阵的相应位置写入相应的代价
题目里有个测试地图。我想把结果用图的形式显示出来,对Ruby 这方面的库又不熟,只好用Python了(需要用到matplotlib)。
P.S. 果然不习惯Python的语法。
红色表示水塘,不能通过。
这是结果:
好丑,还不如ASCII码: