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}