diff options
| author | Reiner Herrmann <reiner@reiner-h.de> | 2022-12-16 23:29:28 +0100 |
|---|---|---|
| committer | Reiner Herrmann <reiner@reiner-h.de> | 2022-12-16 23:29:28 +0100 |
| commit | 5af7c936f9bb7cc0109cd610ac499ffaae95f443 (patch) | |
| tree | e357126007d40bc46c854b0bf6d38617245fb403 /src/bin/day14.rs | |
| parent | 6e8c0c66ddb5f24c8f25a702371bbcf0c5de5e7b (diff) | |
day14
Diffstat (limited to 'src/bin/day14.rs')
| -rw-r--r-- | src/bin/day14.rs | 132 |
1 files changed, 132 insertions, 0 deletions
diff --git a/src/bin/day14.rs b/src/bin/day14.rs new file mode 100644 index 0000000..c6323a3 --- /dev/null +++ b/src/bin/day14.rs @@ -0,0 +1,132 @@ +use std::collections::HashMap; + +static DAY: u8 = 14; + +fn main() { + let input = advent::read_lines(DAY); + println!("{DAY}a: {}", sand_before_abyss(&input)); + println!("{DAY}b: {}", sand_to_rest(&input)); +} + +#[derive(Debug)] +enum Material { + Rock, + Sand, +} + +fn parse_map(input: &[String]) -> HashMap<(isize,isize),Material> { + let mut map = HashMap::new(); + + for line in input { + let mut path = Vec::new(); + for coord in line.split(" -> ") { + let (x,y) = coord.split_once(',').expect("coordinate needs to contain ,"); + let x = x.parse::<isize>().expect("x coord should be number"); + let y = y.parse::<isize>().expect("y coord should be number"); + path.push((x,y)); + } + for pos in path.windows(2) { + let (x1, x2) = if pos[0].0 < pos[1].0 { + (pos[0].0, pos[1].0) + } else { + (pos[1].0, pos[0].0) + }; + + let (y1, y2) = if pos[0].1 < pos[1].1 { + (pos[0].1, pos[1].1) + } else { + (pos[1].1, pos[0].1) + }; + + for x in x1 ..= x2 { + for y in y1 ..= y2 { + map.insert((x,y), Material::Rock); + } + } + } + } + map +} + +fn next_sand_pos(map: &mut HashMap<(isize,isize),Material>, pos: (isize,isize)) -> Option<(isize,isize)> { + let next_positions = [ + (pos.0, pos.1 + 1), + (pos.0 - 1, pos.1 + 1), + (pos.0 + 1, pos.1 + 1), + ]; + next_positions.iter().find(|next_pos| !map.contains_key(next_pos)).copied() +} + +fn sand_above_abyss(map: &mut HashMap<(isize,isize),Material>, above: isize) -> bool { + let mut pos = (500,0); + + while pos.1 <= above { + match next_sand_pos(map, pos) { + Some(next_pos) => pos = next_pos, + None => { + map.insert(pos, Material::Sand); + break; + } + } + } + + pos.1 <= above +} + +fn sand_at_source(map: &mut HashMap<(isize,isize),Material>, floor: isize) -> bool { + let mut pos = (500,0); + + loop { + match next_sand_pos(map, pos) { + Some(next_pos) => { + if next_pos.1 == floor { + map.insert(pos, Material::Sand); + break; + } + pos = next_pos; + }, + None => { + map.insert(pos, Material::Sand); + break; + } + } + } + + pos == (500,0) +} + +fn sand_before_abyss(input: &[String]) -> usize { + let mut map = parse_map(input); + let lowest_rock = *map.keys().max_by_key(|(_,y)| y).expect("map should have a rock"); + let mut sand = 0; + while sand_above_abyss(&mut map, lowest_rock.1) { + sand += 1; + } + sand +} + +fn sand_to_rest(input: &[String]) -> usize { + let mut map = parse_map(input); + let floor = map.keys().max_by_key(|(_,y)| y).expect("map should have a rock").1 + 2; + let mut sand = 0; + while !sand_at_source(&mut map, floor) { + sand += 1; + } + sand + 1 +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test() { + let input = [ + "498,4 -> 498,6 -> 496,6", + "503,4 -> 502,4 -> 502,9 -> 494,9", + ].iter().map(|&x| String::from(x)).collect::<Vec<_>>(); + + assert_eq!(sand_before_abyss(&input), 24); + assert_eq!(sand_to_rest(&input), 93); + } +} |
