diff --git a/2022/inputs/15.txt b/2022/inputs/15.txt new file mode 100644 index 0000000..4d7a5bd --- /dev/null +++ b/2022/inputs/15.txt @@ -0,0 +1,24 @@ +Sensor at x=3988693, y=3986119: closest beacon is at x=3979063, y=3856315 +Sensor at x=1129181, y=241785: closest beacon is at x=1973630, y=-98830 +Sensor at x=2761889, y=2453622: closest beacon is at x=2803715, y=2643139 +Sensor at x=3805407, y=3099635: closest beacon is at x=3744251, y=2600851 +Sensor at x=3835655, y=3999745: closest beacon is at x=3979063, y=3856315 +Sensor at x=3468377, y=3661078: closest beacon is at x=3979063, y=3856315 +Sensor at x=1807102, y=3829998: closest beacon is at x=2445544, y=3467698 +Sensor at x=2774374, y=551040: closest beacon is at x=1973630, y=-98830 +Sensor at x=2004588, y=2577348: closest beacon is at x=2803715, y=2643139 +Sensor at x=2949255, y=3611925: closest beacon is at x=2445544, y=3467698 +Sensor at x=2645982, y=3991988: closest beacon is at x=2445544, y=3467698 +Sensor at x=3444780, y=2880445: closest beacon is at x=3744251, y=2600851 +Sensor at x=3926452, y=2231046: closest beacon is at x=3744251, y=2600851 +Sensor at x=3052632, y=2882560: closest beacon is at x=2803715, y=2643139 +Sensor at x=3994992, y=2720288: closest beacon is at x=3744251, y=2600851 +Sensor at x=3368581, y=1443706: closest beacon is at x=3744251, y=2600851 +Sensor at x=2161363, y=1856161: closest beacon is at x=1163688, y=2000000 +Sensor at x=3994153, y=3414445: closest beacon is at x=3979063, y=3856315 +Sensor at x=2541906, y=2965730: closest beacon is at x=2803715, y=2643139 +Sensor at x=600169, y=3131140: closest beacon is at x=1163688, y=2000000 +Sensor at x=163617, y=1082438: closest beacon is at x=1163688, y=2000000 +Sensor at x=3728368, y=140105: closest beacon is at x=3732654, y=-724773 +Sensor at x=1187681, y=2105247: closest beacon is at x=1163688, y=2000000 +Sensor at x=2327144, y=3342616: closest beacon is at x=2445544, y=3467698 diff --git a/2022/src/common.rs b/2022/src/common.rs index 085cf22..c9eebf8 100644 --- a/2022/src/common.rs +++ b/2022/src/common.rs @@ -180,6 +180,12 @@ impl IndexSet { #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] pub struct Vec2(pub [i32; 2]); +impl Vec2 { + pub fn l1(self) -> i32 { + self.0.into_iter().map(i32::abs).sum() + } +} + impl Add for Vec2 { type Output = Self; diff --git a/2022/src/day15.rs b/2022/src/day15.rs index acd2238..4cd5310 100644 --- a/2022/src/day15.rs +++ b/2022/src/day15.rs @@ -1,9 +1,109 @@ -use anyhow::Result; +use std::cmp::Reverse; +use std::collections::BinaryHeap; -pub fn part1(_input: &[u8]) -> Result { - anyhow::bail!("not implemented") +use ahash::AHashSet; +use anyhow::Result; +use nom::bytes::complete::tag; +use nom::character::complete::newline; +use nom::combinator::map; +use nom::multi::many0; +use nom::sequence::pair; +use nom::sequence::preceded; +use nom::sequence::terminated; +use nom::IResult; + +use crate::common::parse_input; +use crate::common::Vec2; + +struct Beacon { + pos: Vec2, + closest: Vec2, +} + +fn parse_pos(input: &[u8]) -> IResult<&[u8], Vec2> { + use nom::character::complete::i32; + map( + pair(preceded(tag("x="), i32), preceded(tag(", y="), i32)), + |(x, y)| Vec2([x, y]), + )(input) +} + +fn parse_beacons(input: &[u8]) -> IResult<&[u8], Vec> { + let parse_beacon = map( + pair( + preceded(tag("Sensor at "), parse_pos), + preceded(tag(": closest beacon is at "), parse_pos), + ), + |(pos, closest)| Beacon { pos, closest }, + ); + + many0(terminated(parse_beacon, newline))(input) +} + +fn part1_generic(input: &[u8]) -> Result { + let beacons = parse_input(input, parse_beacons)?; + + let mut not_blocking_yet = BinaryHeap::new(); + let mut blocking = BinaryHeap::new(); + + let mut total = 0; + + let mut on_line = AHashSet::new(); + + for beacon in beacons { + let distance = (beacon.closest - beacon.pos).l1(); + let horizontal_distance = distance - (beacon.pos[1] - Y).abs(); + + if horizontal_distance >= 0 { + not_blocking_yet.push(Reverse(( + beacon.pos[0] - horizontal_distance, + beacon.pos[0] + horizontal_distance + 1, + ))) + } + + // Beacons can be beacons, so we should uncount them + if beacon.closest[1] == Y { + on_line.insert(beacon.closest); + } + } + + while let Some(Reverse((block_from, mut block_until))) = not_blocking_yet.pop() { + blocking.push(Reverse(block_until)); + + while let Some(Reverse(block_end)) = blocking.pop() { + block_until = block_until.max(block_end); + + while matches!(not_blocking_yet.peek(), Some(Reverse((block_from, _))) if block_from < &block_end) + { + let Reverse((_, additional_block_until)) = not_blocking_yet.pop().unwrap(); + blocking.push(Reverse(additional_block_until)); + } + } + + total += block_until - block_from; + } + + total -= on_line.len() as i32; + + Ok(total.to_string()) +} + +pub fn part1(input: &[u8]) -> Result { + part1_generic::<2000000>(input) } pub fn part2(_input: &[u8]) -> Result { anyhow::bail!("not implemented") } + +#[cfg(test)] +mod tests { + use super::*; + + const SAMPLE: &[u8] = include_bytes!("samples/15.txt"); + + #[test] + fn sample_part1() { + assert_eq!(part1_generic::<10>(SAMPLE).unwrap(), "26"); + } +} diff --git a/2022/src/samples/15.txt b/2022/src/samples/15.txt new file mode 100644 index 0000000..a612424 --- /dev/null +++ b/2022/src/samples/15.txt @@ -0,0 +1,14 @@ +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