1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209
//! # [Day 19: A Series of Tubes](http://adventofcode.com/2017/day/19) //! //! Somehow, a network packet got <span title="I know how fast it's going, but I //! don't know where it is.">lost</span> and ended up here. It's trying to follow //! a routing diagram (your puzzle input), but it's confused about where to go. use ::Direction; use ::Direction::*; /// Its starting point is just off the top of the diagram. Lines (drawn with /// `|`, `-`, and `+`) show the path it needs to take, starting by going /// down onto the only line connected to the top of the diagram. It needs to /// follow this path until it reaches the end (located somewhere within the /// diagram) and stop there. #[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)] enum Cell { Road, Empty, Letter(char), } use self::Cell::*; type Grid = Vec<Vec<Cell>>; impl From<char> for Cell { fn from(c: char) -> Self { match c { '-' | '|' | '+' => Road, ' ' => Empty, c => Letter(c), } } } /// Sometimes, the lines cross over each other; in these cases, it needs to /// continue going the same direction, and only turn left or right when /// there's no other option. In addition, someone has left *letters* on the /// line; these also don't change its direction, but it can use them to keep /// track of where it's been. For example: /// /// ```text /// | /// | +--+ /// A | C /// F---|----E|--+ /// | | | D /// +B-+ +--+ /// ``` /// /// Given this diagram, the packet needs to take the following path: /// /// - Starting at the only line touching the top of the diagram, it must /// go down, pass through `A`, and continue onward to the first `+`. /// - Travel right, up, and right, passing through `B` in the process. /// - Continue down (collecting `C`), right, and up (collecting `D`). /// - Finally, go all the way left through `E` and stopping at `F`. /// /// Following the path to the end, the letters it sees on its path are /// `ABCDEF`. /// /// ``` /// # use advent_solutions::advent2017::day19::solve; /// # let input = [ /// # " | \n", /// # " | +--+ \n", /// # " A | C \n", /// # " F---|----E|--+ \n", /// # " | | | D \n", /// # " +B-+ +--+ \n", /// # ] /// # .iter() /// # .map(|x| *x) /// # .collect::<String>(); /// let (collected, _) = solve(&input); /// assert_eq!(collected, "ABCDEF"); /// ``` #[derive(Clone, PartialEq, Eq, Debug, Hash)] struct Packet { position: (usize, usize), direction: Direction, collected: Vec<char>, } impl Packet { fn new(x: usize) -> Packet { Packet { position: (x, 0), direction: Down, collected: Vec::new(), } } fn step(&mut self, grid: &Grid) -> bool { if let Letter(c) = grid[self.position.1][self.position.0] { self.collected.push(c); } let n = self.neighbors(grid); if n.iter().any(|&x| x == self.direction) { self.position += self.direction; } else if n.iter().any(|&x| x == self.direction.cw()) { self.direction = self.direction.cw(); self.position += self.direction; } else if n.iter().any(|&x| x == self.direction.ccw()) { self.direction = self.direction.ccw(); self.position += self.direction; } else { return false; } true } fn neighbors(&self, grid: &Grid) -> Vec<Direction> { let w = grid[0].len(); let h = grid.len(); [Up, Down, Left, Right].iter() .map(|&dir| (dir, self.position + dir)) .filter(|&(_, (x, y))| x > 0 && y > 0 && x < w && y < h && grid[y as usize][x as usize] != Empty ) .map(|(dir, _)| dir) .collect() } } /// The little packet looks up at you, hoping you can help it find the way. /// *What letters will it see* (in the order it would see them) if it /// follows the path? (The routing diagram is very wide; make sure you view /// it without line wrapping.) /// /// ## Part Two /// /// The packet is curious how many steps it needs to go. /// /// For example, using the same routing diagram from the example above... /// /// ```text /// | /// | +--+ /// A | C /// F---|----E|--+ /// | | | D /// +B-+ +--+ /// ``` /// /// ...the packet would go: /// /// - `6` steps down (including the first line at the top of the diagram). /// - `3` steps right. /// - `4` steps up. /// - `3` steps right. /// - `4` steps down. /// - `3` steps right. /// - `2` steps up. /// - `13` steps left (including the `F` it stops on). /// /// This would result in a total of `38` steps. /// /// ``` /// # use advent_solutions::advent2017::day19::solve; /// # let input = [ /// # " | \n", /// # " | +--+ \n", /// # " A | C \n", /// # " F---|----E|--+ \n", /// # " | | | D \n", /// # " +B-+ +--+ \n", /// # ] /// # .iter() /// # .map(|x| *x) /// # .collect::<String>(); /// let (_, steps) = solve(&input); /// assert_eq!(steps, 38); /// ``` /// /// *How many steps* does the packet need to go? pub fn solve(input: &str) -> (String, usize) { let grid: Grid = input .lines() .map(|line| line.chars().map(Into::<Cell>::into).collect()) .collect(); let x = grid[0].iter().position(|&c| c == Road) .expect("Could not find starting point"); let mut packet = Packet::new(x); let mut steps = 1; while packet.step(&grid) { steps += 1; } let collected = packet.collected.into_iter().collect::<String>(); (collected, steps) } pub fn parse_input(input: &str) -> &str { input } test_day_both!("19", "LXWCKGRAOY", 17302);