use regex::Regex; use std::ops::Range; #[derive(Debug)] struct Sensor { location: (isize, isize), beacon: (isize, isize), reach: isize, } fn main() { let contents = std::fs::read_to_string("data.txt").expect("Failed to read file"); let sensors = contents.lines().map(parse).collect::>(); // Part 1 let row = 2000000; let mut reached = 0; let range = find_range(&sensors); for col in range { let p = (col, row); // if this cell has a beacon or sensor, it can' be here if sensors.iter().any(|s| s.beacon == p) { continue; } // if any of the beacons can reach this cell, count it if sensors.iter().any(|s| can_reach(s, p)) { reached += 1; } } println!("Reached {reached}"); // Part 2 let max = 4_000_000; // Look in a diamond shape just outside of each sensor's reach 'outer: for s in &sensors { let per = perimeter(&s) .into_iter() .filter(|c| c.0 <= max && c.0 >= 0 && c.1 <= max && c.1 >= 0) .collect::>(); for coord in per { if sensors.iter().any(|s| can_reach(s, coord)) { continue; } else { println!("Found it: {:?}", coord); let freq = coord.0 * 4000000 + coord.1; println!("Tuning Frequency: {:?}", freq); break 'outer; } } } } fn parse(str: &str) -> Sensor { let re = Regex::new(r"Sensor at x=(.*), y=(.*): closest beacon is at x=(.*), y=(.*)").unwrap(); let c = re.captures(str).unwrap(); let p = (1..=4) .map(|i| c.get(i).unwrap().as_str().parse().unwrap()) .collect::>(); let location = (p[0], p[1]); let beacon = (p[2], p[3]); let reach = distance(location, beacon); Sensor { location, beacon, reach, } } fn distance(p1: (isize, isize), p2: (isize, isize)) -> isize { let (x1, y1) = p1; let (x2, y2) = p2; (x1 - x2).abs() + (y1 - y2).abs() } fn can_reach(s: &Sensor, p: (isize, isize)) -> bool { distance(s.location, p) <= s.reach } fn find_range(sensors: &Vec) -> Range { let mut min = 0; let mut max = 0; for s in sensors { let leftmost = s.location.0 - s.reach; if leftmost < min { min = leftmost; } let rightmost = s.location.1 + s.reach; if rightmost > max { max = rightmost; } } min..max } fn perimeter(s: &Sensor) -> Vec<(isize, isize)> { let mut coords = vec![]; let length = s.reach + 2; // start on the left, then go clockwise around the sensor's range let (mut col, mut row) = (s.location.0 - s.reach - 1, s.location.1); for i in 0..=length { coords.push((col + i, row - i)); } // from the top, down and right (col, row) = (s.location.0, s.location.1 - s.reach - 1); for i in 0..=length { coords.push((col + i, row + i)); } // from the right, going down and left (col, row) = (s.location.0 + s.reach + 1, s.location.1); for i in 0..=length { coords.push((col - i, row + i)); } // from the bottom, going up and left (col, row) = (s.location.0, s.location.1 + s.reach + 1); for i in 0..=length { coords.push((col - i, row - i)); } coords }