use nom::{ bytes::streaming::tag, character::complete::{self, multispace1}, multi::separated_list1, sequence::{preceded, separated_pair}, IResult, }; use priority_queue::PriorityQueue; use std::collections::HashSet; fn parse_input(input: &str) -> IResult<&str, Vec<((i32, i32), (i32, i32))>> { let (input, coords) = separated_list1( multispace1, preceded( tag("Sensor at x="), separated_pair( separated_pair(complete::i32, tag(", y="), complete::i32), tag(": closest beacon is at x="), separated_pair(complete::i32, tag(", y="), complete::i32), ), ), )(input)?; Ok((input, coords)) } fn manhattan_dist(a: (i32, i32), b: (i32, i32)) -> i32 { return (a.0 - b.0).abs() + (a.1 - b.1).abs(); } fn _fill_map( beacon_map: &mut HashSet<(i32, i32)>, sensor_coord: (i32, i32), beacon_coord: (i32, i32), ) { let dist = manhattan_dist(sensor_coord, beacon_coord); let (sx, sy) = sensor_coord; for i in 0..=dist { for j in 0..=i { beacon_map.insert((sx + i, sy + j)); beacon_map.insert((sx - i, sy + j)); beacon_map.insert((sx + i, sy - j)); beacon_map.insert((sx - i, sy - j)); } } } fn fill_line( beacon_line: &mut HashSet, sensor_coord: (i32, i32), beacon_coord: (i32, i32), test_line: i32, ) { let dist = manhattan_dist(sensor_coord, beacon_coord); let (sx, sy) = sensor_coord; let cross_section_len = dist - (test_line - sy).abs(); if cross_section_len >= 0 { for i in 0..=cross_section_len { beacon_line.insert(sx + i); beacon_line.insert(sx - i); } } } #[derive(PartialEq, Eq, Hash)] enum EventType { Start, Intersection, Middle, End, } #[derive(PartialEq, Eq, Hash)] struct Event { coord: (i32, i32), t: EventType, line_id: u32, } fn main() { let test_y = 2000000; let input_text = include_str!("../input.txt"); let (_rest, coord_list) = parse_input(input_text).unwrap(); // Testing the parsing // for ((sx, sy), (bx, by)) in coord_list { // println!("Sensor at x={sx}, y={sy}: closest beacon is at x={bx}, y={by}"); // } // Naive method // let mut beacon_map = HashSet::new(); // for (sensor, beacon) in coord_list { // fill_map(&mut beacon_map, sensor, beacon); // } // let positions = beacon_map.into_iter().filter(|(_, y)| *y == test_y).count(); // println!("Number of positions with y={test_y}: {positions}"); // A slightly less naive approach let mut beacon_line = HashSet::new(); for (sensor, beacon) in coord_list.clone() { fill_line(&mut beacon_line, sensor, beacon, test_y); } println!( "Number of positions with y={test_y}: {}", beacon_line.len() - 1 ); // Part 2 // // Ik denk dat dit misschien wel met een sweep line zou moeten kunnen. // Je weet immers waar alle ruiten liggen en je kan die hele mik sorteren. // Je krijgt dan 3 event-types: start van een lijnstuk, een kruising en het einde van een lijnstuk. // Enige is dat je alleen in de daadwerkelijke coordinaat-range zoals in de opgave dingen wilt // mee laten tellen. const MAX_COORD: i32 = 8000000; let mut event_queue: PriorityQueue = PriorityQueue::new(); let mut line_id = 0; for &(sensor, beacon) in coord_list.iter() { let sensor = (sensor.0 * 2, sensor.1 * 2); let beacon = (beacon.0 * 2, beacon.1 * 2); let dist = manhattan_dist(sensor, beacon); let begin = (sensor.0 + dist, sensor.1); let middle_1 = (sensor.0, sensor.1 - dist); let middle_2 = (sensor.0, sensor.1 + dist); let end = (sensor.0 - dist, sensor.1); event_queue.push( Event { coord: begin, t: EventType::Start, line_id, }, begin, ); event_queue.push( Event { coord: middle_1, t: EventType::Middle, line_id, }, middle_1, ); event_queue.push( Event { coord: middle_2, t: EventType::Middle, line_id, }, middle_2, ); event_queue.push( Event { coord: end, t: EventType::End, line_id, }, end, ); line_id += 1; } }