day15.rs (3191B)
1 use regex::Regex; 2 3 #[derive(Debug,Clone)] 4 struct Pos { 5 x: i32, 6 y: i32 7 } 8 9 #[derive(Debug)] 10 struct Sensor { 11 sensor_pos: Pos, 12 beacon_dist: u32 13 } 14 15 #[derive(Debug,Clone)] 16 struct LineSeg { 17 left: i32, 18 right: i32, // Exclusive! 19 } 20 21 impl LineSeg { 22 // fn len(&self) -> u32 { 23 // (self.right - self.left).try_into().unwrap() 24 // } 25 fn contains(&self, x: i32) -> bool { 26 self.left <= x && x < self.right 27 } 28 } 29 30 pub fn run(input: String) { 31 // Am i trying to be too clever, or not clever enough? 32 // Where's my search space? I don't need to search _every_ integer 0-4_000_000 right? I can just search the 33 // edges of each beacon's outline surely. 34 // But that's already what I'm doing -- 35 let re = Regex::new(r"Sensor at x=([-\d]+), y=([-\d]+): closest beacon is at x=([-\d]+), y=([-\d]+)").unwrap(); 36 let search_y = 2_890_000..4_000_000; // Cheat, narrow the search space... 37 let search_x = 0..4_000_000; 38 39 let sens: Vec<Sensor> = input.lines().map(|line| { 40 let cap = re.captures(line).expect("line didn't match regex!"); 41 let sensor_x: i32 = cap[1].parse().expect("sx wasn't an i32!"); 42 let sensor_y: i32 = cap[2].parse().expect("sy wasn't an i32!"); 43 let beacon_x: i32 = cap[3].parse().expect("bx wasn't an i32!"); 44 let beacon_y: i32 = cap[4].parse().expect("by wasn't an i32!"); 45 let beacon_dist = sensor_x.abs_diff(beacon_x)+sensor_y.abs_diff(beacon_y); 46 Sensor { 47 sensor_pos: Pos { x: sensor_x, y: sensor_y }, 48 beacon_dist, 49 } 50 }).collect(); 51 52 let (x, y) = search_y.into_iter().filter_map(|y| { 53 // if y % 10_000 == 0 { 54 // println!("checking y {}", y); 55 // } 56 let mut line_segments: Vec<LineSeg> = sens.iter().flat_map(|sen| { 57 // Map it to a line segment on the target line 58 let target_y_dist = sen.sensor_pos.y.abs_diff(y); 59 if target_y_dist >= sen.beacon_dist { 60 // It's too far from the target line entirely 61 None 62 } else { 63 // Reach along the line is the remaining beacon dist after reachig target y 64 let reach = sen.beacon_dist - target_y_dist; 65 let l = sen.sensor_pos.x-(reach as i32); 66 let r = sen.sensor_pos.x+(reach as i32); 67 //println!("{:?} yd {} reach {} l {} r {}", sen, yd, reach, l,r); 68 Some(LineSeg{left: l,right: r}) 69 } 70 }).collect(); 71 line_segments.sort_by(|ls1,ls2| {ls1.left.cmp(&ls2.left)}); // not sure if this is necessary? 72 // Iterate to find the gap, skipping forward rather than basically iterating 73 let mut ix:i32 = 0; 74 'lp: while ix < search_x.end { 75 for l in &line_segments { 76 if l.contains(ix) { 77 ix = l.right; 78 continue 'lp 79 } 80 } 81 // No line segment contained this point, so it's our answer! 82 return Some((ix+1, y)); 83 } 84 None 85 }).next().expect("no point found!"); 86 println!("Day 15: {}", (x as i64)*4_000_000 + (y as i64)); 87 }