Closed ertugrulcetin closed 11 months ago
O'doyle doesn't have a concept of salience so you can't rely on rules firing in a certain order regardless of how they're defined. I think to update the same fact from multiple rules you need to be able to swap!
values but o'doyle only has upsert-fact
which is like reset!
. I'm not sure of the right approach myself, but for doing something like taking damage and regening health in the same tick I just have it all in one rule, but that's not possible with dynamic rules.
Unrelated, but I think using o/ruleset
is more readable even for single rules.
I don't believe this is related to the salience feature; somehow, dynamic rules behave very differently. Using add-rule
in a different order shouldn't affect the behavior of the rule engine.
@oakes any updates on this?
I don't see how it could be related to dynamic rules; ruleset
is ultimately calling ->rule
underneath (a different arity, but they pretty much are the same). If you change your code to use separate ruleset
s for player-forward
, move-player
, and zoom-camera
, I'm guessing you'll see the same issue.
The order that rules run in a given iteration of fire-rules
is determined by then-queue
and then-finally-queue
, which are sets, so the order is effectively random (based on how the values hashed). Those values include the memory node id, which is just an incrementing number, so rules added earlier have lower memory node ids. I'm guessing that by changing the order in which you add the rules, it is affecting how their memory node ids are hashing, which causes the order in which they fire to change. Definitely not something one can rely on :D
It looks like the issue here is that you have two rules that are both modifying the same state and are both triggered mostly the same way (via the ::time ::delta
fact). In its current form, I think it's inherently in conflict -- which one is supposed to take precedence? As @nivekuil mentioned, maybe this should be expressed as a single rule? Sometimes, a good ol' fashioned if
or cond
within a rule is better than separate rules, if order/precedence is important.
Having something like o/insert
that behaves like swap!
(presumably called o/update
) is something I've thought about, but I don't think it'd necessarily fix this. A pair of update
s can lead to a non-deterministic results just like a pair of insert
s. You have another location to add some conditional logic, but the original problem remains the same -- your rules are contradicting each other, and now you need to decide who wins.
@oakes thank you for the long and detailed explanation! I'll see what I can do for my use case.
There are two important rules:
::update-player-forward
and::move-player
. The issue is that::update-player-forward
must run before::move-player
. When I order the rules in this manner, it doesn't work as expected;When I change the rule order,
::update-player-forward
runs before::move-player
(the positions ofzoom-camera
andmove-player
are swapped).Here is my rules (not working as expected):