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}