1pub mod r#impl;
5pub mod score;
6
7use crate::error::Result;
8use crate::world::World;
9use r#impl::idle::IdleBehavior;
10use rand::seq::{IndexedRandom, SliceRandom};
11use score::BehaviorScore;
12use std::any::Any;
13use std::collections::HashSet;
14use std::fmt::Debug;
15use std::ops::ControlFlow;
16
17pub trait Behavior: Any + Debug {
18 fn score(&self, world: &World) -> Result<BehaviorScore>;
19
20 fn behave(&self, world: &mut World) -> Result<ControlFlow<()>>;
21
22 fn boxed(self) -> Box<dyn Behavior>
23 where
24 Self: Sized + 'static,
25 {
26 Box::new(self)
27 }
28}
29
30#[must_use]
31pub struct BehaviorProcessor<'a> {
32 world: &'a mut World,
33 behaviors: Vec<Box<dyn Behavior>>,
34 buffer: Vec<(usize, BehaviorScore)>,
35 candidates: Vec<usize>,
36 broken: HashSet<usize>,
37}
38
39impl<'a> BehaviorProcessor<'a> {
40 pub(crate) fn new(world: &'a mut World, mut behaviors: Vec<Box<dyn Behavior>>) -> Self {
41 let buffer = Vec::with_capacity(behaviors.len());
42 let candidates = Vec::new();
43 let broken = HashSet::new();
44
45 behaviors.shuffle(&mut rand::rng());
46
47 Self {
48 world,
49 behaviors,
50 buffer,
51 candidates,
52 broken,
53 }
54 }
55
56 fn is_idle(&self, idx: usize) -> bool {
57 (self.behaviors[idx].as_ref() as &dyn Any).is::<IdleBehavior>()
58 }
59}
60
61impl Iterator for BehaviorProcessor<'_> {
62 type Item = Result<()>;
63
64 fn next(&mut self) -> Option<Self::Item> {
65 self.buffer.clear();
66 self.candidates.clear();
67
68 for (idx, behavior) in self.behaviors.iter().enumerate() {
69 if !self.broken.contains(&idx) {
70 match behavior.score(self.world) {
71 Ok(score) => {
72 if score > BehaviorScore::MIN {
73 self.buffer.push((idx, score));
74 }
75 }
76 Err(err) => return Some(Err(err)),
77 }
78 }
79 }
80
81 self
82 .buffer
83 .sort_unstable_by_key(|(_, score)| *score);
84
85 let highest = self.buffer.last()?;
86 if self.is_idle(highest.0) {
87 return None;
88 }
89
90 self
91 .buffer
92 .iter()
93 .filter(|(_, score)| highest.1.is_within_range(*score, 0.2))
94 .map(|(idx, _)| *idx)
95 .collect_into(&mut self.candidates);
96
97 let idx = self
98 .candidates
99 .choose(&mut rand::rng())
100 .copied()
101 .filter(|idx| !self.is_idle(*idx))?;
102
103 let behavior = &self.behaviors[idx];
104 match behavior.behave(self.world) {
105 Ok(cf) => {
106 if let ControlFlow::Break(()) = cf {
107 self.broken.insert(idx);
108 }
109
110 Some(Ok(()))
111 }
112 Err(err) => Some(Err(err)),
113 }
114 }
115}