Skip to content
Snippets Groups Projects
scorers.rs 4.62 KiB
Newer Older
use bevy::prelude::*;
use crate::ScorerEnt;
#[derive(Debug, Clone, Default)]
pub struct Score(pub(crate) f32);

impl Score {
Kat Marchán's avatar
Kat Marchán committed
    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");
        }
        self.0 = value;
    }
}
This trait defines new Scorers. In general, you should use the [derive macro](derive.Scorer.html) instead.
Kat Marchán's avatar
Kat Marchán committed
*/
#[typetag::deserialize]
pub trait Scorer: std::fmt::Debug + Sync + Send {
    fn build(&self, entity: Entity, cmd: &mut Commands) -> ScorerEnt;
Kat Marchán's avatar
Kat Marchán committed
}
Kat Marchán's avatar
Kat Marchán committed

#[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
        }
    }
}