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
This commit is contained in:
Tobias Berger 2022-12-14 13:43:23 +01:00
parent 53f9a710e0
commit caf113ce3d
Signed by: toby
GPG key ID: 2D05EFAB764D6A88
2 changed files with 46 additions and 72 deletions

View file

@ -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<const X: usize>(&mut self) -> Option<(usize, usize)> {
let lowest_position = self.lowest_stone();
let mut current_coordinate = (X, 0);
pub(crate) fn insert_sand_void<const X: usize>(&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<const X: usize>(&mut self) -> (usize, usize) {
let lowest_position = self.lowest_stone() + 2;
pub(crate) fn insert_sand_floor<const X: usize>(&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<Item = (usize, usize)> {
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<Item = [usize; 2]> {
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::<Vec<_>>();
assert_eq!(coordinates.len(), 2);
(
[
coordinates[0].parse::<usize>().unwrap(),
coordinates[1].parse::<usize>().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() {

View file

@ -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;
}