use std::cmp; use std::collections::HashMap; use std::convert::TryFrom; use std::fs; use std::ops::Deref; use std::ops::RangeInclusive; use std::ops::Sub; use std::panic; use std::process; use std::str; use std::sync::Arc; use std::thread; #[derive(Default, Debug, PartialEq)] struct Location { x: isize, y: isize, } impl<'a> Sub for &'a Location { type Output = isize; fn sub(self, other: &'a Location) -> isize { (self.x - other.x).abs() + (self.y - other.y).abs() } } #[derive(Default, PartialEq)] struct Sensor { loc: Location, visibility: usize, } #[derive(Default, Debug, PartialEq)] struct Beacon { loc: Location, } struct ComparableRange(RangeInclusive); impl ComparableRange { pub fn intersects(&self, other: &ComparableRange) -> bool { self.contains(other.start()) || other.contains(self.start()) } pub fn intersection(&self, other: &ComparableRange) -> ComparableRange { match self.intersects(other) { false => panic!("Ranges do not intersect"), true => ComparableRange( cmp::min(*self.start(), *other.start())..=cmp::max(*self.end(), *other.end()), ), } } } impl Deref for ComparableRange { type Target = RangeInclusive; fn deref(&self) -> &Self::Target { &self.0 } } const PART_1_CONST: isize = 2000000; const PART_2_CONST: isize = 4000000; fn main() { run(); } fn run() { let contents = fs::read_to_string("15").unwrap(); process_part_1(contents.lines()); process_part_2(contents.lines()); } fn process_part_2<'a, T>(lines: T) where T: IntoIterator, { let (sensors, worker_bundles) = prep_work(lines); let mut handles = Vec::new(); let bundle_storage = Arc::new(worker_bundles); let sensor_storage = Arc::new(sensors); for worker in 0..=99 { let worker_bundles = Arc::clone(&bundle_storage); let sensors = Arc::clone(&sensor_storage); let bounds = 0..=PART_2_CONST; let handle = thread::spawn(move || do_part2_work(worker_bundles, worker, &bounds, sensors)); handles.push(handle); } handles.into_iter().for_each(|handle| { handle.join(); }); } fn prep_work<'a, T>(lines: T) -> (Vec, HashMap>) where T: IntoIterator, { let (sensors, _) = parse(lines); let mut worker_bundles = HashMap::new(); for worker in 0..99 { let mut work_item = Vec::new(); for sensor in &sensors { let chunk_s = sensor.visibility / 100; work_item.push(chunk_s); } worker_bundles.insert(worker, work_item); } // remainder worker let worker = 99; let mut work_item = Vec::new(); for sensor in &sensors { let chunk_s = sensor.visibility / 100; let remainder = sensor.visibility - (99 * chunk_s); work_item.push(remainder); } worker_bundles.insert(worker, work_item); (sensors, worker_bundles) } fn do_part2_work( worker_bundles: Arc>>, worker: isize, bounds: &RangeInclusive, sensors: Arc>, ) { worker_bundles.get(&worker).and_then(|x| { for (work_idx, work_item) in x.into_iter().enumerate() { for idx in 0..*work_item { let dist = isize::try_from(idx).unwrap() + worker * isize::try_from(*work_item).unwrap(); let sensor = &sensors[work_idx]; let visibility = isize::try_from(sensor.visibility).unwrap(); let x_left = sensor.loc.x - dist; let x_right = sensor.loc.x + dist; let y_lower = sensor.loc.y + (visibility - dist) + 1; let y_upper = sensor.loc.y - (visibility - dist) - 1; [ (x_left, y_upper), (x_right, y_upper), (x_left, y_lower), (x_right, y_lower), ] .into_iter() .for_each(|coord| { if !(bounds).contains(&coord.0) || !(bounds).contains(&coord.1) { return; } if sensors.iter().all(|tgt_sensor| { &tgt_sensor.loc - &Location { x: coord.0, y: coord.1, } > isize::try_from(tgt_sensor.visibility).unwrap() }) { println!("{:?}", coord.0 * 4000000 + coord.1); process::exit(0); } }) } } Some(()) }); } fn process_part_1<'a, T>(lines: T) where T: IntoIterator, { let (sensors, beacons) = parse(lines); let target_row = PART_1_CONST; let mut impossible_ranges: Vec = Vec::new(); for sensor in (&sensors).into_iter() { let visibility = isize::try_from(sensor.visibility).unwrap(); let dist_to_target = &sensor.loc - &(Location { x: sensor.loc.x, y: target_row, }); if dist_to_target > visibility { continue; } let impossible_width = visibility - dist_to_target; let impossible_range = sensor.loc.x - impossible_width..=sensor.loc.x + impossible_width; impossible_ranges.push(ComparableRange(impossible_range)); } impossible_ranges.sort_by(|a, b| a.start().cmp(b.start())); merge(&mut impossible_ranges); let beacons_in_row = beacons .into_iter() .filter(|b| { if b.loc.y != target_row { return false; } (&impossible_ranges) .into_iter() .any(|r| r.contains(&b.loc.x)) }) .count(); println!( "{}", impossible_ranges .into_iter() .map(|r| r.end() - r.start() + 1) .sum::() - isize::try_from(beacons_in_row).unwrap() ); } fn merge(impossible_ranges: &mut Vec) { let mut merge_idx = 0; while merge_idx < impossible_ranges.len() - 1 { if (*impossible_ranges)[merge_idx].intersects(&impossible_ranges[merge_idx + 1]) { let r1 = &impossible_ranges[merge_idx]; let r2 = &impossible_ranges[merge_idx + 1]; impossible_ranges.insert(merge_idx + 2, r1.intersection(&r2)); impossible_ranges.remove(merge_idx + 1); impossible_ranges.remove(merge_idx); } else { merge_idx += 1; } } } fn parse<'a, T>(lines: T) -> (Vec, Vec) where T: IntoIterator, { let mut sensors = Vec::new(); let mut beacons = Vec::new(); let extract_ints = |s: &str| { s.split(&['=', ',', ':'][..]) .filter_map(|s| s.parse::().ok()) .collect::>() }; let loc_sets = lines.into_iter().map(extract_ints); loc_sets.for_each(|loc_set| { let sensor_loc = Location { x: loc_set[0], y: loc_set[1], }; let beacon_loc = Location { x: loc_set[2], y: loc_set[3], }; let visibility = usize::try_from(&sensor_loc - &beacon_loc).unwrap(); sensors.push(Sensor { loc: sensor_loc, visibility: visibility, }); let beacon = Beacon { loc: beacon_loc }; if !beacons.contains(&beacon) { beacons.push(beacon); } }); (sensors, beacons) }