use crate::{
action::Action,
compare::{check_preconditions, compare_values},
effect::Effect,
goal::Goal,
localstate::LocalState,
mutator::{apply_mutator, print_mutators},
};
use bevy_reflect::Reflect;
#[derive(Reflect, Clone, Eq, PartialEq, Hash)]
pub enum Node {
Effect(Effect),
State(LocalState),
}
impl Node {
pub fn state(&self) -> &LocalState {
match self {
Node::Effect(effect) => &effect.state,
Node::State(state) => state,
}
}
}
impl std::fmt::Debug for Node {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Node::Effect(effect) => effect.fmt(f),
Node::State(state) => state.fmt(f),
}
}
}
fn heuristic(node: &Node, goal: &Goal) -> usize {
let distance = node.state().distance_to_goal(goal) as usize;
distance
}
fn successors<'a>(
node: &'a Node,
actions: &'a [Action],
) -> impl Iterator<Item = (Node, usize)> + 'a {
let state = node.state();
actions.iter().filter_map(move |action| {
if check_preconditions(state, action) && !action.effects.is_empty() {
let new_state = state.clone();
let first_effect = &action.effects[0];
let mut new_data = new_state.data.clone();
for mutator in &first_effect.mutators {
apply_mutator(&mut new_data, mutator);
}
let new_effect = Effect {
action: first_effect.action.clone(),
mutators: first_effect.mutators.clone(),
cost: first_effect.cost,
state: LocalState { data: new_data },
};
Some((Node::Effect(new_effect), first_effect.cost))
} else {
None
}
})
}
fn is_goal(node: &Node, goal: &Goal) -> bool {
goal.requirements.iter().all(|(key, value)| {
if let Some(state_val) = node.state().data.get(key) {
compare_values(value, state_val)
} else {
panic!("Couldn't find key {:#?} in LocalState", key);
}
})
}
pub fn make_plan_with_strategy(
strategy: PlanningStrategy,
start: &LocalState,
actions: &[Action],
goal: &Goal,
) -> Option<(Vec<Node>, usize)> {
match strategy {
PlanningStrategy::StartToGoal => {
let start_node = Node::State(start.clone());
pathfinding::directed::astar::astar(
&start_node,
|node| successors(node, actions).collect::<Vec<_>>().into_iter(),
|node| heuristic(node, goal),
|node| is_goal(node, goal),
)
}
PlanningStrategy::GoalToStart => {
panic!("PlanningStrategy::GoalToStart hasn't been implemented yet!");
}
}
}
#[derive(Default)]
pub enum PlanningStrategy {
#[default]
StartToGoal,
GoalToStart,
}
pub fn make_plan(
start: &LocalState,
actions: &[Action],
goal: &Goal,
) -> Option<(Vec<Node>, usize)> {
make_plan_with_strategy(PlanningStrategy::StartToGoal, start, actions, goal)
}
pub fn get_effects_from_plan(plan: Vec<Node>) -> Vec<Effect> {
let mut nodes = vec![];
for node in plan {
match node {
Node::Effect(c) => nodes.push(c),
Node::State(_s) => {}
}
}
nodes
}
pub fn print_plan(plan: (Vec<Node>, usize)) {
let nodes = plan.0;
let cost = plan.1;
let mut last_state = LocalState::new();
for node in nodes {
match node {
Node::Effect(effect) => {
println!("\t\t= DO ACTION {:#?}", effect.action);
println!("\t\tMUTATES:");
print_mutators(effect.mutators);
last_state = effect.state.clone();
}
Node::State(s) => {
println!("\t\t= INITIAL STATE");
for (k, v) in &s.data {
println!("\t\t{} = {}", k, v);
}
last_state = s.clone();
}
}
println!("\n\t\t---\n");
}
println!("\t\t= FINAL STATE (COST: {})", cost);
for (k, v) in &last_state.data {
println!("\t\t{} = {}", k, v);
}
}