use std::collections::HashSet;
use crate::planning::belief::{BeliefId, BeliefSet};
use crate::planning::action::ActionDef;
pub trait TaskResolver<S>: Send + Sync {
fn resolve(
&self,
beliefs: &BeliefSet<S>,
state: &S,
actions: &[ActionDef],
all_tasks: &[Task<S>],
) -> Option<Vec<String>>;
fn debug_name(&self) -> &str;
}
pub enum Task<S> {
Primitive {
name: String,
action_name: String,
},
Compound {
name: String,
methods: Vec<Method>,
},
Dynamic {
name: String,
resolver: Box<dyn TaskResolver<S>>,
},
}
impl<S> std::fmt::Debug for Task<S> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Task::Primitive { name, action_name } => {
f.debug_struct("Primitive").field("name", name).field("action", action_name).finish()
}
Task::Compound { name, methods } => {
f.debug_struct("Compound").field("name", name).field("methods", &methods.len()).finish()
}
Task::Dynamic { name, resolver } => {
f.debug_struct("Dynamic").field("name", name).field("resolver", &resolver.debug_name()).finish()
}
}
}
}
impl<S> Task<S> {
pub fn name(&self) -> &str {
match self {
Task::Primitive { name, .. } => name,
Task::Compound { name, .. } => name,
Task::Dynamic { name, .. } => name,
}
}
pub fn decompose(
&self,
beliefs: &BeliefSet<S>,
state: &S,
actions: &[ActionDef],
all_tasks: &[Task<S>],
) -> Option<Vec<String>> {
match self {
Task::Primitive { action_name, .. } => Some(vec![action_name.clone()]),
Task::Compound { methods, .. } => {
for method in methods {
let conditions_met = method
.conditions
.iter()
.all(|c| beliefs.evaluate(c, state));
if conditions_met {
let mut plan = Vec::new();
let mut all_resolved = true;
for subtask_name in &method.subtasks {
if let Some(subtask) = all_tasks.iter().find(|t| t.name() == subtask_name) {
if let Some(sub_plan) = subtask.decompose(beliefs, state, actions, all_tasks) {
plan.extend(sub_plan);
} else {
all_resolved = false;
break;
}
} else {
plan.push(subtask_name.clone());
}
}
if all_resolved {
return Some(plan);
}
}
}
None
}
Task::Dynamic { resolver, .. } => {
resolver.resolve(beliefs, state, actions, all_tasks)
}
}
}
}
#[derive(Debug)]
pub struct Method {
pub name: String,
pub conditions: HashSet<BeliefId>,
pub subtasks: Vec<String>,
}
pub struct MethodBuilder {
name: String,
conditions: HashSet<BeliefId>,
subtasks: Vec<String>,
}
impl MethodBuilder {
pub fn new(name: impl Into<String>) -> Self {
Self {
name: name.into(),
conditions: HashSet::new(),
subtasks: Vec::new(),
}
}
pub fn condition(mut self, belief: impl Into<BeliefId>) -> Self {
self.conditions.insert(belief.into());
self
}
pub fn subtask(mut self, task_name: impl Into<String>) -> Self {
self.subtasks.push(task_name.into());
self
}
pub fn build(self) -> Method {
Method {
name: self.name,
conditions: self.conditions,
subtasks: self.subtasks,
}
}
}
pub struct TaskBuilder;
impl TaskBuilder {
pub fn primitive(name: impl Into<String>, action_name: impl Into<String>) -> Task<()> {
Task::Primitive {
name: name.into(),
action_name: action_name.into(),
}
}
pub fn compound(name: impl Into<String>, methods: Vec<Method>) -> Task<()> {
Task::Compound {
name: name.into(),
methods,
}
}
pub fn dynamic<S>(name: impl Into<String>, resolver: impl TaskResolver<S> + 'static) -> Task<S> {
Task::Dynamic {
name: name.into(),
resolver: Box::new(resolver),
}
}
}