You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
127 lines
3.4 KiB
Rust
127 lines
3.4 KiB
Rust
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::<Vec<Sensor>>();
|
|
|
|
// 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::<Vec<(isize, isize)>>();
|
|
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::<Vec<isize>>();
|
|
|
|
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<Sensor>) -> Range<isize> {
|
|
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
|
|
}
|