screeps_pathfinding/utils/cache/path_cache_struct.rs
1use std::collections::HashMap;
2use std::hash::Hash;
3
4use serde::{Deserialize, Serialize};
5
6use screeps::Position;
7
8/// A simple cache for path data.
9#[derive(Debug, Clone, Serialize, Deserialize)]
10pub struct PathCache<K>
11where
12 K: PartialEq + Eq + Hash,
13{
14 cache: HashMap<K, Vec<Position>>,
15}
16
17impl<K> Default for PathCache<K>
18where
19 K: PartialEq + Eq + Hash + Clone,
20{
21 fn default() -> Self {
22 Self::new()
23 }
24}
25
26impl<K> PathCache<K>
27where
28 K: PartialEq + Eq + Hash + Clone,
29{
30 /// Initializes a new, empty terrain cache.
31 pub fn new() -> Self {
32 Self {
33 cache: HashMap::new(),
34 }
35 }
36
37 /// Returns the cached path, if it exists. Returns None if the path isn't cached.
38 ///
39 /// # Examples
40 /// ```rust
41 /// use screeps::{Direction, Position, RoomCoordinate};
42 /// use screeps_pathfinding::utils::cache::PathCache;
43 ///
44 /// // Create a new path cache that uses String as the key type
45 /// let mut cache: PathCache<String> = PathCache::new();
46 ///
47 /// // Build a path
48 /// let start = Position::new(
49 /// RoomCoordinate::try_from(24).unwrap(),
50 /// RoomCoordinate::try_from(18).unwrap(),
51 /// "E5N6".parse().unwrap(),
52 /// );
53 /// let goal = start.checked_add_direction(Direction::Right).unwrap();
54 /// let path = vec!(start, goal);
55 ///
56 /// let existing_key = "existing_key".to_string();
57 ///
58 /// // Actually store the path in the cache
59 /// assert_eq!(cache.is_path_cached(&existing_key), false);
60 /// cache.update_cached_path(&existing_key, path.into_iter());
61 /// assert_eq!(cache.is_path_cached(&existing_key), true);
62 ///
63 /// // Pull the cached copy of the path
64 /// let path_opt = cache.get_cached_path(&existing_key);
65 /// assert!(path_opt.is_some());
66 ///
67 /// let path_slice = path_opt.unwrap();
68 /// assert!(path_slice.len() == 2);
69 /// assert!(path_slice[0] == start);
70 /// assert!(path_slice[1] == goal);
71 ///
72 /// // Attempt to pull a non-existent path entry
73 /// let nonexisting_key = "nonexisting_key".to_string();
74 /// let path_opt = cache.get_cached_path(&nonexisting_key);
75 /// assert!(path_opt.is_none());
76 /// assert_eq!(cache.is_path_cached(&nonexisting_key), false);
77 /// ```
78 pub fn get_cached_path(&self, path_key: &K) -> Option<&[Position]> {
79 self.cache.get(path_key).map(|v| &**v)
80 }
81
82 /// Returns whether a path is cached with the provided path key.
83 ///
84 /// # Examples
85 /// ```rust
86 /// use screeps::{Direction, Position, RoomCoordinate};
87 /// use screeps_pathfinding::utils::cache::PathCache;
88 ///
89 /// // Create a new path cache that uses String as the key type
90 /// let mut cache: PathCache<String> = PathCache::new();
91 ///
92 /// // A path doesn't exist yet in the cache for this key
93 /// let key = "some_key".to_string();
94 /// assert_eq!(cache.is_path_cached(&key), false);
95 ///
96 /// // Build a path
97 /// let start = Position::new(
98 /// RoomCoordinate::try_from(24).unwrap(),
99 /// RoomCoordinate::try_from(18).unwrap(),
100 /// "E5N6".parse().unwrap(),
101 /// );
102 /// let goal = start.checked_add_direction(Direction::Right).unwrap();
103 /// let path = vec!(start, goal);
104 ///
105 /// // Store the path in the cache
106 /// cache.update_cached_path(&key, path.into_iter());
107 ///
108 /// assert_eq!(cache.is_path_cached(&key), true);
109 /// ```
110 pub fn is_path_cached(&self, path_key: &K) -> bool {
111 self.cache.contains_key(path_key)
112 }
113
114 /// Returns the path, generating and caching it if it's not already cached.
115 ///
116 /// Returns None only if the path is not cached and the generation function returns None.
117 ///
118 /// # Examples
119 /// ```rust
120 /// use screeps::{Direction, Position, RoomCoordinate};
121 /// use screeps_pathfinding::utils::cache::PathCache;
122 ///
123 /// // Create a new path cache that uses String as the key type
124 /// let mut cache: PathCache<String> = PathCache::new();
125 ///
126 /// // The path doesn't exist yet in the cache
127 /// let key = "some_key".to_string();
128 /// assert_eq!(cache.is_path_cached(&key), false);
129 ///
130 /// let start = Position::new(
131 /// RoomCoordinate::try_from(24).unwrap(),
132 /// RoomCoordinate::try_from(18).unwrap(),
133 /// "E5N6".parse().unwrap(),
134 /// );
135 /// let goal = start.checked_add_direction(Direction::Right).unwrap();
136 ///
137 /// // Pull the path, generating it since it doesn't exist
138 /// let path_opt = cache.get_path(&key, |path_key| {
139 /// // Build a path
140 /// let path = vec!(start, goal);
141 /// Some(path)
142 /// });
143 /// assert!(path_opt.is_some());
144 ///
145 /// let path_slice = path_opt.unwrap();
146 /// assert!(path_slice.len() == 2);
147 /// assert!(path_slice[0] == start);
148 /// assert!(path_slice[1] == goal);
149 ///
150 /// assert_eq!(cache.is_path_cached(&key), true);
151 /// ```
152 pub fn get_path<G>(&mut self, path_key: &K, generator_fn: G) -> Option<&[Position]>
153 where
154 G: FnOnce(&K) -> Option<Vec<Position>>,
155 {
156 if !self.is_path_cached(path_key) {
157 // We don't have a cached copy of the path, generate and cache it
158 let path_opt = generator_fn(path_key);
159 if let Some(path) = path_opt {
160 let _ = self.cache.insert(path_key.clone(), path);
161 } else {
162 return None;
163 }
164 }
165
166 self.get_cached_path(path_key)
167 }
168
169 /// Updates the path cache for a specific key.
170 ///
171 /// This allows for pre-loading the cache with any existing
172 /// path data you might already have available.
173 ///
174 /// # Examples
175 /// ```rust
176 /// use screeps::{Direction, Position, RoomCoordinate};
177 /// use screeps_pathfinding::utils::cache::PathCache;
178 ///
179 /// // Create a new path cache that uses String as the key type
180 /// let mut cache: PathCache<String> = PathCache::new();
181 ///
182 /// // Build a path
183 /// let start = Position::new(
184 /// RoomCoordinate::try_from(24).unwrap(),
185 /// RoomCoordinate::try_from(18).unwrap(),
186 /// "E5N6".parse().unwrap(),
187 /// );
188 /// let goal = start.checked_add_direction(Direction::Right).unwrap();
189 /// let path = vec!(start, goal);
190 ///
191 /// let existing_key = "existing_key".to_string();
192 ///
193 /// // Store the path in the cache
194 /// cache.update_cached_path(&existing_key, path.into_iter());
195 ///
196 /// // Pull the cached copy of the path
197 /// let path_opt = cache.get_cached_path(&existing_key);
198 /// assert!(path_opt.is_some());
199 ///
200 /// let path_slice = path_opt.unwrap();
201 /// assert!(path_slice.len() == 2);
202 /// assert!(path_slice[0] == start);
203 /// assert!(path_slice[1] == goal);
204 /// ```
205 pub fn update_cached_path(&mut self, path_key: &K, path_iter: impl Iterator<Item = Position>) {
206 let path: Vec<Position> = path_iter.collect();
207 let _ = self.cache.insert(path_key.clone(), path);
208 }
209
210 /// Removes the path cached for a specific key.
211 pub fn remove_cached_path(&mut self, path_key: &K) {
212 self.cache.remove(path_key);
213 }
214}