/*
Hurry Curry! - a game about cooking
Copyright (C) 2025 Hurry Curry! Contributors
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, version 3 of the License only.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see .
*/
use hurrycurry_protocol::{Demand, ItemIndex, Recipe, TileIndex, glam::IVec2};
use log::debug;
use std::collections::{HashMap, HashSet};
pub fn filter_demands_and_recipes(
initial_map: &HashMap, Option)>,
demands: &mut Vec,
recipes: &mut Vec,
) {
debug!(
"running demand filter with {} recipes and {} demands",
recipes.len(),
demands.len()
);
let map_tiles = initial_map
.values()
.flat_map(|(t, _)| t)
.copied()
.collect::>();
let map_items = initial_map
.values()
.flat_map(|(_, i)| i)
.copied()
.collect::>();
// Remove tile-bound recipes that cant be performed
recipes.retain(|r| r.tile().is_none_or(|t| map_tiles.contains(&t)));
let mut producable = HashMap::new();
// Items already in the map have no cost
producable.extend(map_items.iter().map(|i| (*i, 0.0)));
loop {
let prod_count = producable.len();
for r in &*recipes {
let output_count = r.outputs().filter(|o| !map_items.contains(o)).count();
let Some(ingred_cost) = r
.inputs()
.map(|i| producable.get(&i).copied())
.reduce(|a, b| {
if let (Some(a), Some(b)) = (a, b) {
Some(a + b)
} else {
None
}
})
.unwrap_or(Some(0.))
else {
continue;
};
let base_cost = match r {
Recipe::Passive { speed, .. } => 2. + (1. / speed) * 0.1,
Recipe::Active { speed, .. } => 2. + (1. / speed),
Recipe::Instant { .. } => 1.,
};
let output_cost = (ingred_cost + base_cost) / output_count as f32;
for o in r.outputs() {
let cost = producable.entry(o).or_insert(f32::INFINITY);
*cost = cost.min(output_cost);
}
}
if prod_count == producable.len() {
break;
}
}
recipes.retain(|r| {
r.inputs().all(|o| producable.contains_key(&o))
&& r.outputs().all(|o| producable.contains_key(&o))
});
demands.retain_mut(|d| {
if let Some(&cost) = producable.get(&d.input) {
d.points = cost as i64;
true
} else {
false
}
});
debug!(
"{} applicable recipes and {} demands selected",
recipes.len(),
demands.len()
)
}