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

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