用 clips 模拟生态0.4:由位置定速度
先重构,如前文打算提取了一些函数到功用.clp
:
(deffunction 加一 (?数)
(+ 1 ?数))
(deffunction 更新速度 (?距离 ?速度)
(bind ?速度上限 2)
(bind ?差值 (* 0.005 ?距离))
(bind ?新速度 (+ ?速度 ?差值))
(bind ?绝对值 (abs ?新速度))
(if (> ?绝对值 ?速度上限)
then (* ?速度上限 (/ ?绝对值 ?新速度))
else ?新速度))
模版部分提取到数据结构.clp
。某种
加了速度
属性;上个版本里世界
就加了个体总数量
属性。
然后发现加载的时候各种符号还有不同含义的样子,颇有上世纪特色:
CLIPS> (batch* 加载.bat)
!!
%%%
*****
TRUE
每行对应一个源码文件。!表示函数定义(function),%为模版(template),*为规则。
业务部分六十行左右。之前木兰实现的业务部分大约一百五十行,当然还有一部分未实现:
(defrule 开始
=>
【同前】
(assert (某种 (长辈 0) (编号 0) (能量 0) (经历时间 0) (x (random 0 ?宽)) (速度 0))))
; 当无优先级声明时,规则编写顺序决定了执行的优先级!
(defrule 时间流逝
; 无食则停
(食物)
?环境 <- (世界 (时间 ?时间))
=>
(modify ?环境 (时间 (加一 ?时间)))
)
(defrule 觅食
(declare (salience 5))
(世界 (时间 ?时间))
?个体 <- (某种 (编号 ?个体编号) (能量 ?能量) (x ?个体位置) (速度 ?当前速度) (经历时间 ?经历时间&:(< ?经历时间 ?时间)))
?食物 <- (食物 (编号 ?食物编号) (x ?食物位置))
=>
(modify ?个体 (经历时间 (加一 ?经历时间)))
(bind ?距离 (- ?食物位置 ?个体位置))
(if (< (abs ?距离) 2)
then
(retract ?食物)
(modify ?个体 (能量 (加一 ?能量)))
(println ?个体编号 "在" ?食物位置 "吃了" ?食物编号)
else
; 靠近食物
(bind ?新速度 (更新速度 ?距离 ?当前速度))
(modify ?个体 (速度 ?新速度) (x (+ ?个体位置 ?新速度)))
)
)
(defrule 繁衍
【同前】
=>
(assert (某种 (长辈 ?编号) (编号 ?个体量) (能量 0) (经历时间 ?时间) (x (加一 ?长辈位置)) (速度 0)))
(modify ?世界 (数量 (加一 ?个体量)))
(modify ?长辈 (能量 0))
)
; 待做:加开关
(defrule 显示详情
(declare (salience 100))
(某种 (编号 0) (速度 ?当前速度&:(neq ?当前速度 0)))
=>
(println ?当前速度)
)
在为了查看某个体的速度变化加最后一段时,感觉到和传统的C类代码开发的不同之处。之前这种情况,往往就找个地方塞这个print语句,比如在 觅食
的速度更新之后。CLIPS里虽然也可以这么干,不过也可以借助规则把这个「需求」另开一段代码组织,从阅读代码角度看,可以更少影响业务部分的顺畅可读性。
接下去先实现统计部分和狼、羊,再做二维地图。