1pub mod build;
5pub mod idle;
6pub mod recruit;
7
8use crate::error::Result;
9use crate::world::World;
10use derive_more::Into;
11use idle::IdleBehavior;
12use rand::rngs::ThreadRng;
13use rand::seq::{IndexedRandom, SliceRandom};
14use std::any::Any;
15use std::cmp::Ordering;
16use std::collections::HashSet;
17use std::fmt::Debug;
18use std::ops::{Add, AddAssign, ControlFlow, Div, DivAssign, Mul, MulAssign, Sub, SubAssign};
19
20pub trait Behavior: Any + Debug {
21 fn score(&self, world: &World) -> Result<BehaviorScore>;
22 fn behave(&self, world: &mut World) -> Result<ControlFlow<()>>;
23
24 fn boxed(self) -> Box<dyn Behavior>
25 where
26 Self: Sized + 'static,
27 {
28 Box::new(self)
29 }
30}
31
32#[must_use]
33pub struct BehaviorProcessor<'a> {
34 world: &'a mut World,
35 behaviors: Vec<Box<dyn Behavior>>,
36 buffer: Vec<(usize, BehaviorScore)>,
37 candidates: Vec<(usize, f64)>,
38 broken: HashSet<usize>,
39 rng: ThreadRng,
40}
41
42impl<'a> BehaviorProcessor<'a> {
43 pub(crate) fn new(world: &'a mut World, mut behaviors: Vec<Box<dyn Behavior>>) -> Self {
44 let buffer = Vec::with_capacity(behaviors.len());
45 let candidates = Vec::new();
46 let broken = HashSet::new();
47
48 let mut rng = rand::rng();
49 behaviors.shuffle(&mut rng);
50
51 Self {
52 world,
53 behaviors,
54 buffer,
55 candidates,
56 broken,
57 rng,
58 }
59 }
60
61 fn is_idle(&self, idx: usize) -> bool {
62 (self.behaviors[idx].as_ref() as &dyn Any).is::<IdleBehavior>()
63 }
64}
65
66impl Iterator for BehaviorProcessor<'_> {
67 type Item = Result<()>;
68
69 fn next(&mut self) -> Option<Self::Item> {
70 self.buffer.clear();
71 self.candidates.clear();
72
73 for (idx, behavior) in self.behaviors.iter().enumerate() {
74 if !self.broken.contains(&idx) {
75 match behavior.score(self.world) {
76 Ok(score) => self.buffer.push((idx, score)),
77 Err(err) => return Some(Err(err)),
78 }
79 }
80 }
81
82 self
83 .buffer
84 .sort_unstable_by_key(|(_, score)| *score);
85
86 let highest = self.buffer.last()?;
87 if self.is_idle(highest.0) {
88 return None;
89 }
90
91 self
92 .buffer
93 .iter()
94 .filter(|(_, score)| highest.1.is_within_range(*score, 0.2))
95 .map(|(idx, score)| (*idx, f64::from(*score)))
96 .collect_into(&mut self.candidates);
97
98 let idx = self
99 .candidates
100 .choose(&mut self.rng)
101 .map(|(idx, _)| *idx)
102 .filter(|idx| !self.is_idle(*idx))?;
103
104 let behavior = &self.behaviors[idx];
105 match behavior.behave(self.world) {
106 Ok(cf) => {
107 if let ControlFlow::Break(()) = cf {
108 self.broken.insert(idx);
109 }
110
111 Some(Ok(()))
112 }
113 Err(err) => Some(Err(err)),
114 }
115 }
116}
117
118#[derive(Clone, Copy, Debug, Into)]
119pub struct BehaviorScore(f64);
120
121impl BehaviorScore {
122 pub const MIN: Self = BehaviorScore(0.0);
123 pub const MAX: Self = BehaviorScore(1.0);
124
125 #[inline]
126 pub const fn new(score: f64) -> Self {
127 debug_assert!(score.is_finite());
128 debug_assert!(!score.is_subnormal());
129 Self(score.clamp(Self::MIN.0, Self::MAX.0))
130 }
131
132 #[inline]
133 pub(crate) fn is_within_range(self, other: BehaviorScore, range: f64) -> bool {
134 (self.0 - other.0).abs() < range
135 }
136}
137
138impl Default for BehaviorScore {
139 fn default() -> Self {
140 Self(0.0)
141 }
142}
143
144impl From<f64> for BehaviorScore {
145 fn from(score: f64) -> Self {
146 Self::new(score)
147 }
148}
149
150impl PartialEq for BehaviorScore {
151 fn eq(&self, other: &Self) -> bool {
152 matches!(self.0.total_cmp(&other.0), Ordering::Equal)
153 }
154}
155
156impl Eq for BehaviorScore {}
157
158impl PartialOrd for BehaviorScore {
159 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
160 Some(self.cmp(other))
161 }
162}
163
164impl Ord for BehaviorScore {
165 fn cmp(&self, other: &Self) -> Ordering {
166 self.0.total_cmp(&other.0)
167 }
168}
169
170impl PartialEq<f64> for BehaviorScore {
171 fn eq(&self, other: &f64) -> bool {
172 self.0.eq(other)
173 }
174}
175
176impl PartialOrd<f64> for BehaviorScore {
177 fn partial_cmp(&self, other: &f64) -> Option<Ordering> {
178 self.0.partial_cmp(other)
179 }
180}
181
182impl Add<f64> for BehaviorScore {
183 type Output = BehaviorScore;
184
185 fn add(self, rhs: f64) -> Self::Output {
186 BehaviorScore::new(self.0 + rhs)
187 }
188}
189
190impl AddAssign<f64> for BehaviorScore {
191 fn add_assign(&mut self, rhs: f64) {
192 *self = *self + rhs;
193 }
194}
195
196impl Sub<f64> for BehaviorScore {
197 type Output = BehaviorScore;
198
199 fn sub(self, rhs: f64) -> Self::Output {
200 BehaviorScore::new(self.0 - rhs)
201 }
202}
203
204impl SubAssign<f64> for BehaviorScore {
205 fn sub_assign(&mut self, rhs: f64) {
206 *self = *self - rhs;
207 }
208}
209
210impl Mul<f64> for BehaviorScore {
211 type Output = BehaviorScore;
212
213 fn mul(self, rhs: f64) -> Self::Output {
214 BehaviorScore::new(self.0 * rhs)
215 }
216}
217
218impl MulAssign<f64> for BehaviorScore {
219 fn mul_assign(&mut self, rhs: f64) {
220 *self = *self * rhs;
221 }
222}
223
224impl Div<f64> for BehaviorScore {
225 type Output = BehaviorScore;
226
227 fn div(self, rhs: f64) -> Self::Output {
228 BehaviorScore::new(self.0 / rhs)
229 }
230}
231
232impl DivAssign<f64> for BehaviorScore {
233 fn div_assign(&mut self, rhs: f64) {
234 *self = *self / rhs;
235 }
236}