summaryrefslogtreecommitdiff
path: root/src/bin/day14.rs
diff options
context:
space:
mode:
authorReiner Herrmann <reiner@reiner-h.de>2022-12-16 23:29:28 +0100
committerReiner Herrmann <reiner@reiner-h.de>2022-12-16 23:29:28 +0100
commit5af7c936f9bb7cc0109cd610ac499ffaae95f443 (patch)
treee357126007d40bc46c854b0bf6d38617245fb403 /src/bin/day14.rs
parent6e8c0c66ddb5f24c8f25a702371bbcf0c5de5e7b (diff)
day14
Diffstat (limited to 'src/bin/day14.rs')
-rw-r--r--src/bin/day14.rs132
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);
+ }
+}