Day 15
authorGreg Burri <greg.burri@gmail.com>
Thu, 15 Dec 2022 17:05:06 +0000 (18:05 +0100)
committerGreg Burri <greg.burri@gmail.com>
Thu, 15 Dec 2022 17:05:06 +0000 (18:05 +0100)
src/day15.rs [new file with mode: 0644]
src/days.rs
src/main.rs

diff --git a/src/day15.rs b/src/day15.rs
new file mode 100644 (file)
index 0000000..80cd727
--- /dev/null
@@ -0,0 +1,141 @@
+use itertools::Itertools;
+use regex::Regex;
+
+#[derive(Debug)]
+pub struct Sensor {
+    x: i64,
+    y: i64,
+    radius: i64,
+}
+
+#[derive(PartialEq)]
+pub struct Beacon {
+    x: i64,
+    y: i64,
+}
+
+pub fn parse(input: &str) -> (Vec<Sensor>, Vec<Beacon>) {
+    let regex =
+        Regex::new(r"Sensor at x=(-?{1}\d+), y=(-?\d+): closest beacon is at x=(-?\d+), y=(-?\d+)")
+            .unwrap();
+    let mut sensors = Vec::new();
+    let mut beacons = Vec::new();
+    for l in input.lines() {
+        let captures = regex.captures(l).unwrap();
+
+        let (s_x, s_y) = (
+            captures[1].parse::<i64>().unwrap(),
+            captures[2].parse::<i64>().unwrap(),
+        );
+        let (b_x, b_y) = (
+            captures[3].parse::<i64>().unwrap(),
+            captures[4].parse::<i64>().unwrap(),
+        );
+
+        sensors.push(Sensor {
+            x: s_x,
+            y: s_y,
+            radius: (s_x - b_x).abs() + (s_y - b_y).abs(),
+        });
+
+        let beacon = Beacon { x: b_x, y: b_y };
+        if !beacons.contains(&beacon) {
+            beacons.push(Beacon { x: b_x, y: b_y });
+        }
+    }
+
+    (sensors, beacons)
+}
+
+pub fn number_of_position_without_beacon(sensors: &[Sensor], beacons: &[Beacon], row: i64) -> i64 {
+    let nb_beacons_on_row = beacons
+        .into_iter()
+        .filter_map(|b| if b.y == row { Some(b.x) } else { None })
+        .count() as i64;
+
+    let segments = sensors
+        .into_iter()
+        .filter_map(|s| {
+            let dx = s.radius - (s.y - row).abs();
+            if dx >= 0 {
+                Some((s.x - dx, s.x + dx))
+            } else {
+                None
+            }
+        })
+        .sorted();
+
+    [(i64::MIN, i64::MIN)]
+        .into_iter()
+        .chain(segments)
+        .tuple_windows()
+        .fold(-nb_beacons_on_row + 1, |sum, (seg1, seg2)| {
+            sum + if seg2.0 > seg1.1 {
+                seg2.1 - seg2.0
+            } else if seg2.1 > seg1.1 {
+                seg2.1 - seg1.1
+            } else {
+                0
+            }
+        })
+}
+
+pub fn tuning_frequency(sensors: &[Sensor], limit: i64) -> i64 {
+    for s in sensors.iter() {
+        for x in s.x - s.radius - 1..=s.x + s.radius + 1 {
+            if x > limit {
+                break;
+            } else if x < 0 {
+                continue;
+            }
+
+            let dy = s.radius - (x - s.x).abs() + 1;
+            'a: for y in [s.y + dy, s.y - dy] {
+                if y <= limit && y >= 0 {
+                    for s2 in sensors.iter() {
+                        if (s2.x - x).abs() + (s2.y - y).abs() <= s2.radius {
+                            break 'a;
+                        }
+                    }
+                    return x * 4_000_000 + y;
+                }
+            }
+        }
+    }
+    0
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    static SCAN: &str = "Sensor at x=2, y=18: closest beacon is at x=-2, y=15
+    Sensor at x=9, y=16: closest beacon is at x=10, y=16
+    Sensor at x=13, y=2: closest beacon is at x=15, y=3
+    Sensor at x=12, y=14: closest beacon is at x=10, y=16
+    Sensor at x=10, y=20: closest beacon is at x=10, y=16
+    Sensor at x=14, y=17: closest beacon is at x=10, y=16
+    Sensor at x=8, y=7: closest beacon is at x=2, y=10
+    Sensor at x=2, y=0: closest beacon is at x=2, y=10
+    Sensor at x=0, y=11: closest beacon is at x=2, y=10
+    Sensor at x=20, y=14: closest beacon is at x=25, y=17
+    Sensor at x=17, y=20: closest beacon is at x=21, y=22
+    Sensor at x=16, y=7: closest beacon is at x=15, y=3
+    Sensor at x=14, y=3: closest beacon is at x=15, y=3
+    Sensor at x=20, y=1: closest beacon is at x=15, y=3";
+
+    #[test]
+    fn part1() {
+        let (sensors, beacons) = parse(SCAN);
+        assert_eq!(
+            number_of_position_without_beacon(&sensors, &beacons, 10),
+            26
+        );
+    }
+
+    #[test]
+    fn part2() {
+        let (sensors, _) = parse(SCAN);
+        assert_eq!(tuning_frequency(&sensors, 20), 56_000_011);
+    }
+}
index 704e5b2..45a6b9b 100644 (file)
@@ -151,3 +151,12 @@ pub fn day14() -> String {
         first_grain_touching_floor, last_grain
     )
 }
+
+pub fn day15() -> String {
+    let (sensors, beacons) = day15::parse(&fs::read_to_string("data/day15.input").unwrap());
+    format!(
+        "part1: {}, part2: {}",
+        day15::number_of_position_without_beacon(&sensors, &beacons, 2_000_000),
+        day15::tuning_frequency(&sensors, 4_000_000)
+    )
+}
index 1229242..c56644b 100644 (file)
@@ -17,6 +17,7 @@ mod day11;
 mod day12;
 mod day13;
 mod day14;
+mod day15;
 mod days;
 
 #[derive(Parser, Debug)]
@@ -47,6 +48,7 @@ fn main() {
         days::day12,
         days::day13,
         days::day14,
+        days::day15,
     ];
 
     let args = Args::parse();