You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
124 lines
3.4 KiB
Rust
124 lines
3.4 KiB
Rust
use std::collections::HashMap;
|
|
|
|
#[derive(Debug, Clone)]
|
|
enum Pixel {
|
|
Wall,
|
|
Sand,
|
|
Source,
|
|
}
|
|
|
|
fn main() {
|
|
let contents = std::fs::read_to_string("data.txt").expect("Failed to read file");
|
|
let paths = contents
|
|
.lines()
|
|
.map(parse)
|
|
.collect::<Vec<Vec<(usize, usize)>>>();
|
|
|
|
let mut cave: HashMap<(usize, usize), Pixel> = HashMap::new();
|
|
cave.insert((500, 0), Pixel::Source);
|
|
create_cave(&mut cave, paths.clone());
|
|
|
|
// Part 1
|
|
simulate(&mut cave.clone());
|
|
|
|
// Part 2
|
|
add_floor(&mut cave);
|
|
simulate(&mut cave);
|
|
}
|
|
|
|
fn parse(line: &str) -> Vec<(usize, usize)> {
|
|
line.split(" -> ")
|
|
.map(|c| c.split_once(",").unwrap())
|
|
.map(|(l, r)| (l.parse().unwrap(), r.parse().unwrap()))
|
|
.collect::<Vec<(usize, usize)>>()
|
|
}
|
|
|
|
fn create_cave(cave: &mut HashMap<(usize, usize), Pixel>, paths: Vec<Vec<(usize, usize)>>) -> () {
|
|
for path in paths {
|
|
for i in 0..path.len() - 1 {
|
|
let (x1, y1) = path[i];
|
|
let (x2, y2) = path[i + 1];
|
|
|
|
for x in if x1 < x2 { x1..=x2 } else { x2..=x1 } {
|
|
cave.insert((x, y1), Pixel::Wall);
|
|
}
|
|
|
|
for y in if y1 < y2 { y1..=y2 } else { y2..=y1 } {
|
|
cave.insert((x1, y), Pixel::Wall);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
fn draw_cave(cave: &HashMap<(usize, usize), Pixel>) -> () {
|
|
let x_min = cave.keys().map(|(x, y)| x).min().unwrap();
|
|
let y_min = cave.keys().map(|(x, y)| y).min().unwrap();
|
|
let x_max = cave.keys().map(|(x, y)| x).max().unwrap();
|
|
let y_max = cave.keys().map(|(x, y)| y).max().unwrap();
|
|
|
|
for y in *y_min..=*y_max {
|
|
print!("{y} ");
|
|
for x in *x_min..=*x_max {
|
|
match cave.get(&(x, y)) {
|
|
Some(Pixel::Wall) => print!("#"),
|
|
Some(Pixel::Sand) => print!("o"),
|
|
Some(Pixel::Source) => print!("+"),
|
|
_ => print!("."),
|
|
}
|
|
}
|
|
println!();
|
|
}
|
|
}
|
|
|
|
fn simulate(cave: &mut HashMap<(usize, usize), Pixel>) -> () {
|
|
let mut grains = 1;
|
|
while drop_sand(cave) {
|
|
draw_cave(cave);
|
|
println!("Grains: {grains}");
|
|
grains = grains+1;
|
|
}
|
|
}
|
|
|
|
fn drop_sand(cave: &mut HashMap<(usize, usize), Pixel>) -> bool {
|
|
let mut sand = (500, 0);
|
|
let mut falling = true;
|
|
while falling {
|
|
if cave.get(&(sand.0, sand.1 + 1)).is_none() {
|
|
// sand beats down
|
|
sand = (sand.0, sand.1 + 1);
|
|
|
|
// Part 1 ends when it falls forever (or to 500)
|
|
if sand.1 > 500 {
|
|
return false;
|
|
}
|
|
} else {
|
|
if cave.get(&(sand.0 - 1, sand.1 + 1)).is_none() {
|
|
// check down and left
|
|
sand = (sand.0 - 1, sand.1 + 1);
|
|
} else if cave.get(&(sand.0 + 1, sand.1 + 1)).is_none() {
|
|
// check down and right
|
|
sand = (sand.0 + 1, sand.1 + 1);
|
|
} else {
|
|
// neither are open
|
|
cave.insert((sand.0, sand.1), Pixel::Sand);
|
|
falling = false;
|
|
|
|
// Part 2 ends when it hits sand immediately
|
|
if sand.1 == 0 {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
fn add_floor(cave: &mut HashMap<(usize, usize), Pixel>) -> () {
|
|
let y = cave.keys().map(|(_, y)| y).max().unwrap().to_owned();
|
|
|
|
for x in 0..1000 {
|
|
cave.insert((x, y + 2), Pixel::Wall);
|
|
}
|
|
}
|