feagi_brain_development/connectivity/rules/
patterns.rs1use crate::types::Position;
9
10type Dimensions = (usize, usize, usize);
11
12#[derive(Debug, Clone, PartialEq)]
14pub enum PatternElement {
15 Wildcard, Skip, Exclude, Exact(i32), }
20
21impl PatternElement {
22 pub fn from_value(value: &str) -> Self {
23 match value {
24 "*" => PatternElement::Wildcard,
25 "?" => PatternElement::Skip,
26 "!" => PatternElement::Exclude,
27 _ => {
28 if let Ok(num) = value.parse::<i32>() {
29 PatternElement::Exact(num)
30 } else {
31 PatternElement::Wildcard }
33 }
34 }
35 }
36
37 pub fn from_int(value: i32) -> Self {
38 if value == -1 {
39 PatternElement::Wildcard
40 } else if value == -2 {
41 PatternElement::Skip
42 } else if value == -3 {
43 PatternElement::Exclude
44 } else {
45 PatternElement::Exact(value)
46 }
47 }
48}
49
50pub type Pattern3D = (PatternElement, PatternElement, PatternElement);
52
53pub fn match_pattern_element(element: &PatternElement, coordinate: i32, src_coord: i32) -> bool {
55 match element {
56 PatternElement::Wildcard => true,
57 PatternElement::Skip => coordinate == src_coord,
58 PatternElement::Exclude => coordinate != src_coord,
59 PatternElement::Exact(val) => coordinate == *val,
60 }
61}
62
63pub fn find_destination_coordinates(
65 dst_dimensions: Dimensions,
66 src_coordinate: Position,
67 _src_pattern: &Pattern3D,
68 dst_pattern: &Pattern3D,
69) -> Vec<Position> {
70 let mut results = Vec::new();
71
72 let (dst_width, dst_height, dst_depth) = dst_dimensions;
73 let (src_x, src_y, src_z) = src_coordinate;
74
75 let x_range: Vec<u32> = match &dst_pattern.0 {
77 PatternElement::Wildcard => (0..dst_width as u32).collect(),
78 PatternElement::Skip => {
79 if (src_x as usize) < dst_width {
80 vec![src_x]
81 } else {
82 vec![]
83 }
84 }
85 PatternElement::Exclude => (0..dst_width as u32).filter(|&x| x != src_x).collect(),
86 PatternElement::Exact(val) => {
87 if *val >= 0 && (*val as usize) < dst_width {
88 vec![*val as u32]
89 } else {
90 vec![]
91 }
92 }
93 };
94
95 let y_range: Vec<u32> = match &dst_pattern.1 {
96 PatternElement::Wildcard => (0..dst_height as u32).collect(),
97 PatternElement::Skip => {
98 if (src_y as usize) < dst_height {
99 vec![src_y]
100 } else {
101 vec![]
102 }
103 }
104 PatternElement::Exclude => (0..dst_height as u32).filter(|&y| y != src_y).collect(),
105 PatternElement::Exact(val) => {
106 if *val >= 0 && (*val as usize) < dst_height {
107 vec![*val as u32]
108 } else {
109 vec![]
110 }
111 }
112 };
113
114 let z_range: Vec<u32> = match &dst_pattern.2 {
115 PatternElement::Wildcard => (0..dst_depth as u32).collect(),
116 PatternElement::Skip => {
117 if (src_z as usize) < dst_depth {
118 vec![src_z]
119 } else {
120 vec![]
121 }
122 }
123 PatternElement::Exclude => (0..dst_depth as u32).filter(|&z| z != src_z).collect(),
124 PatternElement::Exact(val) => {
125 if *val >= 0 && (*val as usize) < dst_depth {
126 vec![*val as u32]
127 } else {
128 vec![]
129 }
130 }
131 };
132
133 for x in &x_range {
135 for y in &y_range {
136 for z in &z_range {
137 results.push((*x, *y, *z));
138 }
139 }
140 }
141
142 results
143}
144
145pub fn find_source_coordinates(
147 src_pattern: &Pattern3D,
148 src_dimensions: Dimensions,
149) -> Vec<Position> {
150 let mut results = Vec::new();
151
152 let (src_width, src_height, src_depth) = src_dimensions;
153
154 let x_range: Vec<u32> = match &src_pattern.0 {
156 PatternElement::Wildcard => (0..src_width as u32).collect(),
157 PatternElement::Exact(val) => {
158 if *val >= 0 && (*val as usize) < src_width {
159 vec![*val as u32]
160 } else {
161 vec![]
162 }
163 }
164 _ => (0..src_width as u32).collect(), };
166
167 let y_range: Vec<u32> = match &src_pattern.1 {
168 PatternElement::Wildcard => (0..src_height as u32).collect(),
169 PatternElement::Exact(val) => {
170 if *val >= 0 && (*val as usize) < src_height {
171 vec![*val as u32]
172 } else {
173 vec![]
174 }
175 }
176 _ => (0..src_height as u32).collect(),
177 };
178
179 let z_range: Vec<u32> = match &src_pattern.2 {
180 PatternElement::Wildcard => (0..src_depth as u32).collect(),
181 PatternElement::Exact(val) => {
182 if *val >= 0 && (*val as usize) < src_depth {
183 vec![*val as u32]
184 } else {
185 vec![]
186 }
187 }
188 _ => (0..src_depth as u32).collect(),
189 };
190
191 for x in &x_range {
193 for y in &y_range {
194 for z in &z_range {
195 results.push((*x, *y, *z));
196 }
197 }
198 }
199
200 results
201}
202
203pub fn match_patterns_batch(
205 src_coordinate: Position,
206 patterns: &[(Pattern3D, Pattern3D)], _src_dimensions: Dimensions,
208 dst_dimensions: Dimensions,
209) -> Vec<Position> {
210 let mut all_results = Vec::new();
211
212 for (src_pattern, dst_pattern) in patterns {
213 let (src_x, src_y, src_z) = src_coordinate;
215
216 let x_match = match &src_pattern.0 {
217 PatternElement::Wildcard => true,
218 PatternElement::Exact(val) => src_x == (*val as u32),
219 _ => true, };
221
222 let y_match = match &src_pattern.1 {
223 PatternElement::Wildcard => true,
224 PatternElement::Exact(val) => src_y == (*val as u32),
225 _ => true,
226 };
227
228 let z_match = match &src_pattern.2 {
229 PatternElement::Wildcard => true,
230 PatternElement::Exact(val) => src_z == (*val as u32),
231 _ => true,
232 };
233
234 if x_match && y_match && z_match {
235 let mut results = find_destination_coordinates(
236 dst_dimensions,
237 src_coordinate,
238 src_pattern,
239 dst_pattern,
240 );
241 all_results.append(&mut results);
242 }
243 }
244
245 all_results.sort_unstable();
247 all_results.dedup();
248
249 all_results
250}
251
252#[cfg(test)]
253mod tests {
254 use super::*;
255
256 #[test]
257 fn test_wildcard_pattern() {
258 let src_pattern = (
259 PatternElement::Wildcard,
260 PatternElement::Wildcard,
261 PatternElement::Exact(0),
262 );
263 let dst_pattern = (
264 PatternElement::Skip,
265 PatternElement::Skip,
266 PatternElement::Exact(1),
267 );
268
269 let results =
270 find_destination_coordinates((10, 10, 10), (5, 5, 0), &src_pattern, &dst_pattern);
271
272 assert_eq!(results.len(), 1);
273 assert_eq!(results[0], (5, 5, 1)); }
275
276 #[test]
277 fn test_exact_pattern() {
278 let src_pattern = (
279 PatternElement::Exact(0),
280 PatternElement::Exact(0),
281 PatternElement::Exact(0),
282 );
283 let dst_pattern = (
284 PatternElement::Exact(1),
285 PatternElement::Exact(2),
286 PatternElement::Exact(3),
287 );
288
289 let results =
290 find_destination_coordinates((10, 10, 10), (0, 0, 0), &src_pattern, &dst_pattern);
291
292 assert_eq!(results.len(), 1);
293 assert_eq!(results[0], (1, 2, 3));
294 }
295
296 #[test]
297 fn test_exclude_pattern() {
298 let src_pattern = (
299 PatternElement::Wildcard,
300 PatternElement::Wildcard,
301 PatternElement::Wildcard,
302 );
303 let dst_pattern = (
304 PatternElement::Exclude,
305 PatternElement::Exact(0),
306 PatternElement::Exact(0),
307 );
308
309 let results =
310 find_destination_coordinates((3, 1, 1), (1, 0, 0), &src_pattern, &dst_pattern);
311
312 assert_eq!(results.len(), 2);
314 assert!(results.contains(&(0, 0, 0)));
315 assert!(results.contains(&(2, 0, 0)));
316 }
317}