this is the part where the challenges start to get hard. implementing
the naive solution for part 1 did not take much time. used a hashset
since it's easier than using arrays, but slightly less performant. I
expected the naive solution would not work for part 2 due to the size of
the problem but it still worked, although it did take a few seconds to
finish. the solution i would have used if part 2 took too long would be
to trace some 45* rays from the end points of horizontal beams and
subtract the area inside from the largest sand triangle.
This commit is contained in:
Peter 2022-12-14 23:37:58 +08:00
parent 1e4645e835
commit 2ae5cce33d

View File

@ -1,3 +1,112 @@
fn main() {
println!("Hello, world!");
use std::{collections::HashSet, fs::read_to_string, ops::RangeInclusive};
const PATH: &str = "src/input";
fn parse_pt(p_str: &str) -> Vec<i32> {
p_str
.split(',')
.map(|x| x.parse::<i32>().unwrap())
.collect::<Vec<i32>>()
}
fn any_range(a: i32, b: i32) -> RangeInclusive<i32> {
if a < b {
return a..=b;
}
return b..=a;
}
const SAND_SOURCE: (i32, i32) = (500, 0);
fn main() {
let data = read_to_string(PATH).expect("Error reading file");
assert!(data.is_ascii());
println!("PART 1 {}", sand_sim(data, true));
let data = read_to_string(PATH).expect("Error reading file");
assert!(data.is_ascii());
println!("PART 2 {}", sand_sim(data, false));
}
fn sand_sim(data: String, part_1: bool) -> u32 {
// TODO: DETERMINE MAX DEPTH AUTOMATICALLY.
let (mut walls, max_depth) = scan_walls(data);
let mut stop_sand = false;
let mut sand_units = 0;
while !stop_sand && !walls.contains(&SAND_SOURCE) {
let mut sand_pos = SAND_SOURCE;
loop {
// If the tile immediately below is blocked (by rock or sand), ...
if walls.contains(&(sand_pos.0, sand_pos.1 + 1)) {
// ... the unit of sand attempts to instead move diagonally one step down and to the left
if !walls.contains(&(sand_pos.0 - 1, sand_pos.1 + 1)) {
sand_pos.0 -= 1;
// If that tile is blocked, the unit of sand attempts to instead move diagonally one step down and to the right
} else if !walls.contains(&(sand_pos.0 + 1, sand_pos.1 + 1)) {
sand_pos.0 += 1;
// If all three possible destinations are blocked, the unit of sand comes to rest and no longer moves
} else {
walls.insert(sand_pos);
sand_units += 1;
break;
}
}
if sand_pos.1 > max_depth {
walls.insert(sand_pos);
if part_1 {
stop_sand = true;
}
sand_units += 1;
break;
}
// A unit of sand always falls down one step if possible.
sand_pos.1 += 1;
}
}
// visualize_sand(walls, max_depth);
return sand_units;
}
fn scan_walls(data: String) -> (HashSet<(i32, i32)>, i32) {
let paths = data.split('\n');
let mut walls = HashSet::<(i32, i32)>::new();
let mut max_depth = 0;
for path in paths {
let mut points = path.split(" -> ").into_iter();
let mut previous_point = parse_pt(points.next().unwrap());
while let Some(next_point) = points.next() {
let next_point = parse_pt(next_point);
if previous_point[1] > max_depth {
max_depth = previous_point[1];
}
if next_point[1] > max_depth {
max_depth = next_point[1];
}
// println!(
// "{} {} -> {} {}",
// previous_point[0], previous_point[1], next_point[0], next_point[1]
// );
for x in any_range(previous_point[0], next_point[0]) {
for y in any_range(previous_point[1], next_point[1]) {
walls.insert((x, y));
}
}
previous_point = next_point;
}
}
return (walls, max_depth);
}
fn visualize_sand(walls: HashSet<(i32, i32)>, max_depth: i32) {
for j in 0..=max_depth + 2 {
for i in 480..550 {
if walls.contains(&(i, j)) {
print!("#");
} else {
print!(".");
}
}
println!();
}
}