cellular_snapp/
flat.rs

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