day 15 done (partial clippy)

This commit is contained in:
Dylan Thies
2023-08-30 18:49:51 -04:00
parent fc8b9329e4
commit 33464d260f
5 changed files with 311 additions and 0 deletions

12
day15/Cargo.toml Normal file
View File

@@ -0,0 +1,12 @@
[package]
name = "day15"
version.workspace = true
edition.workspace = true
authors.workspace = true
repository.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
nom.workspace = true
itertools.workspace = true

24
day15/input.txt Normal file
View File

@@ -0,0 +1,24 @@
Sensor at x=3291456, y=3143280: closest beacon is at x=3008934, y=2768339
Sensor at x=3807352, y=3409566: closest beacon is at x=3730410, y=3774311
Sensor at x=1953670, y=1674873: closest beacon is at x=2528182, y=2000000
Sensor at x=2820269, y=2810878: closest beacon is at x=2796608, y=2942369
Sensor at x=3773264, y=3992829: closest beacon is at x=3730410, y=3774311
Sensor at x=2913793, y=2629579: closest beacon is at x=3008934, y=2768339
Sensor at x=1224826, y=2484735: closest beacon is at x=2528182, y=2000000
Sensor at x=1866102, y=3047750: closest beacon is at x=1809319, y=3712572
Sensor at x=3123635, y=118421: closest beacon is at x=1453587, y=-207584
Sensor at x=2530789, y=2254773: closest beacon is at x=2528182, y=2000000
Sensor at x=230755, y=3415342: closest beacon is at x=1809319, y=3712572
Sensor at x=846048, y=51145: closest beacon is at x=1453587, y=-207584
Sensor at x=3505756, y=3999126: closest beacon is at x=3730410, y=3774311
Sensor at x=2506301, y=3745758: closest beacon is at x=1809319, y=3712572
Sensor at x=1389843, y=957209: closest beacon is at x=1453587, y=-207584
Sensor at x=3226352, y=3670258: closest beacon is at x=3730410, y=3774311
Sensor at x=3902053, y=3680654: closest beacon is at x=3730410, y=3774311
Sensor at x=2573020, y=3217129: closest beacon is at x=2796608, y=2942369
Sensor at x=3976945, y=3871511: closest beacon is at x=3730410, y=3774311
Sensor at x=107050, y=209321: closest beacon is at x=1453587, y=-207584
Sensor at x=3931251, y=1787536: closest beacon is at x=2528182, y=2000000
Sensor at x=1637093, y=3976664: closest beacon is at x=1809319, y=3712572
Sensor at x=2881987, y=1923522: closest beacon is at x=2528182, y=2000000
Sensor at x=3059723, y=2540501: closest beacon is at x=3008934, y=2768339

266
day15/src/main.rs Normal file
View File

@@ -0,0 +1,266 @@
#![warn(clippy::all, clippy::pedantic)]
use std::{collections::BTreeMap, fmt::Display, fs, ops::RangeInclusive};
use itertools::Itertools;
use nom::{
bytes::complete::tag,
character::complete::{self, newline},
multi::separated_list1,
};
trait Pos {
fn get_pos(self) -> (i64, i64);
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Sensor {
pub x: i64,
pub y: i64,
pub strength: i64,
}
impl Sensor {
pub fn x_covereage_at_y(&self, y: i64) -> Option<RangeInclusive<i64>> {
let dist = (y - self.y).abs();
let n = self.strength - dist;
if n < 0 {
return None;
}
Some((self.x - n)..=(self.x + n))
}
pub fn coverage(&self) -> Vec<(i64, RangeInclusive<i64>)> {
((self.y - self.strength)..=(self.y + self.strength))
.map(|y| {
let Some(x_s) = self.x_covereage_at_y(y) else {
panic!()
};
(y, x_s)
})
.collect()
}
}
impl Pos for Sensor {
fn get_pos(self) -> (i64, i64) {
(self.x, self.y)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Beacon {
pub x: i64,
pub y: i64,
}
impl Pos for Beacon {
fn get_pos(self) -> (i64, i64) {
(self.x, self.y)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum Square {
Covered,
Beacon(Beacon),
Sensor(Sensor),
}
impl Square {
pub fn is_covered(&self) -> bool {
matches!(self, Self::Covered)
}
}
impl Display for Square {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"{}",
match self {
Self::Covered => '#',
Self::Beacon(_) => 'B',
Self::Sensor(_) => 'S',
}
)
}
}
impl From<&Sensor> for Square {
fn from(value: &Sensor) -> Self {
Self::Sensor(*value)
}
}
impl From<Sensor> for Square {
fn from(value: Sensor) -> Self {
Self::Sensor(value)
}
}
impl From<&Beacon> for Square {
fn from(value: &Beacon) -> Self {
Self::Beacon(*value)
}
}
impl From<Beacon> for Square {
fn from(value: Beacon) -> Self {
Self::Beacon(value)
}
}
fn parse_reading(input: &str) -> nom::IResult<&str, (Sensor, Beacon)> {
let (input, _) = tag("Sensor at x=")(input)?;
let (input, x_sensor) = complete::i64(input)?;
let (input, _) = tag(", y=")(input)?;
let (input, y_sensor) = complete::i64(input)?;
let (input, _) = tag(": closest beacon is at x=")(input)?;
let (input, x_beacon) = complete::i64(input)?;
let (input, _) = tag(", y=")(input)?;
let (input, y_beacon) = complete::i64(input)?;
let dist = (x_sensor - x_beacon).abs() + (y_sensor - y_beacon).abs();
Ok((
input,
(
Sensor {
x: x_sensor,
y: y_sensor,
strength: dist,
},
Beacon {
x: x_beacon,
y: y_beacon,
},
),
))
}
fn parse_output(input: &str) -> nom::IResult<&str, Vec<Square>> {
let (input, readings) = separated_list1(newline, parse_reading)(input)?;
let output = readings
.iter()
.flat_map(|(sensor, beacon)| vec![sensor.into(), beacon.into()])
.unique()
.collect();
Ok((input, output))
}
fn coverage_count_at_y(board: &[Square], y: i64) -> Vec<i64> {
board
.iter()
.filter_map(|square| match square {
Square::Sensor(sensor) => Some(sensor),
_ => None,
})
.filter_map(|sensor| sensor.x_covereage_at_y(y))
.flatten()
.unique()
.collect()
}
pub fn part1(board: &[Square], y: i64) -> String {
let pos_covered_on_y = coverage_count_at_y(board, y).len();
let obs_on_y = board
.iter()
.filter_map(|square| match square {
Square::Beacon(beacon) => {
if beacon.y == y {
Some(beacon.x)
} else {
None
}
}
Square::Sensor(sensor) => {
if sensor.y == y {
Some(sensor.x)
} else {
None
}
}
Square::Covered => None,
})
.unique()
.count();
(pos_covered_on_y - obs_on_y).to_string()
}
pub fn part2(board: &[Square], lower: i64, upper: i64) -> String {
let bb = board
.iter()
.filter_map(|square| match square {
Square::Sensor(x) => Some(x),
_ => None,
})
.flat_map(|square| {
square
.coverage()
.into_iter()
.filter(|(y, _)| y >= &lower && y <= &upper)
.map(|(y, x_s)| (y, *x_s.start().max(&lower)..=*x_s.end().min(&upper)))
})
.fold(BTreeMap::new(), |mut acc, (y, x_s)| {
acc.entry(y)
.and_modify(|x_range: &mut Vec<RangeInclusive<i64>>| x_range.push(x_s.clone()))
.or_insert(vec![x_s]);
acc
});
let (x, y) = bb
.into_iter()
.find_map(|(y, mut x_s)| {
x_s.sort_by(|a, b| a.start().cmp(b.start()));
x_s.iter()
.fold(
(lower..=lower, None),
|mut acc: (RangeInclusive<i64>, Option<i64>), x_range: &RangeInclusive<i64>| {
if acc.1.is_some() {
return acc;
}
if acc.0.end() + 1 >= *x_range.start() {
acc.0 = *acc.0.start()..=*acc.0.end().max(x_range.end());
} else {
acc.1 = Some(acc.0.end() + 1);
}
acc
},
)
.1
.map(|x| (x, y))
})
.unwrap();
(4_000_000 * x + y).to_string()
}
fn main() {
let file = fs::read_to_string("./input.txt").unwrap();
let (_, board) = parse_output(&file).unwrap();
let y = 2_000_000;
println!("Part 1: {}", part1(&board, y));
println!("Part 2: {}", part2(&board, 0, 4_000_000));
}
#[cfg(test)]
mod test {
use super::*;
const INPUT: &str = "Sensor at x=2, y=18: closest beacon is at x=-2, y=15
Sensor at x=9, y=16: closest beacon is at x=10, y=16
Sensor at x=13, y=2: closest beacon is at x=15, y=3
Sensor at x=12, y=14: closest beacon is at x=10, y=16
Sensor at x=10, y=20: closest beacon is at x=10, y=16
Sensor at x=14, y=17: closest beacon is at x=10, y=16
Sensor at x=8, y=7: closest beacon is at x=2, y=10
Sensor at x=2, y=0: closest beacon is at x=2, y=10
Sensor at x=0, y=11: closest beacon is at x=2, y=10
Sensor at x=20, y=14: closest beacon is at x=25, y=17
Sensor at x=17, y=20: closest beacon is at x=21, y=22
Sensor at x=16, y=7: closest beacon is at x=15, y=3
Sensor at x=14, y=3: closest beacon is at x=15, y=3
Sensor at x=20, y=1: closest beacon is at x=15, y=3";
#[test]
fn part1_works() {
let (_, board) = parse_output(INPUT).unwrap();
assert_eq!(part1(&board, 10), "26");
}
#[test]
fn part2_works() {
let (_, board) = parse_output(INPUT).unwrap();
assert_eq!(part2(&board, 0, 20), "56000011");
}
}