63 lines
1.6 KiB
Rust
63 lines
1.6 KiB
Rust
#![feature(iterator_try_reduce)]
|
|
#![warn(clippy::pedantic)]
|
|
|
|
use std::{
|
|
collections::BTreeSet,
|
|
io::{stdin, Read},
|
|
};
|
|
|
|
use aoc::vec2::{Direction, Vec2};
|
|
|
|
fn knot_movement(previous: Vec2, knot: Vec2) -> Option<Vec2> {
|
|
let (dx, dy) = (previous - knot).into();
|
|
|
|
if dx.abs() <= 1 && dy.abs() <= 1 {
|
|
None
|
|
} else {
|
|
Some((dx.signum(), dy.signum()).into())
|
|
}
|
|
}
|
|
|
|
fn simulate_knots(num_knots: usize, instructions: &[(Direction, usize)]) -> usize {
|
|
const START: Vec2 = Vec2::new(0, 0);
|
|
|
|
assert!(num_knots >= 2);
|
|
|
|
let mut knots = vec![START; num_knots];
|
|
let mut tail_positions = BTreeSet::new();
|
|
tail_positions.insert(START);
|
|
|
|
for (direction, distance) in instructions {
|
|
for _i in 0..*distance {
|
|
knots[0] += Vec2::from(*direction);
|
|
|
|
if let Some(tail) = knots.iter_mut().try_reduce(|previous, knot| {
|
|
*knot += knot_movement(*previous, *knot)?;
|
|
Some(knot)
|
|
}) {
|
|
tail_positions.insert(tail.copied().unwrap());
|
|
}
|
|
}
|
|
}
|
|
|
|
tail_positions.len()
|
|
}
|
|
|
|
fn main() {
|
|
let mut data = String::new();
|
|
stdin().read_to_string(&mut data).unwrap();
|
|
|
|
let instructions: Vec<_> = data
|
|
.lines()
|
|
.map(|line| {
|
|
let (direction, distance) = line.split_once(' ').unwrap();
|
|
let direction = direction.parse().unwrap();
|
|
let distance = distance.parse().unwrap();
|
|
(direction, distance)
|
|
})
|
|
.collect();
|
|
|
|
println!("{:?}", simulate_knots(2, &instructions));
|
|
println!("{:?}", simulate_knots(10, &instructions));
|
|
}
|