sandpile/
sandpile.rs

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                // Left corner
75                self.data[0][1] += 1;
76                self.data[1][0] += 1;
77            } else if i == 0 && j == y {
78                // Right corner
79                self.data[0][j-1] += 1;
80                self.data[1][j] += 1;
81            } else if i == x && j == 0 {
82                // Bottom left
83                self.data[i-1][0] += 1;
84                self.data[i][1] += 1;
85            } else if i == x && j == y {
86                // Bottom right
87                self.data[i-1][j] += 1;
88                self.data[i][j-1] += 1;
89            } else if i == 0 {
90                // First row
91                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                // First collumn
96                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                // Last row
101                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                // Last collumn
106                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}