day 15 done (partial clippy)
This commit is contained in:
8
Cargo.lock
generated
8
Cargo.lock
generated
@@ -43,6 +43,14 @@ dependencies = [
|
|||||||
"nom",
|
"nom",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "day15"
|
||||||
|
version = "2022.0.0"
|
||||||
|
dependencies = [
|
||||||
|
"itertools",
|
||||||
|
"nom",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "day2"
|
name = "day2"
|
||||||
version = "2022.0.0"
|
version = "2022.0.0"
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ members = [
|
|||||||
"day12",
|
"day12",
|
||||||
"day13",
|
"day13",
|
||||||
"day14",
|
"day14",
|
||||||
|
"day15",
|
||||||
]
|
]
|
||||||
resolver = "2"
|
resolver = "2"
|
||||||
|
|
||||||
|
|||||||
12
day15/Cargo.toml
Normal file
12
day15/Cargo.toml
Normal 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
24
day15/input.txt
Normal 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
266
day15/src/main.rs
Normal 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");
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user