From 086ae68bbe2a015728b8eb3fd624c788a69d2209 Mon Sep 17 00:00:00 2001 From: Kevin Caffrey Date: Mon, 11 Dec 2023 12:55:29 -0500 Subject: [PATCH] minor day 10 optimization because I didn't realize the starting tile was guaranteed to only have two pipes connecting to it --- README.md | 26 +++++++++++----------- src/bin/10.rs | 61 +++++++++++++++++++++++---------------------------- 2 files changed, 40 insertions(+), 47 deletions(-) diff --git a/README.md b/README.md index dade97e..6ea2300 100644 --- a/README.md +++ b/README.md @@ -27,19 +27,19 @@ Solutions for [Advent of Code](https://adventofcode.com/) in [Rust](https://www. | Day | Part 1 | Part 2 | | :---: | :---: | :---: | -| [Day 1](./src/bin/01.rs) | `34.2µs` | `38.0µs` | -| [Day 2](./src/bin/02.rs) | `42.3µs` | `41.3µs` | -| [Day 3](./src/bin/03.rs) | `83.6µs` | `99.8µs` | -| [Day 4](./src/bin/04.rs) | `53.5µs` | `50.4µs` | -| [Day 5](./src/bin/05.rs) | `20.8µs` | `24.3µs` | -| [Day 6](./src/bin/06.rs) | `203.0ns` | `103.0ns` | -| [Day 7](./src/bin/07.rs) | `93.8µs` | `91.4µs` | -| [Day 8](./src/bin/08.rs) | `72.7µs` | `161.5µs` | -| [Day 9](./src/bin/09.rs) | `58.5µs` | `50.6µs` | -| [Day 10](./src/bin/10.rs) | `394.2µs` | `775.3µs` | -| [Day 11](./src/bin/11.rs) | `16.1µs` | `15.7µs` | - -**Total: 2.22ms** +| [Day 1](./src/bin/01.rs) | `36.8µs` | `35.1µs` | +| [Day 2](./src/bin/02.rs) | `41.6µs` | `41.4µs` | +| [Day 3](./src/bin/03.rs) | `83.7µs` | `98.7µs` | +| [Day 4](./src/bin/04.rs) | `49.3µs` | `50.0µs` | +| [Day 5](./src/bin/05.rs) | `20.5µs` | `24.1µs` | +| [Day 6](./src/bin/06.rs) | `208.0ns` | `102.0ns` | +| [Day 7](./src/bin/07.rs) | `97.3µs` | `94.0µs` | +| [Day 8](./src/bin/08.rs) | `72.7µs` | `160.2µs` | +| [Day 9](./src/bin/09.rs) | `58.2µs` | `58.4µs` | +| [Day 10](./src/bin/10.rs) | `320.4µs` | `640.5µs` | +| [Day 11](./src/bin/11.rs) | `16.6µs` | `15.9µs` | + +**Total: 2.02ms** --- diff --git a/src/bin/10.rs b/src/bin/10.rs index 2bac00b..c91e215 100644 --- a/src/bin/10.rs +++ b/src/bin/10.rs @@ -13,7 +13,7 @@ pub fn part_one(input: &str) -> Option { } pub fn part_two(input: &str) -> Option { - let mut field = input.parse::().expect("valid input"); + let field = input.parse::().expect("valid input"); // Figure out where the loop is, and construct a new binary map of loop/not loop. let (loop_position, _) = field.find_definite_loop_position()?; @@ -48,25 +48,6 @@ pub fn part_two(input: &str) -> Option { cur = next; } - // Figure out the missing tile for the start position to make things easier later on. - let starting_tile = Tile::pipes().find(|&pipe| { - pipe.neighbors(field.starting_position).all(|pos| { - let is_in_loop = field - .index(pos) - .map(|index| is_in_loop[index]) - .unwrap_or(false); - let is_connected = field - .pipe_neighbors(pos) - .any(|p| p == field.starting_position); - is_in_loop && is_connected - }) - })?; - if let Some(cell) = field.get_mut(field.starting_position) { - *cell = starting_tile; - } else { - return None; - } - // Create a helper that will let us determine the size of a connected region bounded // by "loop pipes". let mut closed = HashSet::new(); @@ -164,7 +145,6 @@ enum Tile { SouthWestPipe, SouthEastPipe, Ground, - StartingPosition, } #[derive(Debug, Copy, Clone, Eq, PartialEq)] @@ -220,11 +200,6 @@ impl Field { let mut next = ArrayVec::<[(Position, Position); 4]>::new(); use Tile::*; match (self.get(pos), pos.direction_from(last_pos)) { - (Some(StartingPosition), _) => { - for dir in Direction::cardinal() { - next.push((pos.go(dir), pos)); - } - } (Some(HorizontalPipe), Direction::East) => next.push((pos.go(Direction::West), pos)), (Some(HorizontalPipe), Direction::West) => next.push((pos.go(Direction::East), pos)), (Some(VerticalPipe), Direction::North) => next.push((pos.go(Direction::South), pos)), @@ -243,10 +218,13 @@ impl Field { } pub fn find_definite_loop_position(&self) -> Option<(Position, u32)> { - let mut distance = 0; + let mut distance = 1; let mut cur = ArrayVec::<[(Position, Position); 4]>::new(); let mut definite_loop_position = None; - cur.push((self.starting_position, self.starting_position)); + cur.extend( + self.pipe_neighbors(self.starting_position) + .map(|n| (n, self.starting_position)), + ); while definite_loop_position.is_none() && !cur.is_empty() { let mut next = ArrayVec::<[(Position, Position); 4]>::new(); for (pos, from_pos) in cur @@ -418,6 +396,9 @@ enum ParseFieldError { #[error("multiple starting positions were found")] MultipleStartingPositions, + + #[error("no valid tile was found for starting position")] + NoValidStartingTile, } impl FromStr for Field { @@ -440,8 +421,9 @@ impl FromStr for Field { }) .enumerate() .map(|(index, ch)| { - let tile: Tile = ch?.try_into()?; - if tile == Tile::StartingPosition { + let ch = ch?; + let tile: Tile = ch.try_into()?; + if ch == 'S' { if starting_position.is_some() { return Err(ParseFieldError::MultipleStartingPositions); } @@ -450,7 +432,7 @@ impl FromStr for Field { Ok(tile) }) .collect::>()?; - Ok(Self { + let mut field = Self { tiles, rows, cols, @@ -460,7 +442,18 @@ impl FromStr for Field { row: (index / cols) as isize, col: (index % cols) as isize, })?, - }) + }; + let starting_position = field.starting_position; + let starting_tile = Tile::pipes() + .find(|&pipe| { + pipe.neighbors(starting_position) + .all(|n| field.pipe_neighbors(n).any(|n2| n2 == starting_position)) + }) + .ok_or(ParseFieldError::NoValidStartingTile)?; + if let Some(cell) = field.get_mut(starting_position) { + *cell = starting_tile; + }; + Ok(field) } } @@ -476,8 +469,8 @@ impl TryFrom for Tile { 'J' => NorthWestPipe, '7' => SouthWestPipe, 'F' => SouthEastPipe, - '.' => Ground, - 'S' => StartingPosition, + // NOTE: we'll replace the starting tile later, so just return Ground as a placeholder + '.' | 'S' => Ground, ch => return Err(ParseFieldError::InvalidTileCharacter(ch)), }) }