1use std::{
7    hash::{Hash, Hasher},
8    num::NonZeroU64,
9};
10
11use downcast_rs::{impl_downcast, Downcast};
12
13use crate::{impl_task_boxed_methods, AgentId, Domain, StateDiffRef, StateDiffRefMut};
14
15pub type TaskDuration = u64;
17
18pub fn debug_name_to_filename_safe(debug_name: &str) -> String {
20    debug_name
21        .replace(' ', "")
22        .replace('(', "")
23        .replace(')', "")
24        .replace('{', "_")
25        .replace('}', "")
26        .replace(' ', "_")
27        .replace(':', "_")
28        .replace(',', "_")
29}
30
31pub trait Task<D: Domain>: std::fmt::Debug + Downcast + Send + Sync {
36    fn weight(&self, _tick: u64, _state_diff: StateDiffRef<D>, _agent: AgentId) -> f32 {
38        1.0
39    }
40
41    fn duration(&self, _tick: u64, _state_diff: StateDiffRef<D>, _agent: AgentId) -> TaskDuration;
43
44    fn execute(
46        &self,
47        tick: u64,
48        state_diff: StateDiffRefMut<D>,
49        agent: AgentId,
50    ) -> Option<Box<dyn Task<D>>>;
51
52    fn is_valid(&self, tick: u64, state_diff: StateDiffRef<D>, agent: AgentId) -> bool;
54
55    fn display_action(&self) -> D::DisplayAction;
57
58    fn box_clone(&self) -> Box<dyn Task<D>>;
62
63    fn box_hash(&self, state: &mut dyn Hasher);
67
68    #[allow(clippy::borrowed_box)]
74    fn box_eq(&self, other: &Box<dyn Task<D>>) -> bool;
75}
76
77#[derive(Debug, Hash, Clone, PartialEq)]
79pub struct IdleTask;
80
81impl<D: Domain> Task<D> for IdleTask {
82    fn weight(&self, _tick: u64, _state_diff: StateDiffRef<D>, _agent: AgentId) -> f32 {
83        1f32
84    }
85
86    fn duration(&self, _tick: u64, _state_diff: StateDiffRef<D>, _agent: AgentId) -> TaskDuration {
87        1
88    }
89
90    fn execute(
91        &self,
92        _tick: u64,
93        _state_diff: StateDiffRefMut<D>,
94        _agent: AgentId,
95    ) -> Option<Box<dyn Task<D>>> {
96        None
97    }
98
99    fn is_valid(&self, _tick: u64, _state_diff: StateDiffRef<D>, _agent: AgentId) -> bool {
100        true
101    }
102
103    fn display_action(&self) -> D::DisplayAction {
104        D::display_action_task_idle()
105    }
106
107    impl_task_boxed_methods!(D);
108}
109
110#[derive(Clone, Debug, Hash, PartialEq, Eq)]
112pub struct PlanningTask(
113    pub NonZeroU64,
115);
116
117impl<D: Domain> Task<D> for PlanningTask {
118    fn weight(&self, _tick: u64, _state_diff: StateDiffRef<D>, _agent: AgentId) -> f32 {
119        1f32
120    }
121
122    fn duration(&self, _tick: u64, _state_diff: StateDiffRef<D>, _agent: AgentId) -> TaskDuration {
123        self.0.get()
124    }
125
126    fn execute(
127        &self,
128        _tick: u64,
129        _state_diff: StateDiffRefMut<D>,
130        _agent: AgentId,
131    ) -> Option<Box<dyn Task<D>>> {
132        None
133    }
134
135    fn is_valid(&self, _tick: u64, _state_diff: StateDiffRef<D>, _agent: AgentId) -> bool {
136        true
137    }
138
139    fn display_action(&self) -> D::DisplayAction {
140        D::display_action_task_planning()
141    }
142
143    impl_task_boxed_methods!(D);
144}
145
146impl_downcast!(Task<D> where D: Domain);
147
148impl<D: Domain> Clone for Box<dyn Task<D>> {
149    fn clone(&self) -> Self {
150        self.box_clone()
151    }
152}
153
154impl<D: Domain> Hash for Box<dyn Task<D>> {
155    fn hash<H>(&self, state: &mut H)
156    where
157        H: Hasher,
158    {
159        self.box_hash(state);
160    }
161}
162
163impl<D: Domain> PartialEq for Box<dyn Task<D>> {
164    fn eq(&self, other: &Self) -> bool {
165        self.box_eq(other)
166    }
167}
168
169impl<D: Domain> Eq for Box<dyn Task<D>> {}
170
171#[macro_export]
175macro_rules! impl_task_boxed_methods {
176    ($domain: ty) => {
177        fn box_clone(&self) -> Box<dyn Task<$domain>> {
178            Box::new(self.clone())
179        }
180
181        fn box_hash(&self, mut state: &mut dyn std::hash::Hasher) {
182            use std::hash::Hash;
183            self.hash(&mut state)
184        }
185
186        fn box_eq(&self, other: &Box<dyn Task<$domain>>) -> bool {
187            other
188                .downcast_ref::<Self>()
189                .map_or(false, |other| self.eq(other))
190        }
191    };
192}