From 9e2e03812366e39ddc84fb80b5cc90a69a9eb936 Mon Sep 17 00:00:00 2001
From: ironsm4sh <ironsm4sh@ironsm4sh.nl>
Date: Fri, 23 Dec 2022 08:04:57 +0100
Subject: [PATCH] Day 23

---
 Cargo.toml      |   4 +
 input/day23.txt |  71 ++++++++++++++++++
 src/day23.rs    | 191 ++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 266 insertions(+)
 create mode 100644 input/day23.txt
 create mode 100644 src/day23.rs

diff --git a/Cargo.toml b/Cargo.toml
index ab333f0..b99ec3c 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -94,3 +94,7 @@ path = "src/day21.rs"
 [[bin]]
 name = "day22"
 path = "src/day22.rs"
+
+[[bin]]
+name = "day23"
+path = "src/day23.rs"
diff --git a/input/day23.txt b/input/day23.txt
new file mode 100644
index 0000000..9db15fc
--- /dev/null
+++ b/input/day23.txt
@@ -0,0 +1,71 @@
+.###.....#...###.##.#....##.#.#####.#####.#.##.....##..#.#.#.##.#.#....
+#..#####....###.####.###...#..#...#......#..###...#....#####.####..##.#
+#.###..#####..#..#...#####...###...###..##.....##.##..#...##....####..#
+.###.##..##....#...#..#..#..#...###.###.#.#...#.###..##.###...#.....##.
+#.........#.#..#..#####.#.###.....#...#..#..##..#.#..#.##..##..#...####
+..##.#####..########..##..##.#....#.#....#.###.#...#.#####...###...##.#
+#.#..#.#..####.##.#.##.##.#.##.#.##...#.##...##.##.##...##...#..##.#.#.
+##...#...##.#####.#######..##...######.....####.#...####.##....#.###...
+...#####.......##.###.#.##..###.....##.#..####...#.#..#...##.#.##.#..#.
+##..####..######.##......###...#...#.#.#.#....###.##.###...##.#.####...
+.#..#..#.#.##..#....##..##.#.##.#...#..##.###...#####...##.###.#..###.#
+#....###.####.###.#..####.#...#..##.#.....##..#.###.##.#.###########..#
+##..##..#...#..###.###..#.##.######.#......###..####.#..#.#.##..#..###.
+....#.....#.#.#..##.#####.####.##.###..####.#...#.##.####..###..#.#...#
+..###........#.#.#.#....##.##..#..##..#.###.#....##.#....###..#..####..
+##....##.##.####.#..#.#..#...#.#....###..#..##.#...#....##.#.######.#..
+.#.#.#...#....#...##.###..#.#.#.#........#.##...####.#....######.###..#
+.##...#.###.#.#.##..###.#..#.#.###.#####..##..#####..###.##.###..#####.
+.#.##.###..#.....#.......##.##..##..#...#...####..#.#......###.#.#.##..
+#######.#.#..#######....#######.##...###...###.#.#.##...###..#..##.#.##
+.#..#..#.######..####.######..##.#.####..##..###.#...#.##...###.#.#..##
+##..#.#.#..##.#.#.##.#.....##.#.###..##..###.##.##...#..###.#..#.......
+###..#...#...#...#.#.###.#######.#..##..##.#.##.##.##.#.##.##.#.####...
+##...######.###....#..#.#.#.#.#......#.##.#.#.##.#...#.###.##.###.##..#
+.####..#.#.##..#.....#..###..##...##...#.#..##...###..#...######.#...#.
+.###...##...##.#.###....#.#...#..##.#.#.##.#######..#......#...##..####
+#.#.#.###.#.#..#.#.#######.#..##.....#.#####.#..##..##.#....##...#..#.#
+##.###...##........##.###....##.###..####..#..#..#...#..#.#.##......#..
+##...#.#...##.#..#.##...###.##.####.#.##.###..#.#....#...##.##.###..##.
+..##..#..#.####...#.#.######.#..##..#####.#..###....##.#.#.#.##..####..
+####..#....####....##..###..#####.##.#.#.###..#.#.##..#.#....##.###..##
+....##..#....#####...###..###..#.##.....#.#.#..###.####.##....#.#.#.###
+.#.##.##.#..####..#.##........####.#.#.###.##.##.#..###....##..##..#.#.
+.###.##.##.#....#...####..#..#..#..###.##...##.#.#.#.##...#.#.#..#..#..
+#......#.##....####.####...#.#.###..#.#.#.#.#..#.....#.##.#...##..#....
+#.#.#....###.#....###..####...##.##....#..####..#....##.#..####..#..###
+##.#....##.##.....#..######..#.#..##..#.#...##.#.#.#####...#.#.#.#.###.
+...#.###.##....##.#.###.##.......#..#####..#.#.#.##..####.#...###.#.#..
+##......##...###.....######.#.######.#.##.#.#...##.##..#..#.####...###.
+.##.#..######.#.##...##.....##..##.##...##.#.###.#..###......##...#.#..
+.....#####.......##.##.####..#.#..##..#.###.#.#.#..##.####..#.#.##..##.
+....###.######.#.##..######..####...##..##....#..##.#.#.....#.###..####
+.#.#..#.#.#...##...##.#....#.#..###.###.##.#.###.#.##..#..#....##.#####
+..##.#...######.#..##...##.#.###.#...##..#.#####..#..###.#.#.#..###...#
+....##..#.#..........#..##.#.####..##...#.#...##.###...##....#######..#
+##.#.#..####.###.#.#....#.#..#..########.##..##.####.#.#.#..##...#.#.##
+..#.#.#...#.#..#....#..##.#..#.##....#....##.###..###....#.###.#.#..###
+.##.....####..#....####.##...#..##....##..##.....##.###.##.#...##.###..
+.##.....##.#####..#####.####.######....#.###.#.###.##.#.#......#....###
+...#.#..####.#.###.#.....##.##.##.##....##....###....##.##........####.
+.##.###.##.##.##...#..#....#..##.###..##..#..#...#.#.##..##..#..#..####
+.#...#.......#.##.##....##.....#..##...###.#....#.###.#.#..####.#.#..##
+.#..#..###.####..##.#.##..###.###..##....###.#...##.#.......#..##.#.#..
+.##.......#..###.##.##..######..##..###.#.######.####...####.....##..##
+###..#..#...###.#..###..#.#.#.########...#.......#.#.#.#..#.#.##.###..#
+#.#..#...#...##..#.##..#..#............#..#.#..####.###.#######.###...#
+#...#.##.###.##.....#.########..####.###.#..#..####.##.#...##..#..##.#.
+......####...#.#.###.##..#.##.##..#.#.#.....#...#.##.#.#.##..##.###..#.
+.#.#.##.#.##.#.#.#.#...#.###.#....#..####.###.....#.###.##.##.#.##.###.
+#####.#..##.##....##..####....#.#..#..#..#.#.###.#.#..####.####...#.##.
+#...##..###..####.#......#.######...#.####....##....#.##.....####.#.#.#
+.#..##..#.######.#...#..#...########..#.#..#....#.##....#.#.##.####....
+###.#.##......##.#########.#.########...#...#.#..#....#..###.#.#..#..#.
+.####..#.##...#.##..#.##.#.##.##...#..###.##.....#.##.#...####....###.#
+#####...#...#..#.##.#..##.#.##..#.#......##.##.##..#.#.#.##.##..#......
+##.###....#..#......#.####..###.#.#.##...#..#.#.###....####.#.#..##....
+.#..##.######.##...#.#.##..#.###..##..##.##...###..#####.###....#######
+#.#.....#..#...#.........#.####.#...#....#.###.#.#..####....#.##.##.#.#
+..###...###.##.##.#..######..###.###..#.##..###...###.....###.##.######
+########......#.#..#.#.##..##.##....##.#.#.###..#.#...#...#.#..##....##
+.##...##...#..#...#..#...#..#..##.##.#...#.###....#..#.##......#.#....#
\ No newline at end of file
diff --git a/src/day23.rs b/src/day23.rs
new file mode 100644
index 0000000..4ac0686
--- /dev/null
+++ b/src/day23.rs
@@ -0,0 +1,191 @@
+use std::collections::{HashMap, HashSet, VecDeque};
+type Vec2 = (i32, i32);
+
+fn main() {
+    let mut world: HashMap<Vec2, Option<Vec2>> = HashMap::new();
+
+    let mut current = (0, 0);
+    for line in include_str!("../input/day23.txt").split('\n') {
+        for char in line.chars() {
+            match char {
+                '#' => Some(world.insert(current, None)),
+                _ => None,
+            };
+            current.0 += 1;
+        }
+        current.0 = 0;
+        current.1 += 1;
+    }
+
+    let mut instruction_order: VecDeque<char> = VecDeque::from(['N', 'S', 'W', 'E']);
+
+    for _ in 0..10 {
+        let should_move = process_first_half(&mut world, &mut instruction_order);
+        if should_move {
+            world = process_second_half(&world);
+            continue;
+        }
+        break;
+    }
+
+    let silver = get_empty_in_limits(&world);
+    println!("Part one: {silver}");
+
+    let mut round = 10;
+    loop {
+        let should_move = process_first_half(&mut world, &mut instruction_order);
+        round += 1;
+        if should_move {
+            world = process_second_half(&world);
+            continue;
+        }
+        break;
+    }
+
+    println!("Part two: {round}");
+}
+
+fn process_second_half(world: &HashMap<Vec2, Option<Vec2>>) -> HashMap<Vec2, Option<Vec2>> {
+    let mut known: HashMap<Vec2, i32> = HashMap::new();
+    for (pos, optional) in world.iter() {
+        if let Some(proposed_position) = optional {
+            let mut current = *known.get(&proposed_position).unwrap_or(&0);
+            current += 1;
+            known.insert(proposed_position.clone(), current);
+        } else {
+            known.insert(pos.clone(), 2);
+        }
+    }
+
+    let mut new_world: HashMap<Vec2, Option<Vec2>> = HashMap::new();
+    for (pos, optional) in world.iter() {
+        if let Some(proposed_position) = optional {
+            let elves = *known.get(&proposed_position).unwrap();
+            if elves == 1 {
+                new_world.insert(proposed_position.clone(), None);
+            } else {
+                new_world.insert(pos.clone(), None);
+            }
+        } else {
+            new_world.insert(pos.clone(), None);
+        }
+    }
+    return new_world;
+}
+
+fn process_first_half(
+    world: &mut HashMap<Vec2, Option<Vec2>>,
+    instruction_order: &mut VecDeque<char>,
+) -> bool {
+    let north = vec![(0, -1), (-1, -1), (1, -1)];
+    let south = vec![(0, 1), (-1, 1), (1, 1)];
+    let east = vec![(1, 0), (1, -1), (1, 1)];
+    let west = vec![(-1, 0), (-1, -1), (-1, 1)];
+    let mut neighbors: Vec<Vec2> = Vec::new();
+    neighbors.extend(north.iter());
+    neighbors.extend(south.iter());
+    neighbors.extend(east.iter());
+    neighbors.extend(west.iter());
+
+    let instructions = vec![
+        instruction_order.pop_front().unwrap(),
+        instruction_order.pop_front().unwrap(),
+        instruction_order.pop_front().unwrap(),
+        instruction_order.pop_front().unwrap(),
+    ];
+    let mut known: HashSet<Vec2> = HashSet::new();
+    for (pos, _) in world.iter() {
+        known.insert(pos.clone());
+    }
+    let mut output = false;
+    for (pos, proposed_position) in world.iter_mut() {
+        let mut is_alone = true;
+        for neighbor in neighbors.iter() {
+            if known.contains(&(pos.0 + neighbor.0, pos.1 + neighbor.1)) {
+                is_alone = false;
+                break;
+            }
+        }
+        if is_alone {
+            continue;
+        }
+
+        for instruction in instructions.iter() {
+            let to_check;
+            match instruction {
+                'N' => to_check = &north,
+                'E' => to_check = &east,
+                'S' => to_check = &south,
+                'W' => to_check = &west,
+                _ => panic!("Invalid instruction!"),
+            }
+
+            let mut is_alone = true;
+            for neighbor in to_check {
+                if known.contains(&(pos.0 + neighbor.0, pos.1 + neighbor.1)) {
+                    is_alone = false;
+                    break;
+                }
+            }
+            if !is_alone {
+                continue;
+            }
+
+            *proposed_position = Some((pos.0 + to_check[0].0, pos.1 + to_check[0].1));
+            output = true;
+            break;
+        }
+    }
+    instruction_order.push_back(instructions[1]);
+    instruction_order.push_back(instructions[2]);
+    instruction_order.push_back(instructions[3]);
+    instruction_order.push_back(instructions[0]);
+    return output;
+}
+
+fn get_empty_in_limits(world: &HashMap<Vec2, Option<Vec2>>) -> i32 {
+    let limits = get_limits(world);
+    let width = (limits.1 .0 - limits.0 .0) + 1;
+    let height = (limits.1 .1 - limits.0 .1) + 1;
+    return width * height - world.len() as i32;
+}
+
+fn get_limits(world: &HashMap<Vec2, Option<Vec2>>) -> (Vec2, Vec2) {
+    let mut limits = ((0, 0), (0, 0));
+
+    for (pos, _) in world.iter() {
+        if pos.0 < limits.0 .0 {
+            limits.0 .0 = pos.0;
+        }
+        if pos.0 > limits.1 .0 {
+            limits.1 .0 = pos.0;
+        }
+        if pos.1 < limits.0 .1 {
+            limits.0 .1 = pos.1;
+        }
+        if pos.1 > limits.1 .1 {
+            limits.1 .1 = pos.1;
+        }
+    }
+
+    return limits;
+}
+
+#[allow(dead_code)]
+fn print_world(world: &HashMap<Vec2, Option<Vec2>>) {
+    let limits = get_limits(world);
+    let mut output = String::new();
+    output += "\n";
+    for y in limits.0 .1..=limits.1 .1 {
+        for x in limits.0 .0..=limits.1 .0 {
+            let v = world.get(&(x, y));
+
+            match v {
+                Some(_) => output += "#",
+                None => output += ".",
+            }
+        }
+        output += "\n";
+    }
+    println!("{output}");
+}
-- 
GitLab