cellular_snapp/
deep.rs

1//! This module exists to help you create 3D (aka flat) cellular automata.
2//! 
3//! To start, you'll want to decide on your rules and create an AutomataRules object containing them.
4//! 
5//! ```
6//! let rules = AutomataRules::new(Rule::Single(4), Rule::Single(4), 5, Method::Moore);
7//! ```
8//! 
9//! 
10
11//--> Imports <--
12
13use crate::{AutomataRules, Method, Rule};
14use std::hash::Hash;
15use std::ops::{Add, Sub};
16use std::default::Default;
17use std::collections::HashMap;
18
19#[cfg(feature = "rayon")]
20use rayon::prelude::*;
21
22//--> Structs <--
23
24/// A position on a 3D grid, or the size of a 3D grid.
25#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Clone)]
26pub struct Vec3 { x: usize, y: usize, z: usize }
27
28/// The humble 3D cellular automaton.
29pub struct Automaton {
30	rules: AutomataRules,
31	bounds: Vec3,
32	cells: HashMap<Vec3, u8>
33}
34
35//--> Functions <--
36
37impl Vec3 {
38	/// Creates a new 3D position.
39	pub fn new(x: usize, y: usize, z: usize) -> Vec3 { Vec3 { x, y, z } }
40}
41
42impl Add for Vec3 {
43	type Output = Vec3;
44	fn add(self, rhs: Vec3) -> Vec3 {
45		Vec3 {
46			x: self.x + rhs.x,
47			y: self.y + rhs.y,
48			z: self.z + rhs.z
49		}
50	}
51}
52
53impl Sub for Vec3 {
54	type Output = Vec3;
55	fn sub(self, rhs: Vec3) -> Vec3 {
56		Vec3 {
57			x: self.x - rhs.x,
58			y: self.y - rhs.y,
59			z: self.z - rhs.z
60		}
61	}
62}
63
64impl Default for Vec3 {
65	fn default() -> Vec3 { Vec3 { x: 0, y: 0, z: 0 } }
66}
67
68impl Automaton {
69	/// Creates a new deep (3D) automaton with the given rules, bounds, and starting cells.
70	/// This can fail if your survival and birth rules exceeds the amount of neighbors a cell could have, given your chosen neighbor counting method.
71	/// If that happens, this function will error out and return the maximum amount of neighbors.
72	pub fn new(rules: AutomataRules, bounds: Vec3, start_cells: Vec<Vec3>) -> Result<Automaton, u8> {
73		let other_rules = rules.clone();
74		let mut a = Automaton { rules, bounds, cells: HashMap::new() };
75
76		let max_neighbors: u8 = match a.rules.neighbor_method {
77			Method::Moore => 26,
78			Method::VonNeumann => 6
79		};
80
81		match other_rules.to_survive {
82			Rule::Single(s) => if s > max_neighbors { return Err(max_neighbors) },
83			Rule::Range(r) => if r.start > max_neighbors || r.end > max_neighbors { return Err(max_neighbors) },
84			Rule::Many(m) => for s in m {
85				if s > max_neighbors { return Err(max_neighbors) }
86			}
87		}
88
89		match other_rules.to_be_born {
90			Rule::Single(s) => if s > max_neighbors { return Err(max_neighbors) },
91			Rule::Range(r) => if r.start > max_neighbors || r.end > max_neighbors { return Err(max_neighbors) },
92			Rule::Many(m) => for s in m {
93				if s > max_neighbors { return Err(max_neighbors) }
94			}
95		}
96
97		for x in 0..a.bounds.x {
98			for y in 0..a.bounds.y {
99				for z in 0..a.bounds.z {
100					let v = Vec3::new(x, y, z);
101
102					if start_cells.contains(&v) {
103						a.cells.insert(v, a.rules.cell_states - 1);
104					} else {
105						a.cells.insert(v, 0);
106					}
107				}
108			}
109		}
110
111		Ok(a)
112	}
113
114	/// Advances the automaton by one time step (or tick).
115	pub fn tick(&mut self) {
116		let neighbor_counts = self.cells.iter().map(|(v, _)| {
117			let mut count = 0;
118			let mut poss_neighbors = Vec::new();
119
120			// primary directions (up, down, left, right, front, back)
121
122			// only modify x-axis
123			poss_neighbors.push(Vec3::new(v.x - 1, v.y, v.z));
124			poss_neighbors.push(Vec3::new(v.x + 1, v.y, v.z));
125
126			// only modify y-axis
127			poss_neighbors.push(Vec3::new(v.x, v.y - 1, v.z));
128			poss_neighbors.push(Vec3::new(v.x, v.y + 1, v.z));
129
130			// only modify z-axis
131			poss_neighbors.push(Vec3::new(v.x, v.y, v.z - 1));
132			poss_neighbors.push(Vec3::new(v.x, v.y, v.z + 1));
133
134			// secondary directions if using Moore
135			if let Method::Moore = self.rules.neighbor_method {
136				// only keep x-axis
137				poss_neighbors.push(Vec3::new(v.x, v.y - 1, v.z - 1));
138				poss_neighbors.push(Vec3::new(v.x, v.y - 1, v.z + 1));
139				poss_neighbors.push(Vec3::new(v.x, v.y + 1, v.z - 1));
140				poss_neighbors.push(Vec3::new(v.x, v.y + 1, v.z + 1));
141
142				// only keep y-axis
143				poss_neighbors.push(Vec3::new(v.x - 1, v.y, v.z - 1));
144				poss_neighbors.push(Vec3::new(v.x - 1, v.y, v.z + 1));
145				poss_neighbors.push(Vec3::new(v.x + 1, v.y, v.z - 1));
146				poss_neighbors.push(Vec3::new(v.x + 1, v.y, v.z + 1));
147
148				// only keep z-axis
149				poss_neighbors.push(Vec3::new(v.x - 1, v.y - 1, v.z));
150				poss_neighbors.push(Vec3::new(v.x - 1, v.y + 1, v.z));
151				poss_neighbors.push(Vec3::new(v.x + 1, v.y - 1, v.z));
152				poss_neighbors.push(Vec3::new(v.x + 1, v.y + 1, v.z));
153
154				// change all axes
155				poss_neighbors.push(Vec3::new(v.x - 1, v.y - 1, v.z - 1));
156				poss_neighbors.push(Vec3::new(v.x - 1, v.y - 1, v.z + 1));
157				poss_neighbors.push(Vec3::new(v.x - 1, v.y + 1, v.z - 1));
158				poss_neighbors.push(Vec3::new(v.x - 1, v.y + 1, v.z + 1));
159				poss_neighbors.push(Vec3::new(v.x + 1, v.y - 1, v.z - 1));
160				poss_neighbors.push(Vec3::new(v.x + 1, v.y - 1, v.z + 1));
161				poss_neighbors.push(Vec3::new(v.x + 1, v.y + 1, v.z - 1));
162				poss_neighbors.push(Vec3::new(v.x + 1, v.y + 1, v.z + 1));
163			}
164
165			for poss_neighbor in poss_neighbors {
166				if let Some(s) = self.cells.get(&poss_neighbor) {
167					if s > &0 {
168						count += 1;
169					}
170				}
171			}
172
173			(v.clone(), count)
174		}).collect::<HashMap<Vec3, u8>>();
175
176		self.cells.iter_mut().for_each(|(v, s)| {
177			if s == &0 {
178				// cell is dead
179				match self.rules.to_be_born {
180					Rule::Single(ref goal) => {
181						if let Some(neighbor_count) = neighbor_counts.get(v) {
182							if neighbor_count == goal {
183								// cell will be born
184								*s = self.rules.cell_states - 1;
185							}
186						}
187					},
188					Rule::Range(ref goal_range) => {
189						if let Some(neighbor_count) = neighbor_counts.get(v) {
190							if goal_range.contains(neighbor_count) {
191								// cell will be born
192								*s = self.rules.cell_states - 1;
193							}
194						}
195					},
196					Rule::Many(ref goals) => {
197						if let Some(neighbor_count) = neighbor_counts.get(v) {
198							if goals.contains(neighbor_count) {
199								// cell will be born
200								*s = self.rules.cell_states - 1;
201							}
202						}
203					}
204				}
205			} else if s == &(self.rules.cell_states - 1) {
206				// cell is alive
207				match self.rules.to_survive {
208					Rule::Single(ref goal) => {
209						if let Some(neighbor_count) = neighbor_counts.get(v) {
210							if neighbor_count != goal {
211								// cell will start dying now
212								*s -= 1;
213							}
214						} else {
215							// cell should not exist
216							*s = 0;
217						}
218					},
219					Rule::Range(ref goal_range) => {
220						if let Some(neighbor_count) = neighbor_counts.get(v) {
221							if !goal_range.contains(neighbor_count) {
222								// cell will start dying now
223								*s -= 1;
224							}
225						} else {
226							// cell should not exist
227							*s = 0;
228						}
229					},
230					Rule::Many(ref goals) => {
231						if let Some(neighbor_count) = neighbor_counts.get(v) {
232							if !goals.contains(neighbor_count) {
233								// cell will start dying now
234								*s -= 1;
235							}
236						} else {
237							// cell should not exist
238							*s = 0;
239						}
240					}
241				}
242			} else {
243				// cell is dying
244				*s -= 1;
245			}
246		});
247	}
248
249	/// Advances the automaton by one time step (or tick), but using multiple threads.
250	#[cfg(feature = "rayon")]
251	pub fn par_tick(&mut self) {
252		let neighbor_counts = self.cells.par_iter().map(|(v, _)| {
253			let mut count = 0;
254			let mut poss_neighbors = Vec::new();
255
256			// primary directions (up, down, left, right, front, back)
257
258			// only modify x-axis
259			poss_neighbors.push(Vec3::new(v.x - 1, v.y, v.z));
260			poss_neighbors.push(Vec3::new(v.x + 1, v.y, v.z));
261
262			// only modify y-axis
263			poss_neighbors.push(Vec3::new(v.x, v.y - 1, v.z));
264			poss_neighbors.push(Vec3::new(v.x, v.y + 1, v.z));
265
266			// only modify z-axis
267			poss_neighbors.push(Vec3::new(v.x, v.y, v.z - 1));
268			poss_neighbors.push(Vec3::new(v.x, v.y, v.z + 1));
269
270			// secondary directions if using Moore
271			if let Method::Moore = self.rules.neighbor_method {
272				// only keep x-axis
273				poss_neighbors.push(Vec3::new(v.x, v.y - 1, v.z - 1));
274				poss_neighbors.push(Vec3::new(v.x, v.y - 1, v.z + 1));
275				poss_neighbors.push(Vec3::new(v.x, v.y + 1, v.z - 1));
276				poss_neighbors.push(Vec3::new(v.x, v.y + 1, v.z + 1));
277
278				// only keep y-axis
279				poss_neighbors.push(Vec3::new(v.x - 1, v.y, v.z - 1));
280				poss_neighbors.push(Vec3::new(v.x - 1, v.y, v.z + 1));
281				poss_neighbors.push(Vec3::new(v.x + 1, v.y, v.z - 1));
282				poss_neighbors.push(Vec3::new(v.x + 1, v.y, v.z + 1));
283
284				// only keep z-axis
285				poss_neighbors.push(Vec3::new(v.x - 1, v.y - 1, v.z));
286				poss_neighbors.push(Vec3::new(v.x - 1, v.y + 1, v.z));
287				poss_neighbors.push(Vec3::new(v.x + 1, v.y - 1, v.z));
288				poss_neighbors.push(Vec3::new(v.x + 1, v.y + 1, v.z));
289
290				// change all axes
291				poss_neighbors.push(Vec3::new(v.x - 1, v.y - 1, v.z - 1));
292				poss_neighbors.push(Vec3::new(v.x - 1, v.y - 1, v.z + 1));
293				poss_neighbors.push(Vec3::new(v.x - 1, v.y + 1, v.z - 1));
294				poss_neighbors.push(Vec3::new(v.x - 1, v.y + 1, v.z + 1));
295				poss_neighbors.push(Vec3::new(v.x + 1, v.y - 1, v.z - 1));
296				poss_neighbors.push(Vec3::new(v.x + 1, v.y - 1, v.z + 1));
297				poss_neighbors.push(Vec3::new(v.x + 1, v.y + 1, v.z - 1));
298				poss_neighbors.push(Vec3::new(v.x + 1, v.y + 1, v.z + 1));
299			}
300
301			for poss_neighbor in poss_neighbors {
302				if let Some(s) = self.cells.get(&poss_neighbor) {
303					if s > &0 {
304						count += 1;
305					}
306				}
307			}
308
309			(v.clone(), count)
310		}).collect::<HashMap<Vec3, u8>>();
311
312		self.cells.par_iter_mut().for_each(|(v, s)| {
313			if s == &0 {
314				// cell is dead
315				match self.rules.to_be_born {
316					Rule::Single(ref goal) => {
317						if let Some(neighbor_count) = neighbor_counts.get(v) {
318							if neighbor_count == goal {
319								// cell will be born
320								*s = self.rules.cell_states - 1;
321							}
322						}
323					},
324					Rule::Range(ref goal_range) => {
325						if let Some(neighbor_count) = neighbor_counts.get(v) {
326							if goal_range.contains(neighbor_count) {
327								// cell will be born
328								*s = self.rules.cell_states - 1;
329							}
330						}
331					},
332					Rule::Many(ref goals) => {
333						if let Some(neighbor_count) = neighbor_counts.get(v) {
334							if goals.contains(neighbor_count) {
335								// cell will be born
336								*s = self.rules.cell_states - 1;
337							}
338						}
339					}
340				}
341			} else if s == &(self.rules.cell_states - 1) {
342				// cell is alive
343				match self.rules.to_survive {
344					Rule::Single(ref goal) => {
345						if let Some(neighbor_count) = neighbor_counts.get(v) {
346							if neighbor_count != goal {
347								// cell will start dying now
348								*s -= 1;
349							}
350						} else {
351							// cell should not exist
352							*s = 0;
353						}
354					},
355					Rule::Range(ref goal_range) => {
356						if let Some(neighbor_count) = neighbor_counts.get(v) {
357							if !goal_range.contains(neighbor_count) {
358								// cell will start dying now
359								*s -= 1;
360							}
361						} else {
362							// cell should not exist
363							*s = 0;
364						}
365					},
366					Rule::Many(ref goals) => {
367						if let Some(neighbor_count) = neighbor_counts.get(v) {
368							if !goals.contains(neighbor_count) {
369								// cell will start dying now
370								*s -= 1;
371							}
372						} else {
373							// cell should not exist
374							*s = 0;
375						}
376					}
377				}
378			} else {
379				// cell is dying
380				*s -= 1;
381			}
382		});
383	}
384
385	/// Get a copy of the automaton's internal state (the cells).
386	pub fn get_cells(&self) -> HashMap<Vec3, u8> {
387		self.cells.clone()
388	}
389}