diff --git a/examples/thirst.rs b/examples/thirst.rs index 914003e4d2e7d20c030e1e49667e344ec44e27f2..b0e54154831609946dd99b99a3b644bfe6f50ab5 100644 --- a/examples/thirst.rs +++ b/examples/thirst.rs @@ -24,7 +24,6 @@ pub fn thirst_system(time: Res<Time>, mut thirsts: Query<&mut Thirst>) { if thirst.thirst >= 100.0 { thirst.thirst = 100.0; } - println!("Thirst: {}", thirst.thirst); } } @@ -105,6 +104,7 @@ pub fn thirsty_scorer_system( // // The score here must be between 0.0 and 100.0. score.set(thirst.thirst); + println!("Thirst: {}", thirst.thirst); } } } diff --git a/src/evaluators.rs b/src/evaluators.rs index 36511926027c8c5fcd23869640c37236d02d5e1c..34711ce41dabce316e1f8a846cfc8c39fcfea225 100644 --- a/src/evaluators.rs +++ b/src/evaluators.rs @@ -140,7 +140,7 @@ impl Default for SigmoidEvaluator { } } -fn clamp<T: PartialOrd>(val: T, min: T, max: T) -> T { +pub(crate) fn clamp<T: PartialOrd>(val: T, min: T, max: T) -> T { let val = if val > max { max } else { val }; if val < min { min diff --git a/src/lib.rs b/src/lib.rs index a6f21184eea27e63a0625d430d3fb5f0c297d3b3..ddb48e8dc3bcfbb0ef37b629277eb4e0ffbcc0d3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -23,5 +23,8 @@ pub struct BigBrainPlugin; impl Plugin for BigBrainPlugin { fn build(&self, app: &mut AppBuilder) { app.add_system(thinker_system.system()); + app.add_system(fixed_score_system.system()); + app.add_system(all_or_nothing_system.system()); + app.add_system(sum_of_scorers_system.system()); } } diff --git a/src/scorers.rs b/src/scorers.rs index 1343bdd1c4dfb05c29314424c649334cda2bad8b..0df09596b6cd976ceed21fec704c19b337a029af 100644 --- a/src/scorers.rs +++ b/src/scorers.rs @@ -6,6 +6,9 @@ use crate::ScorerEnt; pub struct Score(pub(crate) f32); impl Score { + pub fn get(&self) -> f32 { + self.0 + } pub fn set(&mut self, value: f32) { if !(0.0..=100.0).contains(&value) { panic!("Score value must be between 0.0 and 100.0"); @@ -21,3 +24,155 @@ This trait defines new Scorers. In general, you should use the [derive macro](de pub trait Scorer: std::fmt::Debug + Sync + Send { fn build(&self, entity: Entity, cmd: &mut Commands) -> ScorerEnt; } + +#[derive(Debug)] +pub struct FixedScore(f32); + +pub fn fixed_score_system(mut query: Query<(&FixedScore, &mut Score)>) { + for (FixedScore(fixed), mut score) in query.iter_mut() { + score.set(*fixed); + } +} + +mod fixed_score { + use super::*; + + use serde::Deserialize; + + #[derive(Debug, Deserialize)] + struct FixedScore(f32); + + #[typetag::deserialize] + impl Scorer for FixedScore { + fn build(&self, actor: Entity, cmd: &mut Commands) -> ScorerEnt { + let ent = ScorerEnt(cmd.spawn().id()); + cmd.entity(ent.0) + .insert(Score::default()) + .insert(super::FixedScore(self.0)); + cmd.entity(actor).push_children(&[ent.0]); + ent + } + } +} + +#[derive(Debug)] +pub struct AllOrNothing { + threshold: f32, + scorers: Vec<ScorerEnt>, +} + +pub fn all_or_nothing_system(query: Query<(Entity, &AllOrNothing)>, mut scores: Query<&mut Score>) { + for ( + aon_ent, + AllOrNothing { + threshold, + scorers: children, + }, + ) in query.iter() + { + let mut sum = 0.0; + for ScorerEnt(child) in children.iter() { + let score = scores.get_mut(*child).expect("where is it?"); + if score.0 < *threshold { + sum = 0.0; + break; + } else { + sum += score.0; + } + } + let mut score = scores.get_mut(aon_ent).expect("where did it go?"); + score.set(crate::evaluators::clamp(sum, 0.0, 100.0)); + } +} + +mod all_or_nothing { + use super::*; + + use serde::Deserialize; + + #[derive(Debug, Deserialize)] + struct AllOrNothing { + threshold: f32, + scorers: Vec<Box<dyn Scorer>>, + } + + #[typetag::deserialize] + impl Scorer for AllOrNothing { + fn build(&self, actor: Entity, cmd: &mut Commands) -> ScorerEnt { + let ent = ScorerEnt(cmd.spawn().id()); + let scorers: Vec<_> = self + .scorers + .iter() + .map(|scorer| scorer.build(actor, cmd).0) + .collect(); + cmd.entity(ent.0) + .insert(Score::default()) + .insert(super::AllOrNothing { + threshold: self.threshold, + scorers: scorers.into_iter().map(ScorerEnt).collect(), + }); + cmd.entity(actor).push_children(&[ent.0]); + ent + } + } +} + +#[derive(Debug)] +pub struct SumOfScorers { + threshold: f32, + scorers: Vec<ScorerEnt>, +} + +pub fn sum_of_scorers_system(query: Query<(Entity, &SumOfScorers)>, mut scores: Query<&mut Score>) { + for ( + sos_ent, + SumOfScorers { + threshold, + scorers: children, + }, + ) in query.iter() + { + let mut sum = 0.0; + for ScorerEnt(child) in children.iter() { + let score = scores.get_mut(*child).expect("where is it?"); + sum += score.0; + } + if sum < *threshold { + sum = 0.0; + } + let mut score = scores.get_mut(sos_ent).expect("where did it go?"); + score.set(crate::evaluators::clamp(sum, 0.0, 100.0)); + } +} + +mod sum_of_scorers { + use super::*; + + use serde::Deserialize; + + #[derive(Debug, Deserialize)] + struct SumOfScorers { + threshold: f32, + scorers: Vec<Box<dyn Scorer>>, + } + + #[typetag::deserialize] + impl Scorer for SumOfScorers { + fn build(&self, actor: Entity, cmd: &mut Commands) -> ScorerEnt { + let ent = ScorerEnt(cmd.spawn().id()); + let scorers: Vec<_> = self + .scorers + .iter() + .map(|scorer| scorer.build(actor, cmd).0) + .collect(); + cmd.entity(ent.0) + .insert(Score::default()) + .insert(super::AllOrNothing { + threshold: self.threshold, + scorers: scorers.into_iter().map(ScorerEnt).collect(), + }); + cmd.entity(actor).push_children(&[ent.0]); + ent + } + } +}