Advent of Code solutions
at main 1075 lines 32 kB view raw
1use cursors::GridCursor; 2 3use crate::{ 4 dir::{Direction, Movement, CARDINALS}, 5 pos::Position, 6}; 7 8#[derive(Clone)] 9/// A 2D integer grid of values. 10/// 11/// This grid is represented by a vector of vectors. 12/// 13/// # Examples 14/// 15/// ``` 16/// use utils::prelude::*; 17/// 18/// let data = vec![ 19/// vec![1, 2, 3], 20/// vec![4, 5, 6], 21/// vec![7, 8, 9], 22/// ]; 23/// 24/// let grid = Grid::new(data); 25/// 26/// assert_eq!(grid.get(Position::new(0, 0)), Some(&1)); 27/// assert_eq!(grid.get(Position::new(1, 1)), Some(&5)); 28/// assert_eq!(grid.get(Position::new(2, 2)), Some(&9)); 29/// ``` 30/// 31pub struct Grid<T> { 32 data: Vec<Vec<T>>, 33} 34 35impl<T> Grid<T> { 36 /// Create a new grid from a vector of vectors. 37 pub fn new(data: Vec<Vec<T>>) -> Self { 38 Self { data } 39 } 40 41 /// Parse a grid from a string, this will convert each character into `T` via `From<char>`. 42 /// 43 /// Use the `tiles!` macro to easily create an enum that implements `From<char>`. 44 /// 45 /// # Examples 46 /// 47 /// ``` 48 /// use utils::prelude::*; 49 /// 50 /// #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 51 /// enum Tile { 52 /// Floor, 53 /// Wall, 54 /// } 55 /// 56 /// impl From<char> for Tile { 57 /// fn from(c: char) -> Self { 58 /// match c { 59 /// '.' => Self::Floor, 60 /// '#' => Self::Wall, 61 /// _ => panic!("Invalid tile {c}"), 62 /// } 63 /// } 64 /// } 65 /// 66 /// let input = ".#.\n#.#\n.#."; 67 /// let grid = Grid::<Tile>::parse(input); 68 /// 69 /// assert_eq!(grid.get(Position::new(0, 0)), Some(&Tile::Floor)); 70 /// assert_eq!(grid.get(Position::new(1, 0)), Some(&Tile::Wall)); 71 /// assert_eq!(grid.get(Position::new(2, 0)), Some(&Tile::Floor)); 72 /// assert_eq!(grid.get(Position::new(1, 1)), Some(&Tile::Floor)); 73 /// ``` 74 /// 75 /// Using `tiles!`... 76 /// 77 /// ``` 78 /// use utils::prelude::*; 79 /// use utils::tiles; 80 /// 81 /// tiles!(Tile, [ 82 /// '.' => Floor, 83 /// '#' => Wall, 84 /// ]); 85 /// 86 /// let input = ".#.\n#.#\n.#."; 87 /// let grid = Grid::<Tile>::parse(input); 88 /// 89 /// assert_eq!(grid.get(Position::new(0, 0)), Some(&Tile::Floor)); 90 /// assert_eq!(grid.get(Position::new(1, 0)), Some(&Tile::Wall)); 91 /// assert_eq!(grid.get(Position::new(2, 0)), Some(&Tile::Floor)); 92 /// assert_eq!(grid.get(Position::new(1, 1)), Some(&Tile::Floor)); 93 /// ``` 94 /// 95 pub fn parse(input: &str) -> Self 96 where 97 T: From<char>, 98 { 99 let data = input 100 .lines() 101 .map(|line| line.chars().map(|c| c.into()).collect()) 102 .collect(); 103 Self::new(data) 104 } 105 106 /// Return the width of the grid. 107 pub fn width(&self) -> usize { 108 self.data[0].len() 109 } 110 111 /// Return the height of the grid. 112 pub fn height(&self) -> usize { 113 self.data.len() 114 } 115 116 /// Get the size of the grid. 117 pub fn size(&self) -> (usize, usize) { 118 (self.width(), self.height()) 119 } 120 121 /// Get the bounds of the grid. 122 /// 123 /// (This is the same as `self.size()` with -1 added to each component) 124 pub fn bounds(&self) -> (usize, usize) { 125 (self.width() - 1, self.height() - 1) 126 } 127 128 /// Get if a given position is in this grid's bounds 129 pub fn in_bounds(&self, pos: &Position) -> bool { 130 pos.x >= 0 && pos.y >= 0 && pos.x < self.width() as isize && pos.y < self.height() as isize 131 } 132 133 /// Get a value from the grid at the given position. 134 /// 135 /// # Examples 136 /// 137 /// ``` 138 /// use utils::prelude::*; 139 /// 140 /// let data = vec![ 141 /// vec![1, 2, 3], 142 /// vec![4, 5, 6], 143 /// vec![7, 8, 9], 144 /// ]; 145 /// 146 /// let grid = Grid::new(data); 147 /// assert_eq!(grid.get(Position::new(0, 0)), Some(&1)); 148 /// assert_eq!(grid.get(Position::new(1, 1)), Some(&5)); 149 /// assert_eq!(grid.get(Position::new(2, 2)), Some(&9)); 150 /// assert_eq!(grid.get(Position::new(3, 3)), None); 151 /// ``` 152 /// 153 pub fn get(&self, pos: Position) -> Option<&T> { 154 self.data 155 .get(pos.y as usize) 156 .and_then(|row| row.get(pos.x as usize)) 157 } 158 159 /// Obtain a mutable reference to a tile in the grid 160 pub fn get_mut(&mut self, pos: Position) -> Option<&mut T> { 161 self.data 162 .get_mut(pos.y as usize) 163 .and_then(|row| row.get_mut(pos.x as usize)) 164 } 165 166 /// Get a value from the grid at the given position, 167 /// panicking if the position is out of bounds. 168 /// 169 /// # Examples 170 /// 171 /// ``` 172 /// use utils::prelude::*; 173 /// 174 /// let data = vec![ 175 /// vec![1, 2, 3], 176 /// vec![4, 5, 6], 177 /// vec![7, 8, 9], 178 /// ]; 179 /// 180 /// let grid = Grid::new(data); 181 /// 182 /// assert_eq!(grid.unsafe_get(Position::new(0, 0)), &1); 183 /// assert_eq!(grid.unsafe_get(Position::new(1, 1)), &5); 184 /// ``` 185 /// 186 pub fn unsafe_get(&self, pos: Position) -> &T { 187 &self.data[pos.y as usize][pos.x as usize] 188 } 189 190 /// Get the value at the given position, wrapping around the grid if necessary. 191 /// 192 /// # Examples 193 /// 194 /// ``` 195 /// use utils::prelude::*; 196 /// 197 /// let data = vec![ 198 /// vec![1, 2, 3], 199 /// vec![4, 5, 6], 200 /// vec![7, 8, 9], 201 /// ]; 202 /// 203 /// let grid = Grid::new(data); 204 /// assert_eq!(grid.get_wrapped(Position::new(0, 0)), &1); 205 /// assert_eq!(grid.get_wrapped(Position::new(1, 1)), &5); 206 /// assert_eq!(grid.get_wrapped(Position::new(2, 2)), &9); 207 /// assert_eq!(grid.get_wrapped(Position::new(3, 3)), &1); 208 /// assert_eq!(grid.get_wrapped(Position::new(-1, -1)), &9); 209 /// ``` 210 /// 211 pub fn get_wrapped(&self, pos: Position) -> &T { 212 let wrapped_pos = pos.bind(self.size()); 213 &self.data[wrapped_pos.1][wrapped_pos.0] 214 } 215 216 /// Iterate over a row of the grid. 217 /// 218 /// # Examples 219 /// 220 /// ``` 221 /// use utils::prelude::*; 222 /// 223 /// let data = vec![ 224 /// vec![1, 2, 3], 225 /// vec![4, 5, 6], 226 /// vec![7, 8, 9], 227 /// ]; 228 /// 229 /// let grid = Grid::new(data); 230 /// 231 /// assert_eq!(grid.iter_row(0).unwrap().collect::<Vec<_>>(), vec![&1, &2, &3]); 232 /// assert_eq!(grid.iter_row(1).unwrap().sum::<usize>(), 4+5+6); 233 /// assert!(grid.iter_row(8).is_none()); 234 /// ``` 235 /// 236 pub fn iter_row(&self, row: usize) -> Option<impl Iterator<Item = &T>> { 237 self.data.get(row).map(|row| row.iter()) 238 } 239 240 /// Iterate over a column of the grid. 241 /// 242 /// # Examples 243 /// 244 /// ``` 245 /// use utils::prelude::*; 246 /// 247 /// 248 /// let data = vec![ 249 /// vec![1, 2, 3], 250 /// vec![4, 5, 6], 251 /// vec![7, 8, 9], 252 /// ]; 253 /// 254 /// let grid = Grid::new(data); 255 /// 256 /// assert_eq!(grid.iter_col(0).unwrap().collect::<Vec<_>>(), vec![&1, &4, &7]); 257 /// assert_eq!(grid.iter_col(1).unwrap().sum::<usize>(), 2+5+8); 258 /// assert!(grid.iter_col(8).is_none()); 259 /// ``` 260 /// 261 pub fn iter_col(&self, col: usize) -> Option<impl Iterator<Item = &T>> { 262 if col > self.width() { 263 return None; 264 } 265 Some(self.data.iter().filter_map(move |row| row.get(col))) 266 } 267 268 /// Get a row of the grid. 269 /// 270 /// This is the same as `self.iter_row(row).map(|iter| iter.collect())`. 271 pub fn get_row(&self, y: usize) -> Option<Vec<&T>> { 272 self.iter_row(y).map(|iter| iter.collect()) 273 } 274 275 /// Get a column of the grid. 276 /// 277 /// This is the same as `self.iter_col(col).map(|iter| iter.collect())`. 278 pub fn get_col(&self, x: usize) -> Option<Vec<&T>> { 279 self.iter_col(x).map(|iter| iter.collect()) 280 } 281 282 /// Iterate over all rows of the grid. 283 /// 284 /// # Examples 285 /// 286 /// ``` 287 /// use utils::prelude::*; 288 /// 289 /// 290 /// let data = vec![ 291 /// vec![1, 2, 3], 292 /// vec![4, 5, 6], 293 /// vec![7, 8, 9], 294 /// ]; 295 /// 296 /// let grid = Grid::new(data); 297 /// 298 /// assert_eq!(grid.iter_rows().enumerate().filter_map(|(y, row)| row.collect::<Vec<_>>().get(y).copied()).sum::<usize>(), 1+5+9); 299 /// ``` 300 /// 301 pub fn iter_rows(&self) -> impl Iterator<Item = impl Iterator<Item = &T>> { 302 self.data.iter().map(|row| row.iter()) 303 } 304 305 /// Iterate over all columns of the grid. 306 /// 307 /// # Examples 308 /// 309 /// ``` 310 /// use utils::prelude::*; 311 /// 312 /// 313 /// let data = vec![ 314 /// vec![1, 2, 3], 315 /// vec![4, 5, 6], 316 /// vec![7, 8, 9], 317 /// ]; 318 /// 319 /// let grid = Grid::new(data); 320 /// 321 /// assert_eq!(grid.iter_cols().enumerate().filter_map(|(x, col)| col.collect::<Vec<_>>().get(x).copied()).sum::<usize>(), 1+5+9); 322 /// ``` 323 /// 324 pub fn iter_cols(&self) -> impl Iterator<Item = impl Iterator<Item = &T>> { 325 (0..self.width()).map(move |col| self.iter_col(col).unwrap()) 326 } 327 328 /// Iterate over all elements of the grid. 329 /// 330 /// This also yields the position of each element for easy access. 331 /// 332 /// # Examples 333 /// 334 /// ``` 335 /// use utils::prelude::*; 336 /// 337 /// let data = vec![ 338 /// vec![1, 2, 3], 339 /// vec![4, 5, 6], 340 /// vec![7, 8, 9], 341 /// ]; 342 /// 343 /// let grid = Grid::new(data); 344 /// 345 /// assert_eq!(grid.iter().map(|(_, v)| v).sum::<usize>(), 1+2+3+4+5+6+7+8+9); 346 /// ``` 347 /// 348 pub fn iter(&self) -> impl Iterator<Item = (Position, &T)> { 349 self.data.iter().enumerate().flat_map(|(y, row)| { 350 row.iter() 351 .enumerate() 352 .map(move |(x, col)| (Position::new(x as isize, y as isize), col)) 353 }) 354 } 355 356 /// Iterate over all elements of the grid. 357 /// 358 /// This also yields the position of each element for easy access. 359 /// 360 /// # Examples 361 /// 362 /// ``` 363 /// use utils::prelude::*; 364 /// 365 /// let data = vec![ 366 /// vec![1, 2, 3], 367 /// vec![4, 5, 6], 368 /// vec![7, 8, 9], 369 /// ]; 370 /// 371 /// let grid = Grid::new(data); 372 /// 373 /// assert_eq!(grid.iter().map(|(_, v)| v).sum::<usize>(), 1+2+3+4+5+6+7+8+9); 374 /// ``` 375 /// 376 pub fn iter_mut(&mut self) -> impl Iterator<Item = (Position, &mut T)> { 377 self.data.iter_mut().enumerate().flat_map(|(y, row)| { 378 row.iter_mut() 379 .enumerate() 380 .map(move |(x, col)| (Position::new(x as isize, y as isize), col)) 381 }) 382 } 383 384 /// Get all positions relative to the given position in the grid based off the given kernels. 385 /// 386 /// This will automatically filter out any positions that are out of bounds. 387 /// 388 /// # Examples 389 /// 390 /// ``` 391 /// use utils::prelude::*; 392 /// 393 /// let data = vec![ 394 /// vec![1, 2, 3], 395 /// vec![4, 5, 6], 396 /// vec![7, 8, 9], 397 /// ]; 398 /// 399 /// let grid = Grid::new(data); 400 /// 401 /// let pos = Position::new(1, 1); 402 /// let kernels = &[ 403 /// Direction::North, 404 /// Direction::East, 405 /// ]; 406 /// 407 /// let mut relatives = grid.relatives(pos, kernels); 408 /// 409 /// assert_eq!(relatives.next(), Some((Direction::North, Position::new(1, 0), &2))); 410 /// assert_eq!(relatives.next(), Some((Direction::East, Position::new(2, 1), &6))); 411 /// ``` 412 /// 413 /// ``` 414 /// use utils::prelude::*; 415 /// 416 /// let data = vec![ 417 /// vec![1, 2, 3], 418 /// vec![4, 5, 6], 419 /// vec![7, 8, 9], 420 /// ]; 421 /// 422 /// let grid = Grid::new(data); 423 /// 424 /// let pos = Position::new(1, 0); 425 /// let kernels = &[ 426 /// Direction::North, // This will be filtered out, as (1, -1) is out of bounds 427 /// Direction::East, 428 /// ]; 429 /// 430 /// let mut relatives = grid.relatives(pos, kernels); 431 /// 432 /// assert_eq!(relatives.next(), Some((Direction::East, Position::new(2, 0), &3))); 433 /// ``` 434 /// 435 pub fn relatives<'a, M: Movement>( 436 &'a self, 437 pos: Position, 438 kernels: &'a [M], 439 ) -> impl Iterator<Item = (M, Position, &'a T)> + 'a { 440 pos.relatives(kernels) 441 .filter_map(move |(pos, dir)| self.get(pos).map(|v| (dir, pos, v))) 442 } 443 444 pub fn relatives_valid<'a, M: Movement>( 445 &'a self, 446 pos: Position, 447 kernels: &'a [M], 448 ) -> impl Iterator<Item = (M, Option<(Position, &'a T)>)> + 'a { 449 pos.relatives(kernels) 450 .map(move |(pos, dir)| (dir, self.get(pos).map(|v| (pos, v)))) 451 } 452 453 /// Get all positions relative to the given position, returning [None] if the relatives would 454 /// go out of bounds 455 /// 456 /// # Examples 457 /// 458 /// ``` 459 /// use utils::prelude::*; 460 /// 461 /// let data = vec![ 462 /// vec![1, 2, 3], 463 /// vec![4, 5, 6], 464 /// vec![7, 8, 9], 465 /// ]; 466 /// 467 /// let grid = Grid::new(data); 468 /// 469 /// let pos = Position::new(1, 1); 470 /// let kernels = &[ 471 /// Direction::North, 472 /// Direction::East, 473 /// ]; 474 /// 475 /// let mut relatives = grid.relatives_strict(pos, kernels); 476 /// if let Some(relatives) = relatives { 477 /// assert_eq!(relatives.get(0), Some((Direction::North, Position::new(1, 0), &2)).as_ref()); 478 /// assert_eq!(relatives.get(1), Some((Direction::East, Position::new(2, 1), &6)).as_ref()); 479 /// } else { panic!("Relatives should be Some!") } 480 /// ``` 481 /// 482 /// ``` 483 /// use utils::prelude::*; 484 /// 485 /// let data = vec![ 486 /// vec![1, 2, 3], 487 /// vec![4, 5, 6], 488 /// vec![7, 8, 9], 489 /// ]; 490 /// 491 /// let grid = Grid::new(data); 492 /// 493 /// let pos = Position::new(1, 0); 494 /// let kernels = &[ 495 /// Direction::North, // This will result in [None], as (1, -1) is out of bounds 496 /// Direction::East, 497 /// ]; 498 /// 499 /// let mut relatives = grid.relatives_strict(pos, kernels); 500 /// 501 /// assert!(relatives.is_none()); 502 /// ``` 503 pub fn relatives_strict<M: Movement>( 504 &self, 505 pos: Position, 506 kernels: &[M], 507 ) -> Option<Vec<(M, Position, &T)>> { 508 pos.relatives(kernels) 509 .map(move |(pos, dir)| self.get(pos).map(|v| (dir, pos, v))) 510 .collect::<Option<Vec<_>>>() 511 } 512 513 /// Get all positions relative to the given position in the grid based off the given kernels. 514 /// 515 /// Wraps around the grid if necessary. 516 /// 517 pub fn relatives_wrapped<'a, M: Movement>( 518 &'a self, 519 pos: Position, 520 kernels: &'a [M], 521 ) -> impl Iterator<Item = (M, Position, &'a T)> + 'a { 522 pos.relatives(kernels) 523 .map(move |(pos, dir)| (dir, pos, self.get_wrapped(pos))) 524 } 525 526 /// Get all positions relative to the given position in the grid based off the given kernels, 527 /// applying the kernel multiple times. 528 /// 529 /// This will automatically filter out any positions that are out of bounds. 530 /// 531 pub fn relatives_expand_by<'a, M: Movement>( 532 &'a self, 533 pos: Position, 534 kernels: &'a [M], 535 expand: usize, 536 ) -> impl Iterator<Item = ((M, usize), Position, &'a T)> + 'a { 537 pos.relatives_expand_by(kernels, expand) 538 .filter_map(move |(dir, pos)| self.get(pos).map(|v| (dir, pos, v))) 539 } 540 541 /// Get all positions relative to the given position in the grid based off the given kernels, 542 /// applying the kernel multiple times. 543 /// 544 /// Wraps around the grid if necessary. 545 /// 546 pub fn relatives_expand_by_wrapped<'a, M: Movement>( 547 &'a self, 548 pos: Position, 549 kernels: &'a [M], 550 expand: usize, 551 ) -> impl Iterator<Item = ((M, usize), Position, &'a T)> + 'a { 552 pos.relatives_expand_by(kernels, expand) 553 .map(move |(dir, pos)| (dir, pos, self.get_wrapped(pos))) 554 } 555 556 /// Like [Grid::relatives] but with `kernels` set to the four cardinal directions. 557 pub fn adjacent(&self, pos: Position) -> impl Iterator<Item = (Direction, Position, &T)> { 558 self.relatives(pos, &CARDINALS) 559 } 560 561 /// Like [Grid::relatives_wrapped] but with `kernels` set to the four cardinal directions. 562 pub fn adjacent_wrapped( 563 &self, 564 pos: Position, 565 ) -> impl Iterator<Item = (Direction, Position, &T)> { 566 self.relatives_wrapped(pos, &CARDINALS) 567 } 568 569 /// Like [Grid::relatives_expand_by] but with `kernels` set to the four cardinal directions. 570 pub fn adjacent_expand_by( 571 &self, 572 pos: Position, 573 expand: usize, 574 ) -> impl Iterator<Item = ((Direction, usize), Position, &T)> { 575 self.relatives_expand_by(pos, &CARDINALS, expand) 576 } 577 578 /// Like [Grid::relatives_expand_by_wrapped] but with `kernels` set to the four cardinal directions. 579 pub fn adjacent_expand_by_wrapped( 580 &self, 581 pos: Position, 582 expand: usize, 583 ) -> impl Iterator<Item = ((Direction, usize), Position, &T)> { 584 self.relatives_expand_by_wrapped(pos, &CARDINALS, expand) 585 } 586 587 /// Create a new cursor for this grid facing in the specified direction at the specified 588 /// position 589 pub fn cursor<D: Movement>(&self, pos: Position, dir: D) -> GridCursor<'_, T, D> { 590 GridCursor::new(self, pos, dir) 591 } 592} 593 594impl<T: Eq> Grid<T> { 595 pub fn find_tile(&self, tile: &T) -> Option<Position> { 596 self.iter() 597 .find_map(|(p, t)| if t == tile { Some(p) } else { None }) 598 } 599 600 pub fn cursor_at_tile<D: Movement>(&self, tile: &T, dir: D) -> Option<GridCursor<'_, T, D>> { 601 self.find_tile(tile).map(|pos| self.cursor(pos, dir)) 602 } 603} 604 605impl<T: std::fmt::Debug> std::fmt::Debug for Grid<T> { 606 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 607 let mut debug = f.debug_struct("Grid"); 608 for (y, row) in self.data.iter().enumerate() { 609 debug.field(&format!("row_{}", y), row); 610 } 611 debug.finish() 612 } 613} 614 615/// Utilities for making tiles of a grid. 616pub mod tiles { 617 use crate::{dir::Movement, pos::Position}; 618 619 use super::Grid; 620 621 #[macro_export] 622 /// Create an enum that implements `From<char>`. 623 /// 624 /// There are three versions of this macro: 625 /// 626 /// ## 1. Simple 627 /// 628 /// Create a simple enum that implements `From<char>`, with the specific characters mapping to specific variants. 629 /// Also will make the implementation panic if an invalid character is given. 630 /// 631 /// ``` 632 /// use utils::prelude::*; 633 /// use utils::tiles; 634 /// 635 /// tiles!(Tile, [ 636 /// '.' => Floor, 637 /// '#' => Wall, 638 /// ]); 639 /// 640 /// assert_eq!(Tile::from('.'), Tile::Floor); 641 /// assert_eq!(Tile::from('#'), Tile::Wall); 642 /// ``` 643 /// 644 /// ## 2. With Extra Variants 645 /// 646 /// Create an enum that implements `From<char>`, with the specific characters mapping to specific variants. 647 /// Also allows for extra variants to be added, which won't be mapped to any characters. 648 /// 649 /// ``` 650 /// use utils::prelude::*; 651 /// use utils::tiles; 652 /// 653 /// tiles!(Tile, [ 654 /// '.' => Floor, 655 /// '#' => Wall, 656 /// ], [ 657 /// Empty, 658 /// ]); 659 /// 660 /// assert_eq!(Tile::from('.'), Tile::Floor); 661 /// assert_eq!(Tile::from('#'), Tile::Wall); 662 /// let empty = Tile::Empty; 663 /// ``` 664 /// 665 /// The extra variants can also have fields. 666 /// 667 /// ``` 668 /// use utils::prelude::*; 669 /// use utils::tiles; 670 /// 671 /// tiles!(Tile, [ 672 /// '.' => Floor, 673 /// '#' => Wall, 674 /// ], [ 675 /// Door(bool), 676 /// ]); 677 /// 678 /// assert_eq!(Tile::from('.'), Tile::Floor); 679 /// assert_eq!(Tile::from('#'), Tile::Wall); 680 /// let door = Tile::Door(true); 681 /// ``` 682 /// 683 /// ## 3. With Extra Variants and Extra Logic for Invalid Characters 684 /// 685 /// Create an enum that implements `From<char>`, with the specific characters mapping to specific variants. 686 /// Also allows for extra variants to be added, which won't be mapped to any characters. 687 /// Also allows for extra logic to be added for invalid characters. 688 /// 689 /// ``` 690 /// use utils::prelude::*; 691 /// use utils::tiles; 692 /// 693 /// tiles!(Tile, [ 694 /// '.' => Floor, 695 /// '#' => Wall, 696 /// ], [ 697 /// Slope(Direction) 698 /// ], |c| { 699 /// match c { 700 /// '>' => Tile::Slope(Direction::East), 701 /// '<' => Tile::Slope(Direction::West), 702 /// _ => panic!("Invalid tile {c}"), 703 /// } 704 /// }); 705 /// 706 /// assert_eq!(Tile::from('.'), Tile::Floor); 707 /// assert_eq!(Tile::from('#'), Tile::Wall); 708 /// assert_eq!(Tile::from('>'), Tile::Slope(Direction::East)); 709 /// ``` 710 /// 711 macro_rules! tiles { 712 ($name:ident, [$($char:pat => $v_name:ident$(,)?)*]) => { 713 tiles!($name, [$($char => $v_name,)*], [], |c| { panic!("Invalid tile {c}") }); 714 }; 715 716 ($name:ident, [$($char:pat => $v_name:ident$(,)?)*], [$($e_name:ident$(($($i_name:ty$(,)?)*))?$(,)?)*]) => { 717 tiles!($name, [$($char => $v_name,)*], [$($e_name$(($($i_name,)*))?,)*], |c| { panic!("Invalid tile {c}") }); 718 }; 719 720 ($name:ident, [$($char:pat => $v_name:ident$(,)?)*], [$($e_name:ident$(($($i_name:ty$(,)?)*))?$(,)?)*], $default:expr) => { 721 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 722 pub enum $name { 723 $($v_name,)* 724 $($e_name$(($($i_name,)*))?,)* 725 } 726 727 impl From<char> for $name { 728 fn from(c: char) -> Self { 729 match c { 730 $($char => Self::$v_name,)* 731 _ => ($default)(c), 732 } 733 } 734 } 735 }; 736 } 737 738 /// Simple tile that holds a number value. 739 pub struct NumberTile { 740 pub value: isize, 741 } 742 743 impl From<char> for NumberTile { 744 fn from(c: char) -> Self { 745 Self { 746 value: c.to_digit(10).unwrap() as isize, 747 } 748 } 749 } 750 751 /// A tile that represents some kind of movement to another position in the grid. 752 pub trait DirectedTile<T: Movement>: Copy + Clone { 753 /// Get the next direction from the previous direction and position. 754 fn next_dir(&self, previous_dir: T, pos: Position) -> Option<T>; 755 756 /// Get the next position and position from the previous direction and position. 757 fn next_pos(&self, previous_dir: T, pos: Position) -> Option<(T, Position)> { 758 self.next_dir(previous_dir, pos) 759 .map(|d| (d, pos.move_dir(d))) 760 } 761 } 762 763 /// A tile that can be used in a flood fill. 764 pub trait FillableTile: Copy + Clone { 765 /// Check if the tile can be filled. 766 fn get_next_tiles(&self, pos: Position, grid: &Grid<Self>) -> Vec<Position>; 767 } 768} 769 770/// Utilities for traversing a grid. 771pub mod cursors { 772 773 use std::{ 774 collections::{HashSet, VecDeque}, 775 hash::Hasher, 776 }; 777 778 use super::{ 779 tiles::{DirectedTile, FillableTile}, 780 *, 781 }; 782 783 #[derive(Clone, Copy)] 784 /// A cursor for traversing a grid. 785 /// 786 /// This cursor holds a position and a direction which represents the current position in the grid. 787 /// 788 /// # Examples 789 /// 790 /// ``` 791 /// use utils::prelude::*; 792 /// 793 /// let data = vec![ 794 /// vec![1, 2, 3], 795 /// vec![4, 5, 6], 796 /// vec![7, 8, 9], 797 /// ]; 798 /// 799 /// let grid = Grid::new(data); 800 /// 801 /// let mut cursor = GridCursor::zero(&grid); 802 /// 803 /// assert_eq!(cursor.get(), Some(&1)); 804 /// cursor.move_forward(); 805 /// assert_eq!(cursor.get(), Some(&2)); 806 /// cursor.turn(true); 807 /// cursor.move_forward(); 808 /// assert_eq!(cursor.get(), Some(&5)); 809 /// ``` 810 /// 811 pub struct GridCursor<'a, T, D: Movement> { 812 grid: &'a Grid<T>, 813 pub pos: Position, 814 pub dir: D, 815 } 816 817 impl<'a, T> GridCursor<'a, T, Direction> { 818 /// Create a new cursor at position (0, 0) facing east. 819 pub fn zero(grid: &'a Grid<T>) -> Self { 820 Self { 821 grid, 822 pos: Position::new(0, 0), 823 dir: Direction::East, 824 } 825 } 826 827 /// Turn the cursor 90 degrees clockwise or counter-clockwise. 828 pub fn turn(&mut self, clockwise: bool) { 829 self.dir = self.dir.ninety_deg(clockwise); 830 } 831 832 /// Turn the cursor 180 degrees. 833 pub fn turn_around(&mut self) { 834 self.dir = self.dir.opposite(); 835 } 836 } 837 838 impl<T, D: Movement> PartialEq for GridCursor<'_, T, D> { 839 fn eq(&self, other: &Self) -> bool { 840 self.pos == other.pos && self.dir == other.dir 841 } 842 } 843 844 impl<T, D: Movement> std::hash::Hash for GridCursor<'_, T, D> { 845 fn hash<H: Hasher>(&self, state: &mut H) { 846 self.pos.hash(state); 847 self.dir.hash(state); 848 } 849 } 850 851 impl<'a, T, D: Movement> GridCursor<'a, T, D> { 852 /// Create a new cursor at the given position and direction. 853 pub fn new(grid: &'a Grid<T>, pos: Position, dir: D) -> Self { 854 Self { grid, pos, dir } 855 } 856 857 /// Get the current state of the cursor (position and direction) for use in tracking 858 pub fn state(&self) -> (Position, D) { 859 (self.pos, self.dir) 860 } 861 862 /// Move the cursor forward one step in the direction it is facing. 863 pub fn move_forward(&mut self) { 864 self.pos = self.pos.move_dir(self.dir); 865 } 866 867 /// See what tile (if any) the cursor will go to if it would move forward 868 pub fn peek_forward(&self) -> Option<(Position, &T)> { 869 let next_pos = self.pos.move_dir(self.dir); 870 self.grid.get(next_pos).map(|value| (next_pos, value)) 871 } 872 873 /// Get the value at the current position of the cursor. 874 pub fn get(&self) -> Option<&T> { 875 self.grid.get(self.pos) 876 } 877 878 /// Move the cursor forward one step in the direction it is facing and get the value at the new position. 879 pub fn advance_get(&mut self) -> Option<&T> { 880 self.move_forward(); 881 self.get() 882 } 883 } 884 885 impl<T: std::fmt::Debug, D: Movement> std::fmt::Debug for GridCursor<'_, T, D> { 886 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 887 f.debug_struct("GridCursor") 888 .field("pos", &self.pos) 889 .field("dir", &self.dir) 890 .field("value", &self.get()) 891 .finish() 892 } 893 } 894 895 /// A cursor for traversing a grid with a direction. 896 /// 897 /// This cursor will follow the direction of the tile it is currently on. 898 /// 899 /// # Examples 900 /// 901 /// ``` 902 /// use utils::prelude::*; 903 /// use utils::tiles; 904 /// 905 /// tiles!(Tile, [ 906 /// '>' => Right, 907 /// '<' => Left, 908 /// '^' => Up, 909 /// 'v' => Down, 910 /// ]); 911 /// 912 /// impl DirectedTile<Direction> for Tile { 913 /// fn next_dir(&self, previous_dir: Direction, pos: Position) -> Option<Direction> { 914 /// match self { 915 /// Tile::Right => Some(Direction::East), 916 /// Tile::Left => Some(Direction::West), 917 /// Tile::Up => Some(Direction::North), 918 /// Tile::Down => Some(Direction::South), 919 /// _ => None, 920 /// } 921 /// } 922 /// } 923 /// 924 /// let data = vec![ 925 /// vec![Tile::Right, Tile::Right, Tile::Down], 926 /// vec![Tile::Up, Tile::Left, Tile::Down], 927 /// vec![Tile::Up, Tile::Left, Tile::Left], 928 /// ]; 929 /// 930 /// let grid = Grid::new(data); 931 /// 932 /// let mut cursor = DirectedCursor::new(&grid, Position::new(0, 0), Direction::East); 933 /// 934 /// let path = cursor.map(|(p, _, _)| p).take(8).collect::<Vec<_>>(); 935 /// 936 /// assert_eq!(path, vec![ 937 /// Position::new(1, 0), 938 /// Position::new(2, 0), 939 /// Position::new(2, 1), 940 /// Position::new(2, 2), 941 /// Position::new(1, 2), 942 /// Position::new(0, 2), 943 /// Position::new(0, 1), 944 /// Position::new(0, 0), 945 /// ]); 946 /// ``` 947 /// 948 pub struct DirectedCursor<'a, T: DirectedTile<D>, D: Movement>(GridCursor<'a, T, D>); 949 950 impl<'a, T: DirectedTile<D>, D: Movement> DirectedCursor<'a, T, D> { 951 /// Create a new cursor at the given position and direction. 952 /// Note this starting position will *not* be included in the iterator. 953 pub fn new(grid: &'a Grid<T>, pos: Position, dir: D) -> Self { 954 let initial_cursor = GridCursor::new(grid, pos, dir); 955 Self(initial_cursor) 956 } 957 } 958 959 impl<T: DirectedTile<D>, D: Movement> Iterator for DirectedCursor<'_, T, D> { 960 type Item = (Position, D, T); 961 962 fn next(&mut self) -> Option<Self::Item> { 963 let current_val = self.0.get().cloned(); 964 current_val.and_then(|tile| { 965 tile.next_pos(self.0.dir, self.0.pos).map(|(dir, pos)| { 966 self.0.dir = dir; 967 self.0.pos = pos; 968 (self.0.pos, self.0.dir, tile) 969 }) 970 }) 971 } 972 } 973 974 /// A cursor that flood fills a grid. 975 /// 976 /// This cursor will flood fill the grid from the given position, 977 /// using [FillableTile::get_next_tiles] to determine which tiles to fill. 978 /// 979 /// Setting `wrapped` to true will make the cursor wrap around the grid if necessary. 980 /// Note this can lead to infinite loops if you don't have something to stop the iterator. 981 /// 982 /// # Examples 983 /// 984 /// ``` 985 /// use utils::prelude::*; 986 /// use utils::tiles; 987 /// 988 /// tiles!(Tile, [ 989 /// '.' => Floor, 990 /// '#' => Wall, 991 /// ]); 992 /// 993 /// impl FillableTile for Tile { 994 /// fn get_next_tiles(&self, pos: Position, grid: &Grid<Self>) -> Vec<Position> { 995 /// match self { 996 /// Tile::Floor => grid.adjacent(pos).filter(|(_, _, t)| t == &&Tile::Floor).map(|(_, p, _)| p).collect(), 997 /// _ => vec![], 998 /// } 999 /// } 1000 /// } 1001 /// 1002 /// let data = vec![ 1003 /// vec![Tile::Floor, Tile::Floor, Tile::Floor], 1004 /// vec![Tile::Floor, Tile::Wall, Tile::Floor], 1005 /// vec![Tile::Floor, Tile::Floor, Tile::Wall], 1006 /// ]; 1007 /// 1008 /// let grid = Grid::new(data); 1009 /// 1010 /// let mut cursor = FloodFillCursor::new(&grid, Position::new(0, 0), true); 1011 /// 1012 /// let path = cursor.collect::<Vec<_>>(); 1013 /// 1014 /// assert_eq!(path, vec![ 1015 /// Position::new(0, 0), 1016 /// Position::new(0, 1), 1017 /// Position::new(1, 0), 1018 /// Position::new(0, 2), 1019 /// Position::new(2, 0), 1020 /// Position::new(1, 2), 1021 /// Position::new(2, 1), 1022 /// ]); 1023 /// ``` 1024 /// 1025 pub struct FloodFillCursor<'a, T: FillableTile> { 1026 grid: &'a Grid<T>, 1027 visited: HashSet<Position>, 1028 queue: VecDeque<Position>, 1029 wrapped: bool, 1030 } 1031 1032 impl<'a, T: FillableTile> FloodFillCursor<'a, T> { 1033 /// Create a new cursor at the given position. 1034 pub fn new(grid: &'a Grid<T>, pos: Position, wrapped: bool) -> Self { 1035 let mut visited = HashSet::new(); 1036 visited.insert(pos); 1037 let mut queue = VecDeque::new(); 1038 queue.push_back(pos); 1039 Self { 1040 grid, 1041 visited, 1042 queue, 1043 wrapped, 1044 } 1045 } 1046 } 1047 1048 impl<T: FillableTile> std::fmt::Debug for FloodFillCursor<'_, T> { 1049 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 1050 f.debug_struct("FloodFillCursor") 1051 .field("visited", &self.visited) 1052 .field("queue", &self.queue) 1053 .finish() 1054 } 1055 } 1056 1057 impl<T: FillableTile> Iterator for FloodFillCursor<'_, T> { 1058 type Item = Position; 1059 1060 fn next(&mut self) -> Option<Self::Item> { 1061 let pos = self.queue.pop_front()?; 1062 let tile = if self.wrapped { 1063 self.grid.get_wrapped(pos) 1064 } else { 1065 self.grid.get(pos)? 1066 }; 1067 for next_pos in tile.get_next_tiles(pos, self.grid) { 1068 if self.visited.insert(next_pos) { 1069 self.queue.push_back(next_pos); 1070 } 1071 } 1072 Some(pos) 1073 } 1074 } 1075}