[rs] Optimize 2022_15
This commit is contained in:
parent
11d099b8c8
commit
ff64e5db06
1 changed files with 80 additions and 16 deletions
|
|
@ -1,5 +1,6 @@
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
struct Sensor {
|
struct Sensor {
|
||||||
pos: (i32, i32),
|
pos: (i32, i32),
|
||||||
beac: (i32, i32),
|
beac: (i32, i32),
|
||||||
|
|
@ -14,6 +15,12 @@ fn parse_coord(coord: &str) -> i32 {
|
||||||
fn line_ranges(sensors: &[Sensor], y: i32) -> Vec<(i32, i32)> {
|
fn line_ranges(sensors: &[Sensor], y: i32) -> Vec<(i32, i32)> {
|
||||||
let mut raw_ranges = vec![];
|
let mut raw_ranges = vec![];
|
||||||
for sensor in sensors {
|
for sensor in sensors {
|
||||||
|
// A beacon exactly dist away has 1 field, i. e. x±0
|
||||||
|
// A beacon exactly dist-1 away has 3 fields, i. e. x±1
|
||||||
|
// ...
|
||||||
|
// A beacon exactly 0 away has 2dist+1 fields, i. e. x±dist
|
||||||
|
//
|
||||||
|
// Formula: Go from x-(dist-dy) to x+(dist-dy)
|
||||||
let dy = (y - sensor.pos.1).abs();
|
let dy = (y - sensor.pos.1).abs();
|
||||||
if dy > sensor.dist {
|
if dy > sensor.dist {
|
||||||
continue;
|
continue;
|
||||||
|
|
@ -39,16 +46,49 @@ fn line_ranges(sensors: &[Sensor], y: i32) -> Vec<(i32, i32)> {
|
||||||
ranges
|
ranges
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn intersect_lines(tlbr: (i32, i32, i32, i32), trbl: (i32, i32, i32, i32)) -> Option<(i32, i32)> {
|
||||||
|
// For tlbr lines, x1 - y1 = x2 - y2 = x - y = c.
|
||||||
|
// For trbl lines, x1 + y1 = x2 + y2 = x + y = c.
|
||||||
|
assert_eq!(tlbr.0 - tlbr.1, tlbr.2 - tlbr.3);
|
||||||
|
assert_eq!(trbl.0 + trbl.1, trbl.2 + trbl.3);
|
||||||
|
let c_tlbr = tlbr.0 - tlbr.1;
|
||||||
|
let c_trbl = trbl.0 + trbl.1;
|
||||||
|
|
||||||
|
// Find x, y such that
|
||||||
|
// x - y = c_tlbr
|
||||||
|
// x + y = c_trbl
|
||||||
|
//
|
||||||
|
// 2x = c_tlbr + c_trbl
|
||||||
|
// y = c_trbl - x
|
||||||
|
let two_x = c_tlbr + c_trbl;
|
||||||
|
if two_x.rem_euclid(2) != 0 {
|
||||||
|
return None; // Intersection not at integer coordinates
|
||||||
|
}
|
||||||
|
let x = two_x / 2;
|
||||||
|
let y = c_trbl - x;
|
||||||
|
assert_eq!(x - y, c_tlbr);
|
||||||
|
assert_eq!(x + y, c_trbl);
|
||||||
|
|
||||||
|
// Finally, check if the result is on both line segments. We only need to
|
||||||
|
// check one dimension per line segment.
|
||||||
|
let on_tlbr = tlbr.1 <= y && y <= tlbr.3;
|
||||||
|
let on_trbl = trbl.1 <= y && y <= trbl.3;
|
||||||
|
if on_tlbr && on_trbl {
|
||||||
|
Some((x, y))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn covered_by(sensor: Sensor, pos: (i32, i32)) -> bool {
|
||||||
|
let dist = (pos.0 - sensor.pos.0).abs() + (pos.1 - sensor.pos.1).abs();
|
||||||
|
dist <= sensor.dist
|
||||||
|
}
|
||||||
|
|
||||||
pub fn solve(input: String) {
|
pub fn solve(input: String) {
|
||||||
let sensors = input
|
let sensors = input
|
||||||
.lines()
|
.lines()
|
||||||
.map(|l| {
|
.map(|l| {
|
||||||
// A beacon exactly dist away has 1 field, i. e. x±0
|
|
||||||
// A beacon exactly dist-1 away has 3 fields, i. e. x±1
|
|
||||||
// ...
|
|
||||||
// A beacon exactly 0 away has 2dist+1 fields, i. e. x±dist
|
|
||||||
//
|
|
||||||
// Formula: Go from x-(dist-dy) to x+(dist-dy)
|
|
||||||
let parts = l.split_whitespace().collect::<Vec<_>>();
|
let parts = l.split_whitespace().collect::<Vec<_>>();
|
||||||
let pos = (parse_coord(parts[2]), parse_coord(parts[3]));
|
let pos = (parse_coord(parts[2]), parse_coord(parts[3]));
|
||||||
let beac = (parse_coord(parts[8]), parse_coord(parts[9]));
|
let beac = (parse_coord(parts[8]), parse_coord(parts[9]));
|
||||||
|
|
@ -75,16 +115,40 @@ pub fn solve(input: String) {
|
||||||
}
|
}
|
||||||
println!("Part 1: {part1}");
|
println!("Part 1: {part1}");
|
||||||
|
|
||||||
for y in 0..=4000000 {
|
let mut lines_tlbr = vec![];
|
||||||
let mut ranges = line_ranges(&sensors, y);
|
let mut lines_trbl = vec![];
|
||||||
ranges.retain(|(s, e)| *s <= 4000000 && 0 <= *e);
|
for sensor in &sensors {
|
||||||
if ranges.len() != 1 {
|
let d = sensor.dist;
|
||||||
// Found our beacon!
|
let p = sensor.pos;
|
||||||
assert_eq!(ranges.len(), 2);
|
lines_tlbr.push((p.0, p.1 - d - 1, p.0 + d + 1, p.1));
|
||||||
let x = ranges[0].1 + 1;
|
lines_tlbr.push((p.0 - d - 1, p.1, p.0, p.1 + d + 1));
|
||||||
let tuning_frequency = x as i64 * 4000000 + y as i64;
|
lines_trbl.push((p.0, p.1 - d - 1, p.0 - d - 1, p.1));
|
||||||
println!("Part 2: {tuning_frequency}");
|
lines_trbl.push((p.0 + d + 1, p.1, p.0, p.1 + d + 1));
|
||||||
break;
|
}
|
||||||
|
|
||||||
|
for tlbr in &lines_tlbr {
|
||||||
|
for trbl in &lines_trbl {
|
||||||
|
if let Some(intersect) = intersect_lines(*tlbr, *trbl) {
|
||||||
|
let x_in_bounds = 0 <= intersect.0 && intersect.0 <= 4000000;
|
||||||
|
let y_in_bounds = 0 <= intersect.1 && intersect.1 <= 4000000;
|
||||||
|
if !x_in_bounds || !y_in_bounds {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let mut covered = false;
|
||||||
|
for sensor in &sensors {
|
||||||
|
if covered_by(*sensor, intersect) {
|
||||||
|
covered = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if covered {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// We found our candidate :)
|
||||||
|
let tuning_frequency = intersect.0 as i64 * 4000000 + intersect.1 as i64;
|
||||||
|
println!("Part 2: {tuning_frequency}");
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue