big_brain/
pickers.rs

1//! Pickers are used by Thinkers to determine which of its Scorers will "win".
2
3use bevy::prelude::*;
4
5use crate::{choices::Choice, scorers::Score};
6
7/// Required trait for Pickers. A Picker is given a slice of choices and a
8/// query that can be passed into `Choice::calculate`.
9///
10/// Implementations of `pick` must return `Some(Choice)` for the `Choice` that
11/// was picked, or `None`.
12#[reflect_trait]
13pub trait Picker: std::fmt::Debug + Sync + Send {
14    fn pick<'a>(&self, choices: &'a [Choice], scores: &Query<&Score>) -> Option<&'a Choice>;
15}
16
17/// Picker that chooses the first `Choice` with a [`Score`] higher than its
18/// configured `threshold`.
19///
20/// ### Example
21///
22/// ```
23/// # use big_brain::prelude::*;
24/// # fn main() {
25/// Thinker::build()
26///     .picker(FirstToScore::new(0.8))
27///     // .when(...)
28/// # ;
29/// # }
30/// ```
31#[derive(Debug, Clone, Default)]
32pub struct FirstToScore {
33    pub threshold: f32,
34}
35
36impl FirstToScore {
37    pub fn new(threshold: f32) -> Self {
38        Self { threshold }
39    }
40}
41
42impl Picker for FirstToScore {
43    fn pick<'a>(&self, choices: &'a [Choice], scores: &Query<&Score>) -> Option<&'a Choice> {
44        for choice in choices {
45            let value = choice.calculate(scores);
46            if value >= self.threshold {
47                return Some(choice);
48            }
49        }
50        None
51    }
52}
53
54/// Picker that chooses the `Choice` with the highest non-zero [`Score`], and the first highest in case of a tie.
55///
56/// ### Example
57///
58/// ```
59/// # use big_brain::prelude::*;
60/// # fn main() {
61/// Thinker::build()
62///     .picker(Highest)
63///     // .when(...)
64/// # ;
65/// # }
66/// ```
67#[derive(Debug, Clone, Default)]
68pub struct Highest;
69
70impl Picker for Highest {
71    fn pick<'a>(&self, choices: &'a [Choice], scores: &Query<&Score>) -> Option<&'a Choice> {
72        let mut max_score = 0f32;
73
74        choices.iter().fold(None, |acc, choice| {
75            let score = choice.calculate(scores);
76
77            if score <= max_score || score <= 0.0 {
78                return acc;
79            }
80
81            max_score = score;
82            Some(choice)
83        })
84    }
85}
86
87/// Picker that chooses the highest `Choice` with a [`Score`] higher than its
88/// configured `threshold`.
89///
90/// ### Example
91///
92/// ```
93/// # use big_brain::prelude::*;
94/// # fn main() {
95/// Thinker::build()
96///     .picker(HighestToScore::new(0.8))
97///     // .when(...)
98/// # ;
99/// # }
100/// ```
101#[derive(Debug, Clone, Default)]
102pub struct HighestToScore {
103    pub threshold: f32,
104}
105
106impl HighestToScore {
107    pub fn new(threshold: f32) -> Self {
108        Self { threshold }
109    }
110}
111
112impl Picker for HighestToScore {
113    fn pick<'a>(&self, choices: &'a [Choice], scores: &Query<&Score>) -> Option<&'a Choice> {
114        let mut highest_score = 0f32;
115
116        choices.iter().fold(None, |acc, choice| {
117            let score = choice.calculate(scores);
118
119            if score <= self.threshold || score <= highest_score {
120                return acc;
121            }
122
123            highest_score = score;
124            Some(choice)
125        })
126    }
127}