|
@@ -0,0 +1,160 @@
|
|
|
+use aoc2022_niels_overkamp::common::{self, AOCResult};
|
|
|
+use nom::{
|
|
|
+ bytes::complete::tag,
|
|
|
+ character::{complete::i128 as i128_parser, streaming::char},
|
|
|
+ combinator::all_consuming,
|
|
|
+ error::Error,
|
|
|
+ sequence::{preceded, separated_pair},
|
|
|
+ Finish, Parser,
|
|
|
+};
|
|
|
+use std::{
|
|
|
+ collections::{HashMap, HashSet},
|
|
|
+ mem,
|
|
|
+ ops::Range,
|
|
|
+};
|
|
|
+
|
|
|
+const DAY: &str = "day15";
|
|
|
+
|
|
|
+fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|
|
+ common::run(DAY, &run)
|
|
|
+}
|
|
|
+
|
|
|
+type Num = i128;
|
|
|
+type Point = (Num, Num);
|
|
|
+type Line = (Point, Point);
|
|
|
+
|
|
|
+fn intersection(((x1, y1), (x2, y2)): Line, ((x3, y3), (x4, y4)): Line) -> Option<Point> {
|
|
|
+ let d = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4);
|
|
|
+
|
|
|
+ let tn = (x1 - x3) * (y3 - y4) - (y1 - y3) * (x3 - x4);
|
|
|
+ let un = (x1 - x3) * (y1 - y2) - (y1 - y3) * (x1 - x2);
|
|
|
+ let b = if d > 0 {
|
|
|
+ tn > d || un > d || tn < 0 || un < 0
|
|
|
+ } else {
|
|
|
+ tn < d || un < d || tn > 0 || un > 0
|
|
|
+ };
|
|
|
+ if b || d == 0 {
|
|
|
+ return None;
|
|
|
+ }
|
|
|
+ let temp = tn * (x2 - x1);
|
|
|
+ if temp % d != 0 {
|
|
|
+ return None;
|
|
|
+ }
|
|
|
+
|
|
|
+ Some((x1 + tn * (x2 - x1) / d, y1 + tn * (y2 - y1) / d))
|
|
|
+}
|
|
|
+
|
|
|
+pub fn run(input: &Vec<String>) -> AOCResult {
|
|
|
+ let mut line_parser = {
|
|
|
+ let v_parser = |c| preceded(char(c).and(char('=')), i128_parser);
|
|
|
+ let point_parser = || separated_pair(v_parser('x'), tag(", "), v_parser('y'));
|
|
|
+ let sensor_parser = preceded(tag("Sensor at "), point_parser());
|
|
|
+ let beacon_parser = preceded(tag("closest beacon is at "), point_parser());
|
|
|
+ let line_parser = separated_pair(sensor_parser, tag(": "), beacon_parser);
|
|
|
+ all_consuming(line_parser)
|
|
|
+ };
|
|
|
+
|
|
|
+ let check_height = input.last().unwrap().parse::<Num>()?;
|
|
|
+
|
|
|
+ let check_size = check_height * 2;
|
|
|
+ let check_area = 0..=check_size;
|
|
|
+ let mut l_lines = HashSet::new();
|
|
|
+ let mut r_lines = HashSet::new();
|
|
|
+ let mut oor_intersections: HashMap<Point, usize> = HashMap::new();
|
|
|
+ let mut sensors = vec![];
|
|
|
+
|
|
|
+ let mut check_line = HashSet::new();
|
|
|
+ let mut check_line_beacons = HashSet::new();
|
|
|
+
|
|
|
+ let filter_add = |i: Option<Point>, i_map: &mut HashMap<Point, usize>| {
|
|
|
+ i.filter(|i| check_area.contains(&i.0) && check_area.contains(&i.1))
|
|
|
+ .map(|i| *i_map.entry(i).or_default() += 1)
|
|
|
+ };
|
|
|
+
|
|
|
+ for line in &input[..input.len() - 1] {
|
|
|
+ let (sensor, beacon) = line_parser(line.as_str())
|
|
|
+ .finish()
|
|
|
+ .map(|(_, l)| l)
|
|
|
+ .map_err(|e: Error<&str>| e.to_string())?;
|
|
|
+
|
|
|
+ let range = sensor.0.abs_diff(beacon.0) + sensor.1.abs_diff(beacon.1);
|
|
|
+ let dist = check_height.abs_diff(sensor.1);
|
|
|
+
|
|
|
+ let range = range as Num;
|
|
|
+ let dist = dist as Num;
|
|
|
+
|
|
|
+ // Part 2
|
|
|
+
|
|
|
+ sensors.push((sensor, range));
|
|
|
+
|
|
|
+ let corners = [
|
|
|
+ (sensor.0 + range + 1, sensor.1),
|
|
|
+ (sensor.0, sensor.1 + range + 1),
|
|
|
+ (sensor.0 - range - 1, sensor.1),
|
|
|
+ (sensor.0, sensor.1 - range - 1),
|
|
|
+ ];
|
|
|
+
|
|
|
+ let r_line1 = (corners[0], corners[1]);
|
|
|
+ let l_line1 = (corners[1], corners[2]);
|
|
|
+ let r_line2 = (corners[2], corners[3]);
|
|
|
+ let l_line2 = (corners[3], corners[0]);
|
|
|
+
|
|
|
+ for l_line in l_lines.iter() {
|
|
|
+ filter_add(intersection(*l_line, r_line1), &mut oor_intersections);
|
|
|
+ filter_add(intersection(*l_line, r_line2), &mut oor_intersections);
|
|
|
+ }
|
|
|
+ for r_line in r_lines.iter() {
|
|
|
+ filter_add(intersection(*r_line, l_line1), &mut oor_intersections);
|
|
|
+ filter_add(intersection(*r_line, l_line2), &mut oor_intersections);
|
|
|
+ }
|
|
|
+
|
|
|
+ l_lines.extend(&[l_line1, l_line2]);
|
|
|
+ r_lines.extend(&[r_line1, r_line2]);
|
|
|
+
|
|
|
+ // Part 1
|
|
|
+ let radius = (range - dist).max(-1);
|
|
|
+ check_line.extend(
|
|
|
+ Range {
|
|
|
+ start: (sensor.0 - radius),
|
|
|
+ end: (sensor.0 + radius) + 1,
|
|
|
+ }
|
|
|
+ .into_iter(),
|
|
|
+ );
|
|
|
+
|
|
|
+ if beacon.1 == check_height {
|
|
|
+ check_line_beacons.insert(beacon.0);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ let (s, r): (Vec<_>, Vec<_>) = sensors.clone().into_iter().unzip();
|
|
|
+ let (sx, sy): (Vec<_>, Vec<_>) = s.into_iter().unzip();
|
|
|
+
|
|
|
+ let possible_locations = oor_intersections.iter().filter(|(_, n)| **n >= 4);
|
|
|
+ println!("l={:?}", possible_locations.clone().map(|(i, _)| i).collect::<Vec<_>>().as_slice());
|
|
|
+ let mut location = None;
|
|
|
+ for (pos, n) in possible_locations {
|
|
|
+ let mut in_range = false;
|
|
|
+ for (sensor, range) in sensors.iter() {
|
|
|
+ if (sensor.0.abs_diff(pos.0) + sensor.1.abs_diff(pos.1)) as Num <= *range {
|
|
|
+ in_range = true;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if !in_range {
|
|
|
+ location = Some(pos);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ let location = location.ok_or("No suitable location found")?;
|
|
|
+ let tuning_frequency = location.0 * check_size + location.1;
|
|
|
+
|
|
|
+ let count = check_line.difference(&check_line_beacons).count();
|
|
|
+
|
|
|
+ Ok([Some(count.to_string()), Some(tuning_frequency.to_string())])
|
|
|
+}
|
|
|
+
|
|
|
+#[test]
|
|
|
+pub fn test_day15() {
|
|
|
+ assert!(common::run_test(DAY, &run))
|
|
|
+}
|