Skip to content

Commit 0213d24

Browse files
committed
Solved day 17 of 2021
1 parent 837219a commit 0213d24

File tree

4 files changed

+112
-8
lines changed

4 files changed

+112
-8
lines changed

2021/input/day17.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
target area: x=240..292, y=-90..-57

2021/input/day17_example.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
target area: x=20..30, y=-10..-5

2021/src/bin/day17.rs

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
#![feature(test)]
2+
3+
use advent_lib::{parsing::range_inclusive, *};
4+
use fxhash::{FxHashMap, FxHashSet};
5+
use nom_parse_macros::parse_from;
6+
use std::{cmp::max, ops::RangeInclusive};
7+
8+
#[parse_from(preceded("target area: ", (preceded("x=", range_inclusive(i32, "..")), preceded(", y=", range_inclusive(i32, "..")))))]
9+
struct Input {
10+
x_range: RangeInclusive<i32>,
11+
y_range: RangeInclusive<i32>,
12+
}
13+
14+
fn calculate_part1(input: &Input) -> i32 {
15+
let dy = -input.y_range.start() - 1;
16+
(dy * (dy + 1)) / 2
17+
}
18+
19+
fn calculate_part2(input: &Input) -> usize {
20+
let mut vx_start_times = FxHashMap::<i32, RangeInclusive<i32>>::default();
21+
22+
for vx_start in 1..=*input.x_range.end() {
23+
let mut vx = vx_start;
24+
let mut x = 0;
25+
let mut min_time = None;
26+
for time in 1.. {
27+
x += vx;
28+
vx = max(0, vx - 1);
29+
if x < *input.x_range.start() {
30+
if vx == 0 {
31+
break;
32+
} else {
33+
continue;
34+
}
35+
} else if x > *input.x_range.end() {
36+
if let Some(min_time) = min_time {
37+
vx_start_times.insert(vx_start, min_time..=(time - 1));
38+
}
39+
break;
40+
} else if min_time.is_none() {
41+
min_time = Some(time);
42+
} else if vx == 0 {
43+
if let Some(min_time) = min_time {
44+
vx_start_times.insert(vx_start, min_time..=i32::MAX);
45+
}
46+
break;
47+
}
48+
}
49+
}
50+
51+
let mut speeds = FxHashSet::<(i32, i32)>::default();
52+
for vy_start in *input.y_range.start()..(-*input.y_range.start()) {
53+
// This is time spend in the air before crossing crossing the 0 line again
54+
let (mut y, mut vy, extra_time) = if vy_start > 0 {
55+
(0, -vy_start - 1, vy_start * 2 + 1)
56+
} else {
57+
(0, vy_start, 0)
58+
};
59+
60+
for time in (extra_time + 1).. {
61+
y += vy;
62+
vy -= 1;
63+
if y > *input.y_range.end() {
64+
continue;
65+
} else if y < *input.y_range.start() {
66+
break; // We passed the area, no use looking further
67+
} else {
68+
// Found a time that works, now find the related vx_start values
69+
vx_start_times
70+
.iter()
71+
.filter(|(_, range)| range.contains(&time))
72+
.for_each(|(vx_start, _)| {
73+
speeds.insert((*vx_start, vy_start));
74+
});
75+
}
76+
}
77+
}
78+
79+
speeds.len()
80+
}
81+
82+
day_main!(Input);
83+
84+
day_test!( 17, example => 45, 112 );
85+
day_test!( 17 => 4005, 2953 );

shared/src/parsing.rs

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ use nom::{AsBytes, AsChar, Compare, Err, Finish, IResult, Input, Parser};
99
use nom_parse_trait::ParseFrom;
1010
use smallvec::SmallVec;
1111
use std::hash::Hash;
12+
use std::ops::RangeInclusive;
1213

1314
pub fn handle_parser_error<T>(input: &[u8]) -> T
1415
where
@@ -49,16 +50,16 @@ pub fn find_many_skipping_unknown<I: Input, E: ParseError<I>, T: ParseFrom<I, E>
4950
}
5051
}
5152

52-
pub fn many_1_n<const MAX: usize, Input, Output, Error, ParserFunction>(
53+
pub fn many_1_n<const MAX: usize, I, Output, E, ParserFunction>(
5354
mut parser: ParserFunction,
54-
) -> impl FnMut(Input) -> IResult<Input, SmallVec<[Output; MAX]>, Error>
55+
) -> impl Parser<I, Output = SmallVec<[Output; MAX]>, Error = E>
5556
where
56-
Input: nom::Input,
57-
ParserFunction: Parser<Input, Output = Output, Error = Error>,
58-
Error: ParseError<Input>,
57+
I: Input,
58+
ParserFunction: Parser<I, Output = Output, Error = E>,
59+
E: ParseError<I>,
5960
[Output; MAX]: smallvec::Array<Item = Output>,
6061
{
61-
move |mut input: Input| {
62+
move |mut input: I| {
6263
let mut result = SmallVec::new();
6364
while result.len() < MAX {
6465
let input_len_before = input.input_len();
@@ -68,10 +69,10 @@ where
6869
Ok((next_input, output)) => {
6970
// infinite loop check: the parser must always consume
7071
if next_input.input_len() == input_len_before {
71-
return Err(Err::Error(Error::from_error_kind(input, ErrorKind::Many1)));
72+
return Err(Err::Error(E::from_error_kind(input, ErrorKind::Many1)));
7273
}
7374
if result.len() == MAX {
74-
return Err(Err::Error(Error::from_error_kind(input, ErrorKind::ManyMN)));
75+
return Err(Err::Error(E::from_error_kind(input, ErrorKind::ManyMN)));
7576
}
7677

7778
input = next_input;
@@ -355,3 +356,19 @@ fn hex_decode(c: char) -> Option<u8> {
355356
_ => None,
356357
}
357358
}
359+
360+
pub fn range_inclusive<I, E: ParseError<I>, T, F, G>(
361+
value: F,
362+
separator: G,
363+
) -> impl Parser<I, Output = RangeInclusive<T>, Error = E>
364+
where
365+
F: Parser<I, Output = T, Error = E> + Clone,
366+
G: Parser<I, Error = E>,
367+
I: Input,
368+
<I as Input>::Item: AsChar,
369+
for<'a> I: Compare<&'a [u8]>,
370+
{
371+
map(separated_pair(value.clone(), separator, value), |(l, r)| {
372+
l..=r
373+
})
374+
}

0 commit comments

Comments
 (0)