1use std::{cmp::Ordering, sync::Arc};
6
7use bevy::prelude::*;
8#[cfg(feature = "trace")]
9use bevy::utils::tracing::trace;
10
11use crate::{
12 evaluators::Evaluator,
13 measures::{Measure, WeightedMeasure},
14 thinker::{Actor, Scorer, ScorerSpan},
15};
16
17#[derive(Clone, Component, Debug, Default, Reflect)]
19pub struct Score(pub(crate) f32);
20
21impl Score {
22 pub fn get(&self) -> f32 {
24 self.0
25 }
26
27 pub fn set(&mut self, value: f32) {
33 if !(0.0..=1.0).contains(&value) {
34 panic!("Score value must be between 0.0 and 1.0");
35 }
36 self.0 = value;
37 }
38
39 pub fn set_unchecked(&mut self, value: f32) {
44 self.0 = value;
45 }
46}
47
48#[reflect_trait]
56pub trait ScorerBuilder: std::fmt::Debug + Sync + Send {
57 fn build(&self, cmd: &mut Commands, scorer: Entity, actor: Entity);
95
96 fn label(&self) -> Option<&str> {
98 None
99 }
100}
101
102pub fn spawn_scorer<T: ScorerBuilder + ?Sized>(
103 builder: &T,
104 cmd: &mut Commands,
105 actor: Entity,
106) -> Entity {
107 let scorer_ent = cmd.spawn_empty().id();
108 let span = ScorerSpan::new(scorer_ent, ScorerBuilder::label(builder));
109 let _guard = span.span().enter();
110 debug!("New Scorer spawned.");
111 cmd.entity(scorer_ent)
112 .insert(Name::new("Scorer"))
113 .insert(Score::default())
114 .insert(Actor(actor));
115 builder.build(cmd, scorer_ent, actor);
116 std::mem::drop(_guard);
117 cmd.entity(scorer_ent).insert(span);
118 scorer_ent
119}
120
121#[derive(Clone, Component, Debug, Reflect)]
124pub struct FixedScore(pub f32);
125
126impl FixedScore {
127 pub fn build(score: f32) -> FixedScorerBuilder {
128 FixedScorerBuilder { score, label: None }
129 }
130}
131
132pub fn fixed_score_system(mut query: Query<(&FixedScore, &mut Score, &ScorerSpan)>) {
133 for (FixedScore(fixed), mut score, _span) in query.iter_mut() {
134 #[cfg(feature = "trace")]
135 {
136 let _guard = _span.span().enter();
137 trace!("FixedScore: {}", fixed);
138 }
139 score.set(*fixed);
140 }
141}
142
143#[derive(Debug, Reflect)]
144pub struct FixedScorerBuilder {
145 score: f32,
146 label: Option<String>,
147}
148
149impl FixedScorerBuilder {
150 pub fn label(mut self, label: impl Into<String>) -> Self {
151 self.label = Some(label.into());
152 self
153 }
154}
155
156impl ScorerBuilder for FixedScorerBuilder {
157 fn build(&self, cmd: &mut Commands, scorer: Entity, _actor: Entity) {
158 cmd.entity(scorer).insert(FixedScore(self.score));
159 }
160
161 fn label(&self) -> Option<&str> {
162 self.label.as_deref().or(Some("FixedScore"))
163 }
164}
165
166#[derive(Component, Debug, Reflect)]
192pub struct AllOrNothing {
193 threshold: f32,
194 scorers: Vec<Scorer>,
195}
196
197impl AllOrNothing {
198 pub fn build(threshold: f32) -> AllOrNothingBuilder {
199 AllOrNothingBuilder {
200 threshold,
201 scorers: Vec::new(),
202 scorer_labels: Vec::new(),
203 label: None,
204 }
205 }
206}
207
208pub fn all_or_nothing_system(
209 query: Query<(Entity, &AllOrNothing, &ScorerSpan)>,
210 mut scores: Query<&mut Score>,
211) {
212 for (
213 aon_ent,
214 AllOrNothing {
215 threshold,
216 scorers: children,
217 },
218 _span,
219 ) in query.iter()
220 {
221 let mut sum = 0.0;
222 for Scorer(child) in children.iter() {
223 let score = scores.get_mut(*child).expect("where is it?");
224 if score.0 < *threshold {
225 sum = 0.0;
226 break;
227 } else {
228 sum += score.0;
229 }
230 }
231 let mut score = scores.get_mut(aon_ent).expect("where did it go?");
232 score.set(crate::evaluators::clamp(sum, 0.0, 1.0));
233 #[cfg(feature = "trace")]
234 {
235 let _guard = _span.span().enter();
236 trace!("AllOrNothing score: {}", score.get());
237 }
238 }
239}
240
241#[derive(Debug, Clone, Reflect)]
242pub struct AllOrNothingBuilder {
243 threshold: f32,
244 #[reflect(ignore)]
245 scorers: Vec<Arc<dyn ScorerBuilder>>,
246 scorer_labels: Vec<String>,
247 label: Option<String>,
248}
249
250impl AllOrNothingBuilder {
251 pub fn push(mut self, scorer: impl ScorerBuilder + 'static) -> Self {
253 if let Some(label) = scorer.label() {
254 self.scorer_labels.push(label.into());
255 } else {
256 self.scorer_labels.push("Unnamed Scorer".into());
257 }
258 self.scorers.push(Arc::new(scorer));
259 self
260 }
261
262 pub fn label(mut self, label: impl AsRef<str>) -> Self {
264 self.label = Some(label.as_ref().into());
265 self
266 }
267}
268
269impl ScorerBuilder for AllOrNothingBuilder {
270 fn label(&self) -> Option<&str> {
271 self.label.as_deref().or(Some("AllOrNothing"))
272 }
273
274 fn build(&self, cmd: &mut Commands, scorer: Entity, actor: Entity) {
275 let scorers: Vec<_> = self
276 .scorers
277 .iter()
278 .map(|scorer| spawn_scorer(&**scorer, cmd, actor))
279 .collect();
280 cmd.entity(scorer)
281 .insert(Score::default())
282 .add_children(&scorers[..])
283 .insert(Name::new("Scorer"))
284 .insert(AllOrNothing {
285 threshold: self.threshold,
286 scorers: scorers.into_iter().map(Scorer).collect(),
287 });
288 }
289}
290
291#[derive(Component, Debug, Reflect)]
315pub struct SumOfScorers {
316 threshold: f32,
317 scorers: Vec<Scorer>,
318 scorer_labels: Vec<String>,
319}
320
321impl SumOfScorers {
322 pub fn build(threshold: f32) -> SumOfScorersBuilder {
323 SumOfScorersBuilder {
324 threshold,
325 scorers: Vec::new(),
326 scorer_labels: Vec::new(),
327 label: None,
328 }
329 }
330}
331
332pub fn sum_of_scorers_system(
333 query: Query<(Entity, &SumOfScorers, &ScorerSpan)>,
334 mut scores: Query<&mut Score>,
335) {
336 for (
337 sos_ent,
338 SumOfScorers {
339 threshold,
340 scorers: children,
341 ..
342 },
343 _span,
344 ) in query.iter()
345 {
346 let mut sum = 0.0;
347 for Scorer(child) in children.iter() {
348 let score = scores.get_mut(*child).expect("where is it?");
349 sum += score.0;
350 }
351 if sum < *threshold {
352 sum = 0.0;
353 }
354 let mut score = scores.get_mut(sos_ent).expect("where did it go?");
355 score.set(crate::evaluators::clamp(sum, 0.0, 1.0));
356 #[cfg(feature = "trace")]
357 {
358 let _guard = _span.span().enter();
359 trace!(
360 "SumOfScorers score: {}, from {} scores",
361 score.get(),
362 children.len()
363 );
364 }
365 }
366}
367
368#[derive(Debug, Clone, Reflect)]
369pub struct SumOfScorersBuilder {
370 threshold: f32,
371 #[reflect(ignore)]
372 scorers: Vec<Arc<dyn ScorerBuilder>>,
373 scorer_labels: Vec<String>,
374 label: Option<String>,
375}
376
377impl SumOfScorersBuilder {
378 pub fn push(mut self, scorer: impl ScorerBuilder + 'static) -> Self {
380 if let Some(label) = scorer.label() {
381 self.scorer_labels.push(label.into());
382 } else {
383 self.scorer_labels.push("Unnamed Scorer".into());
384 }
385 self.scorers.push(Arc::new(scorer));
386 self
387 }
388
389 pub fn label(mut self, label: impl AsRef<str>) -> Self {
391 self.label = Some(label.as_ref().into());
392 self
393 }
394}
395
396impl ScorerBuilder for SumOfScorersBuilder {
397 fn label(&self) -> Option<&str> {
398 self.label.as_deref().or(Some("SumOfScorers"))
399 }
400
401 #[allow(clippy::needless_collect)]
402 fn build(&self, cmd: &mut Commands, scorer: Entity, actor: Entity) {
403 let scorers: Vec<_> = self
404 .scorers
405 .iter()
406 .map(|scorer| spawn_scorer(&**scorer, cmd, actor))
407 .collect();
408 cmd.entity(scorer)
409 .add_children(&scorers[..])
410 .insert(SumOfScorers {
411 threshold: self.threshold,
412 scorers: scorers.into_iter().map(Scorer).collect(),
413 scorer_labels: self.scorer_labels.clone(),
414 });
415 }
416}
417
418#[derive(Component, Debug, Reflect)]
450pub struct ProductOfScorers {
451 threshold: f32,
452 use_compensation: bool,
453 scorers: Vec<Scorer>,
454 scorer_labels: Vec<String>,
455}
456
457impl ProductOfScorers {
458 pub fn build(threshold: f32) -> ProductOfScorersBuilder {
459 ProductOfScorersBuilder {
460 threshold,
461 use_compensation: false,
462 scorers: Vec::new(),
463 scorer_labels: Vec::new(),
464 label: None,
465 }
466 }
467}
468
469pub fn product_of_scorers_system(
470 query: Query<(Entity, &ProductOfScorers, &ScorerSpan)>,
471 mut scores: Query<&mut Score>,
472) {
473 for (
474 sos_ent,
475 ProductOfScorers {
476 threshold,
477 use_compensation,
478 scorers: children,
479 ..
480 },
481 _span,
482 ) in query.iter()
483 {
484 let mut product = 1.0;
485 let mut num_scorers = 0;
486
487 for Scorer(child) in children.iter() {
488 let score = scores.get_mut(*child).expect("where is it?");
489 product *= score.0;
490 num_scorers += 1;
491 }
492
493 if *use_compensation && product < 1.0 {
496 let mod_factor = 1.0 - 1.0 / (num_scorers as f32);
497 let makeup = (1.0 - product) * mod_factor;
498 product += makeup * product;
499 }
500
501 if product < *threshold {
502 product = 0.0;
503 }
504
505 let mut score = scores.get_mut(sos_ent).expect("where did it go?");
506 score.set(product.clamp(0.0, 1.0));
507 #[cfg(feature = "trace")]
508 {
509 let _guard = _span.span().enter();
510 trace!(
511 "ProductOfScorers score: {}, from {} scores",
512 score.get(),
513 children.len()
514 );
515 }
516 }
517}
518
519#[derive(Debug, Clone)]
520pub struct ProductOfScorersBuilder {
521 threshold: f32,
522 use_compensation: bool,
523 scorers: Vec<Arc<dyn ScorerBuilder>>,
524 scorer_labels: Vec<String>,
525 label: Option<String>,
526}
527
528impl ProductOfScorersBuilder {
529 pub fn use_compensation(mut self, use_compensation: bool) -> Self {
533 self.use_compensation = use_compensation;
534 self
535 }
536
537 pub fn push(mut self, scorer: impl ScorerBuilder + 'static) -> Self {
539 if let Some(label) = scorer.label() {
540 self.scorer_labels.push(label.into());
541 } else {
542 self.scorer_labels.push("Unnamed Scorer".into());
543 }
544 self.scorers.push(Arc::new(scorer));
545 self
546 }
547
548 pub fn label(mut self, label: impl AsRef<str>) -> Self {
550 self.label = Some(label.as_ref().into());
551 self
552 }
553}
554
555impl ScorerBuilder for ProductOfScorersBuilder {
556 fn label(&self) -> Option<&str> {
557 self.label.as_deref().or(Some("ProductOfScorers"))
558 }
559
560 #[allow(clippy::needless_collect)]
561 fn build(&self, cmd: &mut Commands, scorer: Entity, actor: Entity) {
562 let scorers: Vec<_> = self
563 .scorers
564 .iter()
565 .map(|scorer| spawn_scorer(&**scorer, cmd, actor))
566 .collect();
567 cmd.entity(scorer)
568 .add_children(&scorers[..])
569 .insert(ProductOfScorers {
570 threshold: self.threshold,
571 use_compensation: self.use_compensation,
572 scorers: scorers.into_iter().map(Scorer).collect(),
573 scorer_labels: self.scorer_labels.clone(),
574 });
575 }
576}
577
578#[derive(Component, Debug, Reflect)]
605pub struct WinningScorer {
606 threshold: f32,
607 scorers: Vec<Scorer>,
608 scorer_labels: Vec<String>,
609}
610
611impl WinningScorer {
612 pub fn build(threshold: f32) -> WinningScorerBuilder {
613 WinningScorerBuilder {
614 threshold,
615 scorers: Vec::new(),
616 scorer_labels: Vec::new(),
617 label: None,
618 }
619 }
620}
621
622pub fn winning_scorer_system(
623 mut query: Query<(Entity, &mut WinningScorer, &ScorerSpan)>,
624 mut scores: Query<&mut Score>,
625) {
626 for (sos_ent, mut winning_scorer, _span) in query.iter_mut() {
627 let (threshold, children) = (winning_scorer.threshold, &mut winning_scorer.scorers);
628 let mut all_scores = children
629 .iter()
630 .map(|Scorer(e)| scores.get(*e).expect("where is it?"))
631 .collect::<Vec<&Score>>();
632
633 all_scores.sort_by(|a, b| a.get().partial_cmp(&b.get()).unwrap_or(Ordering::Equal));
634 let winning_score_or_zero = match all_scores.last() {
635 Some(s) => {
636 if s.get() < threshold {
637 0.0
638 } else {
639 s.get()
640 }
641 }
642 None => 0.0,
643 };
644 let mut score = scores.get_mut(sos_ent).expect("where did it go?");
645 score.set(crate::evaluators::clamp(winning_score_or_zero, 0.0, 1.0));
646 #[cfg(feature = "trace")]
647 {
648 let _guard = _span.span().enter();
649 trace!(
650 "WinningScorer score: {}, from {} scores",
651 score.get(),
652 children.len()
653 );
654 }
655 }
656}
657
658#[derive(Debug, Clone, Reflect)]
659pub struct WinningScorerBuilder {
660 threshold: f32,
661 #[reflect(ignore)]
662 scorers: Vec<Arc<dyn ScorerBuilder>>,
663 scorer_labels: Vec<String>,
664 label: Option<String>,
665}
666
667impl WinningScorerBuilder {
668 pub fn push(mut self, scorer: impl ScorerBuilder + 'static) -> Self {
670 if let Some(label) = scorer.label() {
671 self.scorer_labels.push(label.into());
672 } else {
673 self.scorer_labels.push("Unnamed Scorer".into())
674 }
675 self.scorers.push(Arc::new(scorer));
676 self
677 }
678
679 pub fn label(mut self, label: impl AsRef<str>) -> Self {
681 self.label = Some(label.as_ref().into());
682 self
683 }
684}
685
686impl ScorerBuilder for WinningScorerBuilder {
687 fn label(&self) -> Option<&str> {
688 self.label.as_deref().or(Some("WinningScorer"))
689 }
690
691 #[allow(clippy::needless_collect)]
692 fn build(&self, cmd: &mut Commands, scorer: Entity, actor: Entity) {
693 let scorers: Vec<_> = self
694 .scorers
695 .iter()
696 .map(|scorer| spawn_scorer(&**scorer, cmd, actor))
697 .collect();
698 cmd.entity(scorer)
699 .add_children(&scorers[..])
700 .insert(WinningScorer {
701 threshold: self.threshold,
702 scorers: scorers.into_iter().map(Scorer).collect(),
703 scorer_labels: self.scorer_labels.clone(),
704 });
705 }
706}
707
708#[derive(Component, Debug, Reflect)]
737#[reflect(from_reflect = false)]
738pub struct EvaluatingScorer {
739 scorer: Scorer,
740 evaluator_string: String,
741 #[reflect(ignore)]
742 evaluator: Arc<dyn Evaluator>,
743}
744
745impl EvaluatingScorer {
746 pub fn build(
747 scorer: impl ScorerBuilder + 'static,
748 evaluator: impl Evaluator + 'static,
749 ) -> EvaluatingScorerBuilder {
750 EvaluatingScorerBuilder {
751 scorer_label: scorer.label().map(|s| s.into()),
752 evaluator: Arc::new(evaluator),
753 scorer: Arc::new(scorer),
754 label: None,
755 }
756 }
757}
758
759pub fn evaluating_scorer_system(
760 query: Query<(Entity, &EvaluatingScorer, &ScorerSpan)>,
761 mut scores: Query<&mut Score>,
762) {
763 for (sos_ent, eval_scorer, _span) in query.iter() {
764 let inner_score = scores
766 .get(eval_scorer.scorer.0)
767 .expect("where did it go?")
768 .get();
769 let mut score = scores.get_mut(sos_ent).expect("where did it go?");
771 score.set(crate::evaluators::clamp(
772 eval_scorer.evaluator.evaluate(inner_score),
773 0.0,
774 1.0,
775 ));
776 #[cfg(feature = "trace")]
777 {
778 let _guard = _span.span().enter();
779 trace!(
780 "EvaluatingScorer score: {}, from score: {}",
781 score.get(),
782 inner_score
783 );
784 }
785 }
786}
787
788#[derive(Debug, Reflect)]
789#[reflect(from_reflect = false)]
790pub struct EvaluatingScorerBuilder {
791 #[reflect(ignore)]
792 scorer: Arc<dyn ScorerBuilder>,
793 scorer_label: Option<String>,
794 #[reflect(ignore)]
795 evaluator: Arc<dyn Evaluator>,
796 label: Option<String>,
797}
798
799impl ScorerBuilder for EvaluatingScorerBuilder {
800 fn label(&self) -> Option<&str> {
801 self.label.as_deref().or(Some("EvaluatingScorer"))
802 }
803
804 fn build(&self, cmd: &mut Commands, scorer: Entity, actor: Entity) {
805 let inner_scorer = spawn_scorer(&*self.scorer, cmd, actor);
806 let scorers = [inner_scorer];
807 cmd.entity(scorer)
808 .add_children(&scorers[..])
809 .insert(EvaluatingScorer {
810 evaluator: self.evaluator.clone(),
811 scorer: Scorer(inner_scorer),
812 evaluator_string: format!("{:#?}", self.evaluator),
813 });
814 }
815}
816
817#[derive(Component, Debug, Reflect)]
868#[reflect(from_reflect = false)]
869pub struct MeasuredScorer {
870 threshold: f32,
871 #[reflect(ignore)]
872 measure: Arc<dyn Measure>,
873 measure_string: String,
874 scorers: Vec<(Scorer, f32)>,
875}
876
877impl MeasuredScorer {
878 pub fn build(threshold: f32) -> MeasuredScorerBuilder {
879 MeasuredScorerBuilder {
880 threshold,
881 measure: Arc::new(WeightedMeasure),
882 measure_string: format!("{WeightedMeasure:#?}"),
883 scorers: Vec::new(),
884 scorer_labels: Vec::new(),
885 label: None,
886 }
887 }
888}
889
890pub fn measured_scorers_system(
891 query: Query<(Entity, &MeasuredScorer, &ScorerSpan)>,
892 mut scores: Query<&mut Score>,
893) {
894 for (
895 sos_ent,
896 MeasuredScorer {
897 threshold,
898 measure,
899 scorers: children,
900 ..
901 },
902 _span,
903 ) in query.iter()
904 {
905 let measured_score = measure.calculate(
906 children
907 .iter()
908 .map(|(scorer, weight)| (scores.get(scorer.0).expect("where is it?"), *weight))
909 .collect::<Vec<_>>(),
910 );
911 let mut score = scores.get_mut(sos_ent).expect("where did it go?");
912
913 if measured_score < *threshold {
914 score.set(0.0);
915 } else {
916 score.set(measured_score.clamp(0.0, 1.0));
917 }
918 #[cfg(feature = "trace")]
919 {
920 let _guard = _span.span().enter();
921 trace!(
922 "MeasuredScorer score: {}, from {} scores",
923 score.get(),
924 children.len()
925 );
926 }
927 }
928}
929
930#[derive(Debug, Reflect)]
931#[reflect(from_reflect = false)]
932pub struct MeasuredScorerBuilder {
933 threshold: f32,
934 #[reflect(ignore)]
935 measure: Arc<dyn Measure>,
936 measure_string: String,
937 #[reflect(ignore)]
938 scorers: Vec<(Arc<dyn ScorerBuilder>, f32)>,
939 scorer_labels: Vec<String>,
940 label: Option<String>,
941}
942
943impl MeasuredScorerBuilder {
944 pub fn measure(mut self, measure: impl Measure + 'static) -> Self {
946 self.measure_string = format!("{measure:#?}");
947 self.measure = Arc::new(measure);
948 self
949 }
950
951 pub fn push(mut self, scorer: impl ScorerBuilder + 'static, weight: f32) -> Self {
952 if let Some(label) = scorer.label() {
953 self.scorer_labels.push(label.into());
954 } else {
955 self.scorer_labels.push("Unnamed Scorer".into());
956 }
957 self.scorers.push((Arc::new(scorer), weight));
958 self
959 }
960
961 pub fn label(mut self, label: impl AsRef<str>) -> Self {
963 self.label = Some(label.as_ref().into());
964 self
965 }
966}
967
968impl ScorerBuilder for MeasuredScorerBuilder {
969 fn label(&self) -> Option<&str> {
970 self.label.as_deref().or(Some("MeasuredScorer"))
971 }
972
973 #[allow(clippy::needless_collect)]
974 fn build(&self, cmd: &mut Commands, scorer: Entity, actor: Entity) {
975 let scorers: Vec<_> = self
976 .scorers
977 .iter()
978 .map(|(scorer, _)| spawn_scorer(&**scorer, cmd, actor))
979 .collect();
980 cmd.entity(scorer)
981 .add_children(&scorers[..])
982 .insert(MeasuredScorer {
983 threshold: self.threshold,
984 measure: self.measure.clone(),
985 scorers: scorers
986 .into_iter()
987 .map(Scorer)
988 .zip(self.scorers.iter().map(|(_, weight)| *weight))
989 .collect(),
990 measure_string: self.measure_string.clone(),
991 });
992 }
993}