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

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
}