前文,添加食物和个体的位置,并需要个体移动到食物附近进食。为简化起见,暂用一维位置即x在【0到1440】。

世界时间流逝未修改,其他部分如下:

(deftemplate 某种
  (slot 长辈)
  (slot 编号)
  (slot 能量)
  (slot 经历时间)
  (slot x))

(deftemplate 食物
  (slot 编号)
  (slot x))

(defrule 开始
  =>
  (println "开天辟地")
  ; 纪录编号,确保不重复;否则新个体会取代老个体
  (assert (世界 (数量 1) (时间 0)))
  (bind ?宽 1440)
  (loop-for-count (?序号 1 10)
    (assert (食物 (编号 ?序号) (x (random 0 ?宽)))))
  (assert (某种 (长辈 0) (编号 0) (能量 0) (经历时间 0) (x (random 0 ?宽)))))

(defrule 觅食
  (declare (salience 5))
  (世界 (时间 ?时间))
  ?个体 <- (某种 (编号 ?个体编号) (能量 ?能量) (x ?个体位置) (经历时间 ?经历时间&:(< ?经历时间 ?时间)))
  ?食物 <- (食物 (编号 ?食物编号) (x ?食物位置))
  =>
  (modify ?个体 (经历时间 (+ 1 ?经历时间)))

  (bind ?速度 1)
  (bind ?距离 (- ?个体位置 ?食物位置))
  (if (< (abs ?距离) 2)
      then
      (retract ?食物)
      (println ?个体编号 "在" ?食物位置 "吃了" ?食物编号)
      (modify ?个体 (能量 (+ ?能量 1)) )
      else
      ; 靠近食物
      (if (> ?距离 0)
        then
        (modify ?个体 (x (- ?个体位置 ?速度)))
        else
        (modify ?个体 (x (+ ?个体位置 ?速度)))
      )
  )
)

(defrule 繁衍
  (declare (salience 10))
  ?世界 <- (世界 (数量 ?个体量) (时间 ?时间))
  ?长辈 <- (某种 (编号 ?编号) (能量 ?能量&:(> ?能量 0)) (x ?长辈位置))
  =>
  (assert (某种 (长辈 ?编号) (编号 ?个体量) (能量 0) (经历时间 ?时间) (x (+ ?长辈位置 1))))
  (modify ?世界 (数量 (+ 1 ?个体量)))
  (modify ?长辈 (能量 0))
)

为追踪繁衍和觅食,加了些辅助属性如长辈。输出像这样:

开天辟地
0在1273吃了10
0在1025吃了9
1在279吃了8
2在572吃了7
4在887吃了6
1在362吃了5
5在826吃了4
7在1317吃了3
1在250吃了2
8在998吃了1

之前的fact消失的确是因为编号重复,但具体是因为改了老个体撞了新个体,还是创建新个体撞了老个体,未深究。

代码嵌套if-else后,仍会难读。感觉按规则组织代码可能减少了循环的使用,但业务逻辑部分稍冗长些,尤其还有lisp风格的括号语法,以及每次重复声明的属性(slot)名。

之后试试 deffunction 清晰化一下代码。