Files
contests/advent_of_code/2022/15/src/main.rs

177 lines
4.6 KiB
Rust

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<i32>,
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<Event, (i32, i32)> = 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;
}
}