1use rand::Rng;
2use phf::phf_map;
3use std::fmt;
4
5pub struct Grid {
7 rows: Vec<Vec<u8>>,
9 pipes: &'static PipeMap,
10}
11
12impl Default for Grid {
13 fn default() -> Self {
14 Grid {
15 rows: vec![vec![0; 4]; 4],
16 pipes: &PIPEMAP_THICK,
17 }
18 }
19}
20
21impl Grid {
22 pub fn new(x_size: usize, y_size: usize) -> Grid {
34 if x_size < 1 || y_size < 1 || x_size < 2 && y_size < 2 {
35 panic!("Grid size cannot be 0");
36 }
37 let grid = Grid {
38 rows: vec![vec![0; x_size]; y_size],
39 ..Default::default()
40 };
41 grid.add_random_number().unwrap().add_random_number().unwrap()
43 }
44
45 pub fn from_rows(rows: Vec<Vec<u8>>) -> Grid {
59 Grid {
60 rows,
61 ..Default::default()
62 }
63 }
64
65 pub fn with_pipes(&self, pipes: &'static PipeMap) -> Grid {
83 Grid {
84 rows: self.rows.clone(),
85 pipes,
86 }
87 }
88 pub fn get_size(&self) -> (usize, usize) {
105 (self.rows.len() * self.formatted_numbers()[0][0].len() + self.rows.len() + 1, self.rows[0].len() * 2 + 1)
106 }
107 pub fn slide(&self, dir: Direction) -> Result<Grid, &'static str> {
125 let mut rows: Vec<Vec<u8>> = self.rows.clone();
126
127 (|| {
128 match dir {
129 Direction::LEFT => {
130 rows = rows.iter().map(|row| self.combine_row(row)).collect();
134 return Ok(());
138 }
139
140 Direction::RIGHT => {
141 rows = rows.iter().map(|row| row.iter().rev().cloned().collect()).collect();
143 rows = rows.iter().map(|row| self.combine_row(row)).collect();
145 rows = rows.iter().map(|row| row.iter().rev().cloned().collect()).collect();
147 return Ok(());
149 }
150
151 Direction::UP => {
152 rows = (0..rows[0].len()).map(|col| rows.iter().map(|row| row[col]).collect()).collect();
154 rows = rows.iter().map(|row| self.combine_row(row)).collect();
156 rows = (0..rows[0].len()).map(|col| rows.iter().map(|row| row[col]).collect()).collect();
158 return Ok(());
160 }
161 Direction::DOWN => {
162 rows = (0..rows[0].len()).map(|col| rows.iter().map(|row| row[col]).collect()).collect();
164 rows = rows.iter().map(|row| row.iter().rev().cloned().collect()).collect();
165 rows = rows.iter().map(|row| self.combine_row(row)).collect();
167 rows = rows.iter().map(|row| row.iter().rev().cloned().collect()).collect();
169 rows = (0..rows[0].len()).map(|col| rows.iter().map(|row| row[col]).collect()).collect();
170 return Ok(());
172 }
173 }
174 })()?;
175
176 let new_grid = Grid { rows, ..Default::default() };
177 let new_grid_with_new_number = new_grid.add_random_number()?;
178 if new_grid.rows != self.rows {
180 return Ok(new_grid_with_new_number);
181 }
182 Ok(new_grid)
183 }
184
185 fn compress_row(&self, row: &Vec<u8>) -> Vec<u8> {
186 let mut new_row = row.iter().filter(|&x| *x != 0).cloned().collect::<Vec<u8>>();
187 new_row.append(&mut vec![0; row.len() - new_row.len()]);
188 new_row
189 }
190
191 fn combine_row(&self, row: &Vec<u8>) -> Vec<u8> {
192 let mut row = self.compress_row(&row);
193 for i in 0..(row.len() - 1) {
194 if row[i] == row[i+1] && row[i] != 0 {
195 row[i] += 1;
196 row[i+1] = 0;
197 }
198 }
199 self.compress_row(&row)
200 }
201
202 fn add_random_number(&self) -> Result<Grid, &'static str> {
203 let options: Vec<(usize, usize)> = self.rows.iter().enumerate().flat_map(|(x, row)| {
205 row.iter().enumerate().filter(|(_, &cell)| cell == 0).map(move |(y, _)| (x, y))
206 }).collect();
207
208 if options.is_empty() {
210 return Err("no more options");
211 }
212
213 let mut rng = rand::thread_rng();
214
215 let option = options[rng.gen_range(0..options.len())];
217
218 let mut power = 1;
219 if rng.gen_range(1..10) == 10 {
221 power = 2;
222 }
223
224 let mut new_rows = self.rows.clone();
225 new_rows[option.0][option.1] = power;
226
227 Ok(Grid { rows: new_rows, ..Default::default() })
228 }
229 fn formatted_numbers(&self) -> Vec<Vec<String>> {
230
231 let mut longest_string_len = 2;
232 for number in self.rows.iter().flatten() {
233 if format_number(number, 0).len() > longest_string_len {
234 longest_string_len = format_number(number, 0).len();
235 }
236 }
237
238 return self.rows.iter().map(|row| {
239 row.iter().map(|number| format_number(number, longest_string_len)).collect()
240 }).collect();
241
242 fn format_number(&number: &u8, len: usize) -> String {
243 let digits = format_digits(&number);
244 if len == 0 {
245 return digits;
246 }
247 return (0..len-digits.len()).map(|_| " ".to_string()).collect::<Vec<String>>().join("") + &digits;
248
249 fn format_digits(&number: &u8) -> String {
250 match number {
251 0 => " ".to_string(),
252 x => format!("{}", (2 as usize).pow(x as u32)),
253 }
254 }
255 }
256 }
257}
258
259impl fmt::Debug for Grid {
260 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
261 let mut grid_str = format!("{} by {} Grid:\n[\n", self.rows.len(), self.rows[0].len());
262 for row in &self.rows {
263 grid_str.push_str(" [");
264 for val in row {
265 grid_str.push_str(&format!(" {} ", val));
266 }
267 grid_str.push_str("]\n");
268 }
269 grid_str.push_str("]\n");
270
271
272 Ok(
273 write!(f, "{}", grid_str)?
274 )
275 }
276}
277
278impl fmt::Display for Grid {
279 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
280
281 let pipes = &self.pipes;
283
284 let grid_str = self.formatted_numbers();
285
286 write!(f, "{}", pipes.get("top_left").unwrap())?;
288 for i in 0..grid_str[0].len() {
289 for _ in 0..grid_str[0][0].len() {
290 write!(f, "{}", pipes.get("horizontal").unwrap())?;
291 }
292 if i != grid_str[0].len() - 1 {
293 write!(f, "{}", pipes.get("top_horizontal").unwrap())?;
294 }
295 }
296 write!(f, "{}\n", pipes.get("top_right").unwrap())?;
297
298 for i in 0..grid_str.len() {
300 for col in &grid_str[i] {
301 write!(f, "{}{}", pipes.get("vertical").unwrap(), col)?;
302 }
303 write!(f, "{}\n", pipes.get("vertical").unwrap())?;
304
305 if i == grid_str.len() - 1 {
307 write!(f, "{}", pipes.get("bottom_left").unwrap())?;
309 for i in 0..grid_str[0].len() {
310 for _ in 0..grid_str[0][0].len() {
311 write!(f, "{}", pipes.get("horizontal").unwrap())?;
312 }
313 if i != grid_str[0].len() - 1 {
314 write!(f, "{}", pipes.get("bottom_horizontal").unwrap())?;
315 }
316 }
317 write!(f, "{}\n", pipes.get("bottom_right").unwrap())?;
318 } else {
319 write!(f, "{}", pipes.get("left_vertical").unwrap())?;
321 for i in 0..grid_str[0].len() {
322 for _ in 0..grid_str[0][0].len() {
323 write!(f, "{}", pipes.get("horizontal").unwrap())?;
324 }
325 if i != grid_str[0].len() - 1 {
326 write!(f, "{}", pipes.get("cross").unwrap())?;
327 }
328 }
329 write!(f, "{}\n", pipes.get("right_vertical").unwrap())?;
330 }
331 }
332
333 Ok(())
334 }
335}
336
337 pub enum Direction {
351 LEFT,
352 RIGHT,
353 UP,
354 DOWN,
355}
356
357type PipeMap = phf::Map<&'static str, &'static str>;
358
359 pub static PIPEMAPS: phf::Map<&'static str, &'static PipeMap> = phf_map! {
380 "Thin" => &PIPEMAP_THIN,
381 "Medium" => &PIPEMAP_MEDIUM,
382 "Thick" => &PIPEMAP_THICK,
383};
384
385static PIPEMAP_THIN: PipeMap = phf_map! {
386 "horizontal" => "─",
387 "vertical" => "│",
388 "top_left" => "┌",
389 "top_right" => "┐",
390 "bottom_left" => "└",
391 "bottom_right" => "┘",
392 "top_horizontal" => "┬",
393 "bottom_horizontal" => "┴",
394 "left_vertical" => "├",
395 "right_vertical" => "┤",
396 "cross" => "┼",
397};
398static PIPEMAP_MEDIUM: PipeMap = phf_map! {
399 "horizontal" => "━",
400 "vertical" => "┃",
401 "top_left" => "┏",
402 "top_right" => "┓",
403 "bottom_left" => "┗",
404 "bottom_right" => "┛",
405 "top_horizontal" => "┳",
406 "bottom_horizontal" => "┻",
407 "left_vertical" => "┣",
408 "right_vertical" => "┫",
409 "cross" => "╋",
410};
411static PIPEMAP_THICK: PipeMap = phf_map! {
412 "horizontal" => "═",
413 "vertical" => "║",
414 "top_left" => "╔",
415 "top_right" => "╗",
416 "bottom_left" => "╚",
417 "bottom_right" => "╝",
418 "top_horizontal" => "╦",
419 "bottom_horizontal" => "╩",
420 "left_vertical" => "╠",
421 "right_vertical" => "╣",
422 "cross" => "╬",
423};
424
425
426#[cfg(test)]
428mod tests {
429 use super::*;
430
431 #[test]
432 #[should_panic]
433 fn zero_grid() {
434 let _grid = Grid::new(1, 0);
435 }
436 #[test]
437 fn add_random_number() {
438 let grid = Grid::new(4, 4);
439 let grid = grid.add_random_number().unwrap();
440 println!("{:?}", grid);
441 }
442
443 #[test]
444 fn compress_row() {
445 let grid = Grid::new(4, 4);
446 let row = grid.compress_row(&vec![2, 0, 4, 0]);
447 assert_eq!(row, vec![2, 4, 0, 0]);
448 let row = grid.compress_row(&vec![4, 0, 2, 8]);
449 assert_eq!(row, vec![4, 2, 8, 0]);
450 }
451
452 #[test]
453 fn combine_row() {
454 let grid = Grid::new(4, 4);
455 let row = grid.combine_row(&vec![1, 0, 1, 0]);
456 assert_eq!(row, vec![2, 0, 0, 0]);
457 let row = grid.combine_row(&vec![2, 2, 3, 4, 6, 6, 5, 0, 6]);
458 assert_eq!(row, vec![3, 3, 4, 7, 5, 6, 0, 0, 0]);
459 }
460
461 #[test]
462 fn overwrite_rows() {
463 let grid = Grid::new(2, 2);
464 assert_eq!(grid.rows.len(), 2);
465 let new_rows = vec![vec![1; 4]; 4];
466 let grid = Grid::from_rows(new_rows.clone());
467 assert_eq!(grid.rows, new_rows);
468 }
469}