1use std::ops::Add;
2use std::fmt;
3
4#[derive(PartialEq, Debug, Clone)]
5pub struct Sandpile {
6 cols: i32,
7 rows: i32,
8 data: Vec<Vec<i32>>,
9}
10
11impl Sandpile {
12 pub fn new(cols: i32, rows: i32, data: Option<Vec<i32>>) -> Sandpile {
13 match data {
14 None => {
15 let temp_data: Vec<Vec<i32>> = vec![vec![0; cols as usize]; rows as usize];
16 Sandpile {
17 rows: rows,
18 cols: cols,
19 data: temp_data
20 }
21 },
22 Some(x) => {
23 if x.len() != (cols*rows) as usize {
24 panic!("Data does not match Sandpile dimensions.");
25 } else {
26 let mut temp_data: Vec<Vec<i32>> = vec![vec![0; cols as usize]; rows as usize];
27 let mut counter: usize = 0;
28 for i in 0..rows as usize {
29 for j in 0..cols as usize {
30 temp_data[i][j] = x[counter];
31 counter += 1;
32 }
33 }
34
35 let mut ret = Sandpile {
36 rows: rows,
37 cols: cols,
38 data: temp_data
39 };
40
41 ret = ret.stabilize();
42
43 ret
44 }
45 }
46 }
47 }
48
49 pub fn stabilize(mut self) -> Self {
50 let mut job_queue = self.get_job_queue();
51
52 while !job_queue.is_empty() {
53 self = self.topple_pass(job_queue);
54 job_queue = self.get_job_queue();
55 }
56 self
57 }
58
59 fn is_add_compatible(&self, other: &Self) -> bool {
60 if self.rows == other.rows && self.cols == other.cols {
61 true
62 } else {
63 false
64 }
65 }
66
67 fn topple_pass(mut self, job_queue: Vec<(usize, usize)>) -> Self {
68 let y: usize = (self.cols - 1) as usize;
69 let x: usize = (self.rows - 1) as usize;
70
71 for (i, j) in job_queue {
72 self.data[i][j] = self.data[i][j] - 4;
73 if i == 0 && j == 0 {
74 self.data[0][1] += 1;
76 self.data[1][0] += 1;
77 } else if i == 0 && j == y {
78 self.data[0][j-1] += 1;
80 self.data[1][j] += 1;
81 } else if i == x && j == 0 {
82 self.data[i-1][0] += 1;
84 self.data[i][1] += 1;
85 } else if i == x && j == y {
86 self.data[i-1][j] += 1;
88 self.data[i][j-1] += 1;
89 } else if i == 0 {
90 self.data[0][j-1] += 1;
92 self.data[0][j+1] += 1;
93 self.data[1][j] += 1;
94 } else if j == 0 {
95 self.data[i-1][0] += 1;
97 self.data[i+1][0] += 1;
98 self.data[i][1] += 1;
99 } else if i == x {
100 self.data[i][j-1] += 1;
102 self.data[i][j+1] += 1;
103 self.data[i-1][j] += 1;
104 } else if j == y {
105 self.data[i-1][j] += 1;
107 self.data[i+1][j] += 1;
108 self.data[i][j-1] += 1;
109 } else {
110 self.data[i-1][j] += 1;
111 self.data[i+1][j] += 1;
112 self.data[i][j-1] += 1;
113 self.data[i][j+1] += 1;
114 }
115 }
116
117 self
118 }
119
120 fn get_job_queue(&self) -> Vec<(usize, usize)> {
121 let mut job_queue: Vec<(usize, usize)> = Vec::new();
122
123 for i in 0..self.rows as usize {
124 for j in 0..self.cols as usize {
125 if self.data[i][j] > 3 {
126 job_queue.push((i,j));
127 }
128 }
129 }
130
131 job_queue
132 }
133
134}
135
136impl Add for Sandpile {
137 type Output = Sandpile;
138
139 fn add(self, other: Sandpile) -> Sandpile {
140 if self.is_add_compatible(&other) {
141 let mut a = Sandpile::new(self.rows, self.cols, None);
142
143 for i in 0..self.rows as usize {
144 for j in 0..self.cols as usize {
145 a.data[i][j] = self.data[i][j] + other.data[i][j];
146 }
147 }
148
149 a.stabilize()
150 } else {
151 panic!("The two sandpiles are not compatible for addition!");
152 }
153 }
154}
155
156impl fmt::Display for Sandpile {
157 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
158 for i in 0..self.rows as usize {
159 for j in 0..self.cols as usize {
160 write!(f, "{} | ", self.data[i][j]).unwrap();
161 }
162 write!(f, "\n").unwrap();
163 }
164 write!(f, "")
165 }
166}
167
168#[cfg(test)]
169mod tests {
170 use super::Sandpile;
171
172 #[test]
173 fn test_equality() {
174 let a = Sandpile::new(3, 3, None);
175 let b = Sandpile::new(3, 3, None);
176
177 assert_eq!(a, b)
178 }
179
180 #[test]
181 fn test_data_initialization() {
182 let a = Sandpile {
183 rows: 3,
184 cols: 3,
185 data: vec![vec![1,1,1], vec![1,2,1], vec![2,2,2]]
186 };
187
188 let b = Sandpile::new(3, 3, Some(vec![1,1,1,1,2,1,2,2,2]));
189
190 assert_eq!(a,b)
191 }
192
193 #[test]
194 fn stabilization_test_1() {
195 let a = Sandpile {
196 rows: 3,
197 cols: 3,
198 data: vec![vec![4,4,4], vec![0,0,0], vec![1,1,1]]
199 };
200
201 let b = Sandpile {
202 rows: 3,
203 cols: 3,
204 data: vec![vec![1,2,1], vec![1,1,1], vec![1,1,1]]
205 };
206
207 assert_eq!(a.stabilize(), b);
208 }
209
210 #[test]
211 fn stabilization_test_2() {
212 let a = Sandpile {
213 rows: 3,
214 cols: 3,
215 data: vec![vec![4,4,4], vec![4,4,4], vec![2,2,2]]
216 };
217
218 let b = Sandpile {
219 rows: 3,
220 cols: 3,
221 data: vec![vec![2,3,2], vec![2,3,2], vec![3,3,3]]
222 };
223
224 assert_eq!(a.stabilize(), b);
225 }
226
227 #[test]
228 fn addition_test() {
229 let a = Sandpile::new(3, 3, Some(vec![2, 1, 2, 1, 0, 1, 2, 1, 2]));
230 let b = Sandpile::new(3, 3, Some(vec![2, 1, 2, 1, 0, 1, 2, 1, 2]));
231
232 let d = a.clone();
233
234 let c = a + b;
235
236 assert_eq!(c, d)
237 }
238}