mirror of
https://github.com/Smaug123/advent-of-code-2021
synced 2025-10-05 20:18:41 +00:00
Day 17 in Rust (#17)
This commit is contained in:
7
Cargo.lock
generated
7
Cargo.lock
generated
@@ -220,6 +220,13 @@ dependencies = [
|
|||||||
"criterion",
|
"criterion",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "day_17"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"criterion",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "day_2"
|
name = "day_2"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
@@ -15,4 +15,5 @@ members = [
|
|||||||
"day_13",
|
"day_13",
|
||||||
"day_14",
|
"day_14",
|
||||||
"day_16",
|
"day_16",
|
||||||
|
"day_17",
|
||||||
]
|
]
|
||||||
|
15
day_17/Cargo.toml
Normal file
15
day_17/Cargo.toml
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
[package]
|
||||||
|
name = "day_17"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["Smaug123 <patrick+github@patrickstevens.co.uk>"]
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
[dev-dependencies]
|
||||||
|
criterion = "0.3"
|
||||||
|
|
||||||
|
[[bench]]
|
||||||
|
name = "bench"
|
||||||
|
harness = false
|
19
day_17/benches/bench.rs
Normal file
19
day_17/benches/bench.rs
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
use criterion::{black_box, criterion_group, criterion_main, Criterion};
|
||||||
|
use day_17::day_17::{input, part_1, part_2};
|
||||||
|
|
||||||
|
fn criterion_benchmark(c: &mut Criterion) {
|
||||||
|
let input = input();
|
||||||
|
c.bench_function("day 17 part 1", |b| {
|
||||||
|
b.iter(|| {
|
||||||
|
black_box(part_1(&input));
|
||||||
|
})
|
||||||
|
});
|
||||||
|
c.bench_function("day 17 part 2", |b| {
|
||||||
|
b.iter(|| {
|
||||||
|
black_box(part_2(&input));
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
criterion_group!(benches, criterion_benchmark);
|
||||||
|
criterion_main!(benches);
|
34
day_17/day17.m
Normal file
34
day_17/day17.m
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
(* ::Package:: *)
|
||||||
|
|
||||||
|
nums=With[{chars=Characters@StringTrim@ReadString[StringJoin[NotebookDirectory[], "/input.txt"]]},Flatten[IntegerDigits[FromDigits[#,16],2,4]&/@chars]];
|
||||||
|
|
||||||
|
|
||||||
|
consumePacket[{}]={};
|
||||||
|
consumePacket[{v1_,v2_,v3_,t1_,t2_,t3_,otherBits___}]:=With[{packetVersion=Sow@FromDigits[{v1,v2,v3},2],typeId=FromDigits[{t1,t2,t3},2], rest={otherBits}},
|
||||||
|
Switch[typeId,
|
||||||
|
4,With[{thisPacket=TakeWhile[Partition[rest,UpTo@5],#[[1]]==1&]},
|
||||||
|
{FromDigits[Flatten[Join[Rest/@thisPacket,rest[[5 Length@thisPacket+2;;5 Length@thisPacket+5]]]],2],rest[[5 Length@thisPacket+6;;]]}
|
||||||
|
],
|
||||||
|
|
||||||
|
_,
|
||||||
|
With[{operator=
|
||||||
|
Switch[typeId,0,Plus,1,Times,2,Min,3,Max,5,Boole@*Greater,6,Boole@*Less,7,Boole@*Equal]
|
||||||
|
},
|
||||||
|
Switch[rest[[1]],
|
||||||
|
0,With[{subPacketLen=FromDigits[rest[[2;;16]],2]},
|
||||||
|
{operator@@consumePackets[rest[[17;;16+subPacketLen]]],rest[[17+subPacketLen;;]]}
|
||||||
|
],
|
||||||
|
1,With[{subPackets=FromDigits[rest[[2;;12]],2]},
|
||||||
|
With[{result=Nest[With[{c=consumePacket[#[[2]]]},{Join[#[[1]],{c[[1]]}],c[[2]]}]&,{{},rest[[13;;]]},subPackets]},
|
||||||
|
{operator@@result[[1]],result[[2]]}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
|
||||||
|
]
|
||||||
|
]]
|
||||||
|
|
||||||
|
consumePackets[l_]:=First@NestWhile[With[{c=consumePacket[#[[2]]]},{Join[#[[1]],{c[[1]]}],c[[2]]}]&,{{},l},Not@AllTrue[#[[2]],#==0&]&]
|
||||||
|
|
||||||
|
|
||||||
|
MapAt[Total@*Flatten,Reap@consumePackets[nums],{2}]
|
1
day_17/input.txt
Normal file
1
day_17/input.txt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
target area: x=269..292, y=-68..-44
|
219
day_17/src/lib.rs
Normal file
219
day_17/src/lib.rs
Normal file
@@ -0,0 +1,219 @@
|
|||||||
|
pub mod day_17 {
|
||||||
|
|
||||||
|
use std::cmp::{max, Ordering};
|
||||||
|
|
||||||
|
pub struct Data<T> {
|
||||||
|
min_x: T,
|
||||||
|
min_y: T,
|
||||||
|
max_x: T,
|
||||||
|
max_y: T,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn chomp_str<I>(chars: &mut I, expected: &str)
|
||||||
|
where
|
||||||
|
I: Iterator<Item = char>,
|
||||||
|
{
|
||||||
|
for e in expected.chars() {
|
||||||
|
let actual = chars.next();
|
||||||
|
match actual {
|
||||||
|
None => {
|
||||||
|
panic!("Expected a char, got none");
|
||||||
|
}
|
||||||
|
Some(actual) => {
|
||||||
|
if actual != e {
|
||||||
|
panic!("Expected {}, got {}", e, actual);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn chomp_int<I>(chars: &mut I) -> i32
|
||||||
|
where
|
||||||
|
I: Iterator<Item = char>,
|
||||||
|
{
|
||||||
|
let fst = chars.next().unwrap();
|
||||||
|
let (sign, start) = if fst == '-' {
|
||||||
|
let fst = chars.next().unwrap();
|
||||||
|
(-1, fst.to_digit(10).unwrap())
|
||||||
|
} else {
|
||||||
|
(1, fst.to_digit(10).unwrap())
|
||||||
|
};
|
||||||
|
let mut ans = start as i32;
|
||||||
|
|
||||||
|
for c in chars {
|
||||||
|
if ('0'..='9').contains(&c) {
|
||||||
|
ans = ans * 10 + (c as u8 - b'0') as i32;
|
||||||
|
} else {
|
||||||
|
return ans * sign;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ans * sign
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn parse(s: &str) -> Data<i32> {
|
||||||
|
let mut chars = s.chars();
|
||||||
|
chomp_str(&mut chars, "target area: x=");
|
||||||
|
let min_x = chomp_int(&mut chars);
|
||||||
|
chomp_str(&mut chars, ".");
|
||||||
|
let max_x = chomp_int(&mut chars);
|
||||||
|
chomp_str(&mut chars, " y=");
|
||||||
|
let min_y = chomp_int(&mut chars);
|
||||||
|
chomp_str(&mut chars, ".");
|
||||||
|
let max_y = chomp_int(&mut chars);
|
||||||
|
|
||||||
|
Data {
|
||||||
|
min_x,
|
||||||
|
max_x,
|
||||||
|
min_y,
|
||||||
|
max_y,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn input() -> Data<i32> {
|
||||||
|
parse(include_str!("../input.txt"))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
struct Vector<T> {
|
||||||
|
x: T,
|
||||||
|
y: T,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct State<T> {
|
||||||
|
position: Vector<T>,
|
||||||
|
velocity: Vector<T>,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn step(state: &State<i32>) -> State<i32> {
|
||||||
|
State {
|
||||||
|
position: Vector {
|
||||||
|
x: state.position.x + state.velocity.x,
|
||||||
|
y: state.position.y + state.velocity.y,
|
||||||
|
},
|
||||||
|
velocity: Vector {
|
||||||
|
x: match 0.cmp(&state.velocity.x) {
|
||||||
|
Ordering::Less => state.velocity.x - 1,
|
||||||
|
Ordering::Greater => state.velocity.x + 1,
|
||||||
|
Ordering::Equal => 0,
|
||||||
|
},
|
||||||
|
y: state.velocity.y - 1,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_in_area<T>(data: &Data<T>, position: &Vector<T>) -> bool
|
||||||
|
where
|
||||||
|
T: Ord,
|
||||||
|
{
|
||||||
|
data.min_x <= position.x
|
||||||
|
&& position.x <= data.max_x
|
||||||
|
&& data.min_y <= position.y
|
||||||
|
&& position.y <= data.max_y
|
||||||
|
}
|
||||||
|
|
||||||
|
fn hits_target(data: &Data<i32>, velocity: &Vector<i32>) -> Option<i32> {
|
||||||
|
// Continue until the x-velocity becomes zero.
|
||||||
|
let mut state = State {
|
||||||
|
position: Vector { x: 0, y: 0 },
|
||||||
|
velocity: velocity.clone(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut max_y_coord = 0;
|
||||||
|
let mut hit_area = false;
|
||||||
|
|
||||||
|
while state.velocity.x != 0 || state.position.y >= data.min_y {
|
||||||
|
state = step(&state);
|
||||||
|
if !hit_area && is_in_area(data, &state.position) {
|
||||||
|
hit_area = true;
|
||||||
|
}
|
||||||
|
max_y_coord = max(max_y_coord, state.position.y);
|
||||||
|
if (state.velocity.x > 0 && state.position.x > data.max_x)
|
||||||
|
|| (state.velocity.x < 0 && state.position.x < data.min_x)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if hit_area {
|
||||||
|
Some(max_y_coord)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sqrt(n: i32) -> i32 {
|
||||||
|
(n as f32).sqrt() as i32
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn part_1(data: &Data<i32>) -> u32 {
|
||||||
|
let mut max_y = 0;
|
||||||
|
// Which x-direction do we need to fire in?
|
||||||
|
if data.min_x > 0 {
|
||||||
|
// If we start with x-velocity n, then we reach at most coordinate n(n+1)/2
|
||||||
|
// so we only need to start from sqrt(2 v)
|
||||||
|
for x in sqrt(2 * data.min_x)..=data.max_x {
|
||||||
|
for y in data.min_y..=data.max_x {
|
||||||
|
match hits_target(data, &Vector { x, y }) {
|
||||||
|
None => {}
|
||||||
|
Some(max_y_new) => {
|
||||||
|
max_y = max(max_y_new, max_y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
panic!("Expected positive x - please implement if this happens");
|
||||||
|
}
|
||||||
|
|
||||||
|
max_y as u32
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn part_2(data: &Data<i32>) -> u64 {
|
||||||
|
let mut count = 0;
|
||||||
|
// Which x-direction do we need to fire in?
|
||||||
|
if data.min_x > 0 {
|
||||||
|
for x in sqrt(2 * data.min_x)..=data.max_x {
|
||||||
|
for y in data.min_y..=data.max_x {
|
||||||
|
match hits_target(data, &Vector { x, y }) {
|
||||||
|
None => {}
|
||||||
|
Some(_) => {
|
||||||
|
count += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
panic!("Expected positive x - please implement if this happens");
|
||||||
|
}
|
||||||
|
|
||||||
|
count
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::day_17::*;
|
||||||
|
|
||||||
|
static TEST_INPUT: &str = "target area: x=20..30, y=-10..-5";
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn part1_known() {
|
||||||
|
let data = parse(&TEST_INPUT);
|
||||||
|
assert_eq!(part_1(&data), 45);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn part2_known() {
|
||||||
|
let data = parse(&TEST_INPUT);
|
||||||
|
assert_eq!(part_2(&data), 112);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_day_17() {
|
||||||
|
let input = input();
|
||||||
|
assert_eq!(part_1(&input), 2278);
|
||||||
|
assert_eq!(part_2(&input), 996);
|
||||||
|
}
|
||||||
|
}
|
7
day_17/src/main.rs
Normal file
7
day_17/src/main.rs
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
use day_17::day_17::{input, part_1, part_2};
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let input = input();
|
||||||
|
println!("part 1 => {}", part_1(&input));
|
||||||
|
println!("part 2 => {}", part_2(&input));
|
||||||
|
}
|
Reference in New Issue
Block a user