From caf113ce3d7207014b3b162c8a393d67b12c62ec Mon Sep 17 00:00:00 2001 From: Tobias Berger Date: Wed, 14 Dec 2022 13:43:23 +0100 Subject: [PATCH] Rust Day 14 - Performance improvements Use [usize; 2] instead of (usize, usize) Use HashSet instead of HashMap Use !.contains instead of .get(...).is_none() Compute lowest_stone when Cave is created, instead of for every sand --- rust/src/day14/main.rs | 116 +++++++++++++++------------------------ rust/src/day14/part_2.rs | 2 +- 2 files changed, 46 insertions(+), 72 deletions(-) diff --git a/rust/src/day14/main.rs b/rust/src/day14/main.rs index 0cf1ac8..7056e8e 100644 --- a/rust/src/day14/main.rs +++ b/rust/src/day14/main.rs @@ -3,118 +3,95 @@ #![feature(generators, generator_trait)] const INPUT: &str = include_str!("input.txt"); -use std::collections::HashMap; +use std::collections::HashSet; mod part_1; use part_1::part_1; mod part_2; use part_2::part_2; -#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)] -enum Material { - Stone, - Sand, -} - -#[derive(Debug, Clone, Eq, PartialEq)] +#[derive(Debug, Clone)] pub(crate) struct Cave { - pub(crate) field: HashMap<(usize, usize), Material>, + pub(crate) field: HashSet<[usize; 2]>, + lowest_stone: usize, } impl Cave { - fn lowest_stone(&self) -> usize { - self.field + fn new(field: HashSet<[usize; 2]>) -> Cave { + let lowest_stone = field .iter() - .filter_map(|(position, material)| { - if matches!(material, Material::Stone) { - Some(position.1) - } else { - None - } - }) + .map(|position| position[1]) .max() - .unwrap_or_default() + .unwrap_or_default(); + Cave { + field, + lowest_stone, + } } /// Inserts a field of sand into this [`Cave`], then returns whether it landed. - pub(crate) fn insert_sand_void(&mut self) -> Option<(usize, usize)> { - let lowest_position = self.lowest_stone(); - - let mut current_coordinate = (X, 0); + pub(crate) fn insert_sand_void(&mut self) -> Option<[usize; 2]> { + let mut current_coordinate = [X, 0]; loop { - if current_coordinate.1 >= lowest_position { + if current_coordinate[1] >= self.lowest_stone { return None; } - let coordinate_below_straight = (current_coordinate.0, current_coordinate.1 + 1); - let field_below_straight = self.field.get(&coordinate_below_straight); - if field_below_straight.is_none() { + let coordinate_below_straight = [current_coordinate[0], current_coordinate[1] + 1]; + if !self.field.contains(&coordinate_below_straight) { current_coordinate = coordinate_below_straight; continue; } - let coordinate_below_left = (current_coordinate.0 - 1, current_coordinate.1 + 1); - let field_below_left = self.field.get(&coordinate_below_left); - if field_below_left.is_none() { + let coordinate_below_left = [current_coordinate[0] - 1, current_coordinate[1] + 1]; + if !self.field.contains(&coordinate_below_left) { current_coordinate = coordinate_below_left; continue; } - let coordinate_below_right = (current_coordinate.0 + 1, current_coordinate.1 + 1); - let field_below_right = self.field.get(&coordinate_below_right); - if field_below_right.is_none() { + let coordinate_below_right = [current_coordinate[0] + 1, current_coordinate[1] + 1]; + if !self.field.contains(&coordinate_below_right) { current_coordinate = coordinate_below_right; continue; } - self.field.insert(current_coordinate, Material::Sand); + self.field.insert(current_coordinate); return Some(current_coordinate); } } /// Inserts a field of sand into this [`Cave`], then returns where it landed. - pub(crate) fn insert_sand_floor(&mut self) -> (usize, usize) { - let lowest_position = self.lowest_stone() + 2; + pub(crate) fn insert_sand_floor(&mut self) -> [usize; 2] { + let lowest_position = self.lowest_stone + 2; - let mut current_coordinate = (X, 0); + let mut current_coordinate = [X, 0]; loop { - let y_below = current_coordinate.1 + 1; + let y_below = current_coordinate[1] + 1; if y_below < lowest_position { - let coordinate_below_straight = (current_coordinate.0, y_below); - let field_below_straight = self.field.get(&coordinate_below_straight); - if field_below_straight.is_none() { + let coordinate_below_straight = [current_coordinate[0], y_below]; + if !self.field.contains(&coordinate_below_straight) { current_coordinate = coordinate_below_straight; continue; } - let coordinate_below_left = (current_coordinate.0 - 1, y_below); - let field_below_left = self.field.get(&coordinate_below_left); - if field_below_left.is_none() { + let coordinate_below_left = [current_coordinate[0] - 1, y_below]; + if !self.field.contains(&coordinate_below_left) { current_coordinate = coordinate_below_left; continue; } - let coordinate_below_right = (current_coordinate.0 + 1, y_below); - let field_below_right = self.field.get(&coordinate_below_right); - if field_below_right.is_none() { + let coordinate_below_right = [current_coordinate[0] + 1, y_below]; + if !self.field.contains(&coordinate_below_right) { current_coordinate = coordinate_below_right; continue; } - self.field - .try_insert(current_coordinate, Material::Sand) - .unwrap_or_else(|err| { - panic!("Tried to insert Sand at occupied position {current_coordinate:?} - {err}") - }); + assert!(self.field.insert(current_coordinate)); return current_coordinate; } else { - self.field - .try_insert(current_coordinate, Material::Sand) - .unwrap_or_else(|err| { - panic!("Tried to insert Sand at occupied position {current_coordinate:?} - {err}") - }); + assert!(self.field.insert(current_coordinate)); return current_coordinate; } } @@ -123,15 +100,12 @@ impl Cave { pub(crate) type Input = Cave; -pub(crate) fn interpolate( - from: (usize, usize), - to: (usize, usize), -) -> impl Iterator { - let min_x = from.0.min(to.0); - let max_x = from.0.max(to.0); - let min_y = from.1.min(to.1); - let max_y = from.1.max(to.1); - (min_x..=max_x).flat_map(move |x| (min_y..=max_y).map(move |y| (x, y))) +pub(crate) fn interpolate(from: [usize; 2], to: [usize; 2]) -> impl Iterator { + let min_x = from[0].min(to[0]); + let max_x = from[0].max(to[0]); + let min_y = from[1].min(to[1]); + let max_y = from[1].max(to[1]); + (min_x..=max_x).flat_map(move |x| (min_y..=max_y).map(move |y| [x, y])) } fn parse_input(input: &'static str) -> Input { @@ -140,14 +114,14 @@ fn parse_input(input: &'static str) -> Input { let coordinates = position_string.split(',').collect::>(); assert_eq!(coordinates.len(), 2); - ( + [ coordinates[0].parse::().unwrap(), coordinates[1].parse::().unwrap(), - ) + ] }) }); - let mut stone_tiles = HashMap::new(); + let mut stone_tiles = HashSet::new(); for lines in result { let mut coordinates = lines; @@ -155,14 +129,14 @@ fn parse_input(input: &'static str) -> Input { for destination in coordinates { for coordinate in interpolate(current_point, destination) { - stone_tiles.insert(coordinate, Material::Stone); + stone_tiles.insert(coordinate); } current_point = destination; } } - Cave { field: stone_tiles } + Cave::new(stone_tiles) } pub fn main() { diff --git a/rust/src/day14/part_2.rs b/rust/src/day14/part_2.rs index 0c2e539..2546c37 100644 --- a/rust/src/day14/part_2.rs +++ b/rust/src/day14/part_2.rs @@ -1,7 +1,7 @@ pub(crate) fn part_2(mut input: crate::Input) -> u64 { // Increase by 1 because I break as soon as (500, 0) is placed, so it doesn't get counted let mut sand_count = 1; - while let inserted_at = input.insert_sand_floor::<500>() && inserted_at.1 != 0 { + while let inserted_at = input.insert_sand_floor::<500>() && inserted_at[1] != 0 { sand_count += 1; }