// ============================================================
// Threshold parallel policy
// ============================================================
///|
/// Tick all children until at least `successes_needed` succeed.
/// A quorum can fail early when the remaining children cannot reach it.
pub fn quorum_node(children : Array[Node], successes_needed : Int) -> Node {
let required = if successes_needed > 0 { successes_needed } else { 1 }
let tick = fn(bb) {
let mut success_count = 0
let mut failure_count = 0
let mut running_count = 0
for child in children {
match child.tick(bb) {
Status::BTSuccess => success_count = success_count + 1
Status::BTFailure => failure_count = failure_count + 1
Status::BTRunning => running_count = running_count + 1
}
}
if success_count >= required {
for child in children {
child.reset()
}
Status::BTSuccess
} else if success_count + running_count < required {
for child in children {
child.reset()
}
Status::BTFailure
} else {
Status::BTRunning
}
}
let reset = fn() {
for child in children {
child.reset()
}
}
Node::new(tick, reset)
}
///|
/// Return the number of child outcomes that are already terminal.
pub fn count_terminal(children : Array[Node], bb : Blackboard) -> Int {
let mut count = 0
for child in children {
match child.tick(bb) {
Status::BTSuccess | Status::BTFailure => count = count + 1
Status::BTRunning => ()
}
}
count
}
///|
/// Select the first child that succeeds, resetting a running loser on success.
pub fn race_node(children : Array[Node]) -> Node {
let tick = fn(bb) {
let mut running_count = 0
for child in children {
match child.tick(bb) {
Status::BTSuccess => {
for other in children {
other.reset()
}
return Status::BTSuccess
}
Status::BTFailure => ()
Status::BTRunning => running_count = running_count + 1
}
}
if running_count > 0 {
Status::BTRunning
} else {
Status::BTFailure
}
}
let reset = fn() {
for child in children {
child.reset()
}
}
Node::new(tick, reset)
}