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:
parent
53f9a710e0
commit
caf113ce3d
2 changed files with 46 additions and 72 deletions
|
@ -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() {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue