CLIPS 规则系统行为

问题描述 投票:0回答:1

我正在学习 CLIPS 来评估视频游戏中的逻辑。我的 CLIPS 代码如下。在这个例子中,有物理伤害和物理抗性,这应该会减少物理伤害一个系数。当前代码适用于这个玩具示例,其中有一个损坏事实和一个阻力事实。然而,当存在多个损坏事实时,它会失败,因为抵抗事实被规则撤回。但是,如果我不撤回抵抗规则,那么它会重新触发创建无限循环。

我想知道以下情况的最佳实践是什么:可能有几个阻力事实和几个损坏事实。在适用的情况下,抵抗事实应应用于每个损坏事实一次且仅一次。换句话说,单个损坏事实可能有多个应用于其的阻力事实,或者单个阻力事实可能适用于多个损坏事实。

(deftemplate damage
  (slot source (type STRING))
  (slot target (type STRING))
  (slot amount (type FLOAT))
  (slot type (type STRING)))

(deftemplate resist
  (slot type (type STRING))
  (slot factor (type FLOAT))
  (slot player_id (type STRING)))

(defrule damage-resistance
   ?d <- (damage (source ?source) (target ?target) (amount ?amount) (type ?type))
   ?r <- (resist (type ?type) (factor ?factor) (player_id ?target))
   =>
   (bind ?reduced-amount (* ?amount ?factor))
   (retract ?r)
   (modify ?d (amount ?reduced-amount))
)

(assert (damage (source "1") (target "2") (amount 10.0) (type "physical")))
(assert (resist (type "physical") (factor 0.9) (player_id "2")))

这里的最佳实践是什么?似乎我可以向模板添加多槽来跟踪已应用的规则,或者我可以以某种方式修改规则......

我尝试修改模板,但不确定我有一个优雅的解决方案

编辑:我决定使用这样的解决方案,其中每个损坏事实都有一个 unique_id,每个阻力事实跟踪它已经应用到的损坏事实。不确定这是否是最优雅的,但它有效......至少满足我当前的需求。

(deftemplate damage
  (slot id (type INTEGER))
  (slot source (type STRING))
  (slot target (type STRING))
  (slot amount (type FLOAT))
  (slot type (type STRING)))

(deftemplate resist
  (slot type (type STRING))
  (slot factor (type FLOAT))
  (slot player_id (type STRING))
  (multislot applied_to)) ; Add a multislot to track to which damage facts this resist has been applied

(defrule damage-resistance
   ?d <- (damage (source ?source) (target ?target) (amount ?amount) (type ?type) (id ?id))
   ?r <- (resist (type ?type) (factor ?factor) (player_id ?target) (applied_to $?applied))
   (test (not (member$ ?id ?applied)))
   =>
   (bind ?reduced-amount (* ?amount ?factor))
   (modify ?r (applied_to $?applied ?id)) ; Mark this resist as applied to this damage source
   (modify ?d (amount ?reduced-amount))
)

(assert (damage (source "1") (target "2") (amount 10.0) (type "physical") (id 0)))
(assert (damage (source "1") (target "2") (amount 5.0) (type "physical") (id 1)))
(assert (resist (type "physical") (factor 0.9) (player_id "2")))
game-development clips
1个回答
0
投票

根据事实,拥有一个跟踪已应用内容的槽可能是最好的解决方案。

对象模式仅在对显式匹配的槽进行更改时才会触发,因此,如果您使用实例并且金额槽未包含在模式中,则可以在规则的操作中修改它,而不会创建无限循环:

         CLIPS (6.4.1 4/8/23)
CLIPS> 
(defclass DAMAGE
  (is-a USER)
  (slot source (type STRING))
  (slot target (type STRING))
  (slot amount (type FLOAT))
  (slot type (type STRING)))
CLIPS> 
(defclass RESIST
  (is-a USER)
  (slot type (type STRING))
  (slot factor (type FLOAT))
  (slot player_id (type STRING)))
CLIPS> 
(defrule damage-resistance
   ?d <- (object (is-a DAMAGE) (source ?source) (target ?target) (type ?type))
   (object (is-a RESIST) (type ?type) (factor ?factor) (player_id ?target))
   =>
   (bind ?reduced-amount (* (send ?d get-amount) ?factor))
   (modify-instance ?d (amount ?reduced-amount)))
CLIPS> 
(definstances initial
  (d1 of DAMAGE (source "1") (target "2") (amount 10.0) (type "physical"))
  (d2 of DAMAGE (source "2") (target "2") (amount 18.0) (type "physical"))
  (r1 of RESIST (type "physical") (factor 0.9) (player_id "2"))
  (r2 of RESIST (type "physical") (factor 0.8) (player_id "2")))
CLIPS> (reset)
CLIPS> (watch rules)
CLIPS> (run)
FIRE    1 damage-resistance: [d1],[r2]
FIRE    2 damage-resistance: [d2],[r2]
FIRE    3 damage-resistance: [d1],[r1]
FIRE    4 damage-resistance: [d2],[r1]
CLIPS> (send [d1] print)
[d1] of DAMAGE
(source "1")
(target "2")
(amount 7.2)
(type "physical")
CLIPS> (send [d2] print)
[d2] of DAMAGE
(source "2")
(target "2")
(amount 12.96)
(type "physical")
CLIPS> 
© www.soinside.com 2019 - 2024. All rights reserved.