一百行Ruby写个A*

A* in Ruby

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的语法。

large test map

红色表示水塘,不能通过。

这是结果:

large test map solution

好丑,还不如ASCII码:

 

Ruby语言具有一些独特且有规律的格式样式: ### 代码注释 Ruby支持单注释和多注释。单注释以 `#` 开头,用于对代码某一或局部进解释。例如: ```ruby # 这是一个单注释 puts "Hello, World!" ``` 多注释使用 `=begin` 和 `=end` 包裹,适合对一段代码进整体说明: ```ruby =begin 这是一个多注释 可以很多内容 用于解释下面的代码 =end puts "This is a code with multi - line comment." ``` ### 变量命名 变量命名遵循一定的规则。局部变量以小字母或下划线开头,例如: ```ruby name = "John" _age = 30 ``` 全局变量以 `$` 开头,类变量以 `@@` 开头,实例变量以 `@` 开头。例如: ```ruby $global_variable = 100 class MyClass @@class_variable = 200 def initialize @instance_variable = 300 end end ``` ### 方法定义 方法定义使用 `def` 关键字开始,`end` 关键字结束。方法名通常使用小字母和下划线组合,参数在方法名后的括号中指定。例如: ```ruby def add_numbers(a, b) return a + b end ``` 当方法只有一个表达式时,可以使用单形式: ```ruby def square(x); x * x; end ``` ### 类定义 类名使用大字母开头的驼峰命名法,类定义使用 `class` 关键字开始,`end` 关键字结束。例如: ```ruby class Person def initialize(name, age) @name = name @age = age end def introduce puts "My name is #{@name} and I'm #{@age} years old." end end ``` ### 控制结构 条件语句 `if - elsif - else` 的格式如下: ```ruby if condition1 # 执代码 elsif condition2 # 执代码 else # 执代码 end ``` 循环语句 `while` 和 `for` 也有特定格式。`while` 循环示例: ```ruby i = 0 while i < 5 puts i i += 1 end ``` `for` 循环示例: ```ruby for num in 1..5 puts num end ``` ### 代码块 代码块可以使用 `do - end` 或 `{}` 来定义。`do - end` 通常用于多代码块,`{}` 用于单代码块。例如: ```ruby [1, 2, 3].each do |num| puts num * 2 end [1, 2, 3].each { |num| puts num * 2 } ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值