Advent of Code solutions

Day 6

bwc9876.dev aa0ed0d1 90981006

verified
+210 -8
+57 -2
utils/src/grid.rs
··· 1 + use cursors::GridCursor; 2 + 1 3 use crate::{ 2 4 dir::{Direction, Movement, CARDINALS}, 3 5 pos::Position, ··· 334 336 pub fn iter(&self) -> impl Iterator<Item = (Position, &T)> { 335 337 self.data.iter().enumerate().flat_map(|(y, row)| { 336 338 row.iter() 339 + .enumerate() 340 + .map(move |(x, col)| (Position::new(x as isize, y as isize), col)) 341 + }) 342 + } 343 + 344 + /// Iterate over all elements of the grid. 345 + /// 346 + /// This also yields the position of each element for easy access. 347 + /// 348 + /// # Examples 349 + /// 350 + /// ``` 351 + /// use utils::prelude::*; 352 + /// 353 + /// let data = vec![ 354 + /// vec![1, 2, 3], 355 + /// vec![4, 5, 6], 356 + /// vec![7, 8, 9], 357 + /// ]; 358 + /// 359 + /// let grid = Grid::new(data); 360 + /// 361 + /// assert_eq!(grid.iter().map(|(_, v)| v).sum::<usize>(), 1+2+3+4+5+6+7+8+9); 362 + /// ``` 363 + /// 364 + pub fn iter_mut(&mut self) -> impl Iterator<Item = (Position, &mut T)> { 365 + self.data.iter_mut().enumerate().flat_map(|(y, row)| { 366 + row.iter_mut() 337 367 .enumerate() 338 368 .map(move |(x, col)| (Position::new(x as isize, y as isize), col)) 339 369 }) ··· 532 562 ) -> impl Iterator<Item = ((Direction, usize), Position, &T)> { 533 563 self.relatives_expand_by_wrapped(pos, &CARDINALS, expand) 534 564 } 565 + 566 + /// Create a new cursor for this grid facing in the specified direction at the specified 567 + /// position 568 + pub fn cursor<D: Movement>(&self, pos: Position, dir: D) -> GridCursor<T, D> { 569 + GridCursor::new(self, pos, dir) 570 + } 571 + } 572 + 573 + impl<T> Grid<T> 574 + where 575 + T: Eq, 576 + { 577 + pub fn find_tile(&self, tile: &T) -> Option<Position> { 578 + self.iter() 579 + .find_map(|(p, t)| if t == tile { Some(p) } else { None }) 580 + } 581 + 582 + pub fn cursor_at<D: Movement>(&self, tile: &T, dir: D) -> Option<GridCursor<T, D>> { 583 + self.find_tile(tile).map(|pos| self.cursor(pos, dir)) 584 + } 535 585 } 536 586 537 587 impl<T: std::fmt::Debug> std::fmt::Debug for Grid<T> { ··· 742 792 /// 743 793 pub struct GridCursor<'a, T, D: Movement> { 744 794 grid: &'a Grid<T>, 745 - pos: Position, 746 - dir: D, 795 + pub pos: Position, 796 + pub dir: D, 747 797 } 748 798 749 799 impl<'a, T> GridCursor<'a, T, Direction> { ··· 789 839 /// Move the cursor forward one step in the direction it is facing. 790 840 pub fn move_forward(&mut self) { 791 841 self.pos = self.pos.move_dir(self.dir); 842 + } 843 + 844 + pub fn peek_forward(&self) -> Option<(Position, &T)> { 845 + let next_pos = self.pos.move_dir(self.dir); 846 + self.grid.get(next_pos).map(|value| (next_pos, value)) 792 847 } 793 848 794 849 /// Get the value at the current position of the cursor.
+133 -6
years/2024/src/day_6.rs
··· 1 + use std::collections::HashSet; 1 2 2 - use advent_core::{Day, day_stuff, ex_for_day}; 3 + use advent_core::{day_stuff, ex_for_day, Day}; 4 + use utils::{dir::Direction, pos::Position, prelude::GridCursor, tiles}; 3 5 4 6 pub struct Day6; 5 7 8 + tiles!(Tile, [ 9 + '.' => Floor, 10 + '#' => Wall, 11 + '^' => GuardStart 12 + ]); 13 + 14 + impl Tile { 15 + fn is_open(&self) -> bool { 16 + matches!(self, Self::Floor | Self::GuardStart) 17 + } 18 + } 19 + 20 + type Grid = utils::grid::Grid<Tile>; 21 + 22 + /// Returns true if we get into a loop 23 + fn crawl_grid_with_obs(mut curs: GridCursor<Tile, Direction>, obs_pos: Position) -> bool { 24 + let mut visited = HashSet::new(); 25 + 26 + while let Some((pos, tile)) = curs.peek_forward() { 27 + visited.insert((curs.pos, curs.dir)); 28 + if tile.is_open() && pos != obs_pos { 29 + curs.move_forward(); 30 + } else { 31 + curs.turn(true); 32 + } 33 + 34 + if visited.contains(&(curs.pos, curs.dir)) { 35 + return true; 36 + } 37 + } 38 + 39 + false 40 + } 41 + 6 42 impl Day for Day6 { 43 + day_stuff!(6, "41", "6", Grid); 44 + 45 + fn part_1(input: Self::Input) -> Option<String> { 46 + let mut curs = input 47 + .cursor_at(&Tile::GuardStart, Direction::North) 48 + .unwrap(); 49 + 50 + let mut visited = HashSet::with_capacity(input.width() * input.height()); 51 + 52 + while let Some((_next_pos, tile)) = curs.peek_forward() { 53 + visited.insert(curs.pos); 54 + if tile.is_open() { 55 + curs.move_forward(); 56 + } else { 57 + curs.turn(true); 58 + } 59 + } 60 + 61 + // While let means we'll be missing one 62 + visited.insert(curs.pos); 63 + 64 + Some(visited.len().to_string()) 65 + } 66 + 67 + fn part_2(input: Self::Input) -> Option<String> { 68 + let mut curs = input 69 + .cursor_at(&Tile::GuardStart, Direction::North) 70 + .unwrap(); 71 + 72 + let start_curs = curs; 73 + 74 + let mut obs = HashSet::with_capacity(input.width() * input.height()); 7 75 8 - day_stuff!(6, "", ""); 76 + while let Some((possible, tile)) = curs.peek_forward() { 77 + if !obs.contains(&possible) { 78 + let curs_2 = start_curs; 79 + if crawl_grid_with_obs(curs_2, possible) { 80 + obs.insert(possible); 81 + } 82 + } 9 83 10 - fn part_1(_input: Self::Input) -> Option<String> { 11 - None 84 + if tile.is_open() { 85 + curs.move_forward(); 86 + } else { 87 + curs.turn(true); 88 + } 89 + } 90 + 91 + Some(obs.len().to_string()) 12 92 } 13 93 14 - fn part_2(_input: Self::Input) -> Option<String> { 15 - None 94 + fn parse_input(input: &str) -> Self::Input { 95 + Grid::parse(input) 16 96 } 17 97 } 98 + 99 + // - ORIGINAL PART 2 : BRUTE FORCE APPROACH (Im so sad i had to do this) - 100 + 101 + // let mut c = 0; 102 + 103 + // let start_pos = input 104 + // .iter() 105 + // .find_map(|(pos, c)| { 106 + // if matches!(c, Tile::GuardStart) { 107 + // Some(pos) 108 + // } else { 109 + // None 110 + // } 111 + // }) 112 + // .unwrap(); 113 + 114 + // for x in 0..input.width() { 115 + // for y in 0..input.height() { 116 + // let pos = mpos!(x as isize, y as isize); 117 + // if matches!(input.get(pos).unwrap(), Tile::Floor) { 118 + // let mut i2 = input.clone(); 119 + // i2.iter_mut().for_each(|(pt, r)| if pt == pos { *r = Tile::Wall; }); 120 + // let mut curs = GridCursor::new(&i2, start_pos, Direction::North); 121 + 122 + // let mut visited = HashSet::with_capacity(i2.width()); 123 + 124 + // loop { 125 + // visited.insert((curs.pos, curs.dir)); 126 + // if let Some(val_ahead) = i2.get(curs.pos.move_dir(curs.dir)) { 127 + // if val_ahead.is_open() { 128 + // curs.move_forward(); 129 + // } else { 130 + // curs.turn(true); 131 + // } 132 + // if visited.contains(&(curs.pos, curs.dir)) { 133 + // c += 1; 134 + // break; 135 + // } 136 + // } else { 137 + // break; 138 + // } 139 + // } 140 + // } 141 + // } 142 + // } 143 + 144 + // Some(c.to_string())
+10
years/2024/src/examples/day_6/1.txt
··· 1 + ....#..... 2 + .........# 3 + .......... 4 + ..#....... 5 + .......#.. 6 + .......... 7 + .#..^..... 8 + ........#. 9 + #......... 10 + ......#...
+10
years/2024/src/examples/day_6/2.txt
··· 1 + ....#..... 2 + .........# 3 + .......... 4 + ..#....... 5 + .......#.. 6 + .......... 7 + .#..^..... 8 + ........#. 9 + #......... 10 + ......#...