// ============================================================
// Composable condition helpers
// ============================================================
///|
/// Evaluate all predicates from left to right.
pub fn all_conditions(
predicates : Array[(Blackboard) -> Bool],
bb : Blackboard,
) -> Bool {
for predicate in predicates {
if !predicate(bb) {
return false
}
}
true
}
///|
/// Evaluate predicates until one is true.
pub fn any_conditions(
predicates : Array[(Blackboard) -> Bool],
bb : Blackboard,
) -> Bool {
for predicate in predicates {
if predicate(bb) {
return true
}
}
false
}
///|
/// Create the logical negation of a predicate.
pub fn not_condition(predicate : (Blackboard) -> Bool) -> (Blackboard) -> Bool {
fn(bb : Blackboard) { !predicate(bb) }
}
///|
/// A condition node backed by a set of predicates.
pub fn all_condition_node(predicates : Array[(Blackboard) -> Bool]) -> Node {
condition_node(fn(bb : Blackboard) { all_conditions(predicates, bb) })
}
///|
/// A condition node that succeeds when any predicate is true.
pub fn any_condition_node(predicates : Array[(Blackboard) -> Bool]) -> Node {
condition_node(fn(bb : Blackboard) { any_conditions(predicates, bb) })
}
///|
/// Compare a blackboard integer with a threshold.
pub(all) enum IntComparison {
LessThan
LessOrEqual
EqualTo
GreaterOrEqual
GreaterThan
} derive(Eq, Debug)
///|
/// Return every comparison in a stable order for configuration UIs.
pub fn all_int_comparisons() -> Array[IntComparison] {
[
IntComparison::LessThan,
IntComparison::LessOrEqual,
IntComparison::EqualTo,
IntComparison::GreaterOrEqual,
IntComparison::GreaterThan,
]
}
///|
/// Return whether an integer field satisfies a comparison.
pub fn compare_int(
bb : Blackboard,
key : String,
comparison : IntComparison,
expected : Int,
) -> Bool {
match bb.get_int(key) {
None => false
Some(actual) =>
match comparison {
IntComparison::LessThan => actual < expected
IntComparison::LessOrEqual => actual <= expected
IntComparison::EqualTo => actual == expected
IntComparison::GreaterOrEqual => actual >= expected
IntComparison::GreaterThan => actual > expected
}
}
}
///|
/// Make an integer comparison node.
pub fn int_comparison_node(
key : String,
comparison : IntComparison,
expected : Int,
) -> Node {
condition_node(fn(bb : Blackboard) {
compare_int(bb, key, comparison, expected)
})
}
///|
/// Detect whether an integer value changed since the previous tick.
pub fn int_changed_node(key : String) -> Node {
let previous : Ref[Int?] = Ref::new(None)
let tick = fn(bb : Blackboard) {
match bb.get_int(key) {
None => Status::BTFailure
Some(value) =>
match previous.get() {
None => {
previous.set(Some(value))
Status::BTFailure
}
Some(old) => {
previous.set(Some(value))
if old == value {
Status::BTFailure
} else {
Status::BTSuccess
}
}
}
}
}
let reset = fn() { previous.set(None) }
Node::new(tick, reset)
}