use bevy::log::LogPlugin;
use bevy::prelude::*;
use bevy::utils::tracing::debug;
use big_brain::prelude::*;
use big_brain::scorers::MeasuredScorer;
#[derive(Debug, Clone)]
pub struct SumWithDecreasingWeightMeasure;
impl Measure for SumWithDecreasingWeightMeasure {
fn calculate(&self, scores: Vec<(&Score, f32)>) -> f32 {
scores
.iter()
.enumerate()
.fold(0f32, |acc, (idx, (score, weight))| {
acc + score.get() * weight / (1.0 + idx as f32)
})
}
}
#[derive(Component, Debug)]
pub struct Pancakes(pub f32);
#[derive(Component, Debug)]
pub struct Waffles(pub f32);
pub fn eat_dessert(time: Res<Time>, mut pancakes: Query<(&mut Pancakes, &mut Waffles)>) {
let delta_t = time.delta_secs();
for (mut pancake, mut waffle) in pancakes.iter_mut() {
pancake.0 = (pancake.0 - delta_t).max(0.0);
waffle.0 = (waffle.0 - delta_t).max(0.0);
info!("Pancake: {}, waffle: {}", pancake.0, waffle.0);
}
}
pub trait EatFood {
fn get(&self) -> f32;
fn eat(&mut self, amount: f32);
}
impl EatFood for Pancakes {
fn get(&self) -> f32 {
self.0
}
fn eat(&mut self, amount: f32) {
self.0 = (self.0 + amount).clamp(0.0, 100.0)
}
}
impl EatFood for Waffles {
fn get(&self) -> f32 {
self.0
}
fn eat(&mut self, amount: f32) {
self.0 = (self.0 + amount).clamp(0.0, 100.0)
}
}
#[derive(Clone, Component, Debug, ActionBuilder)]
pub struct EatPancakes;
#[derive(Clone, Component, Debug, ActionBuilder)]
pub struct EatWaffles;
fn eat_thing_action<
TActionMarker: std::fmt::Debug + Component,
TActorMarker: Component + EatFood,
>(
time: Res<Time>,
mut items: Query<&mut TActorMarker>,
mut query: Query<(&Actor, &mut ActionState, &TActionMarker, &ActionSpan)>,
) {
for (Actor(actor), mut state, action_marker, span) in query.iter_mut() {
let _guard = span.span().enter();
if let Ok(mut item) = items.get_mut(*actor) {
match *state {
ActionState::Requested => {
info!("Time to {:?}", action_marker);
*state = ActionState::Executing;
}
ActionState::Executing => {
debug!("You should {:?}", action_marker);
item.eat(time.delta_secs() * 5.0);
if item.get() > 80.0 {
info!("You shouldn't {:?}", action_marker);
*state = ActionState::Success;
}
}
ActionState::Cancelled => {
info!(
"Cancelled eating {:?}. Considering this a failure.",
action_marker
);
*state = ActionState::Failure;
}
_ => {}
}
}
}
}
#[derive(Clone, Component, Debug, ScorerBuilder)]
pub struct CravingPancakes;
#[derive(Clone, Component, Debug, ScorerBuilder)]
pub struct CravingWaffles;
pub fn craving_food_scorer<
TScoreMarker: std::fmt::Debug + Component,
TActorMarker: Component + EatFood,
>(
items: Query<&TActorMarker>,
mut query: Query<(&Actor, &mut Score), With<TScoreMarker>>,
) {
for (Actor(actor), mut score) in &mut query {
if let Ok(item) = items.get(*actor) {
let current_food = item.get();
if current_food >= 50.0 {
score.set(0.0);
} else {
score.set((1.0 - current_food / 50.0).clamp(0.0, 1.0));
}
}
}
}
pub fn init_entities(mut cmd: Commands) {
cmd.spawn((
Pancakes(50.0),
Waffles(50.0),
Thinker::build()
.label("Hungry Thinker")
.picker(FirstToScore::new(0.5))
.when(
MeasuredScorer::build(0.1)
.label("eat some waffles")
.measure(SumWithDecreasingWeightMeasure)
.push(CravingWaffles, 1.0)
.push(CravingPancakes, 1.0),
EatWaffles,
)
.when(
MeasuredScorer::build(0.1)
.label("eat some pancakes")
.push(CravingPancakes, 1.0)
.push(CravingWaffles, 1.0),
EatPancakes,
),
));
}
fn main() {
App::new()
.add_plugins(MinimalPlugins)
.add_plugins(LogPlugin {
filter: "big_brain=debug,custom_measure=debug".to_string(),
..default()
})
.add_plugins(BigBrainPlugin::new(PreUpdate))
.add_systems(Startup, init_entities)
.add_systems(Update, eat_dessert)
.add_systems(
PreUpdate,
(
eat_thing_action::<EatPancakes, Pancakes>,
eat_thing_action::<EatWaffles, Waffles>,
)
.in_set(BigBrainSet::Actions),
)
.add_systems(
PreUpdate,
(
craving_food_scorer::<CravingPancakes, Pancakes>,
craving_food_scorer::<CravingWaffles, Waffles>,
)
.in_set(BigBrainSet::Scorers),
)
.run();
}