Rust Day 9

This commit is contained in:
Tobias Berger 2022-12-09 18:50:44 +01:00
parent a8cd0b0688
commit 7e9e3e63c3
Signed by: toby
GPG key ID: 2D05EFAB764D6A88
6 changed files with 2211 additions and 0 deletions

View file

@ -55,6 +55,10 @@ path = "src/day07/main.rs"
name = "day08"
path = "src/day08/main.rs"
[[bin]]
name = "day09"
path = "src/day09/main.rs"
[dependencies]
# Required for one of the day06 solutions (commented out)
# phf = { version = "0.11.1", features = ["macros"] }

2000
rust/src/day09/input.txt Normal file

File diff suppressed because it is too large Load diff

62
rust/src/day09/main.rs Normal file
View file

@ -0,0 +1,62 @@
#![feature(exclusive_range_pattern)]
const INPUT: &str = include_str!("input.txt");
mod part_1;
use part_1::part_1;
mod part_2;
use part_2::part_2;
#[derive(Clone, Copy, Debug)]
pub(crate) enum Direction {
Up,
Down,
Left,
Right,
}
impl From<Direction> for (i64, i64) {
fn from(value: Direction) -> Self {
match value {
Direction::Up => (0, 1),
Direction::Down => (0, -1),
Direction::Left => (-1, 0),
Direction::Right => (1, 0),
}
}
}
pub(crate) struct Move(Direction, i64);
impl Move {
fn from_string(string: &'static str) -> Self {
let (direction, distance) = string.split_at(2);
let direction = match direction.chars().next().unwrap() {
'U' => Direction::Up,
'D' => Direction::Down,
'L' => Direction::Left,
'R' => Direction::Right,
_ => panic!("Non-direction character found"),
};
Self(
direction,
distance
.parse()
.unwrap_or_else(|_| panic!("Found non-numeric input {distance}")),
)
}
}
pub(crate) fn distance_between_points(point_a: &(i64, i64), point_b: &(i64, i64)) -> (i64, i64) {
(point_a.0 - point_b.0, point_a.1 - point_b.1)
}
pub(crate) fn parse_input(input: &'static str) -> Vec<Move> {
input.lines().map(Move::from_string).collect()
}
pub fn main() {
let input = parse_input(INPUT);
part_1(&input);
part_2(&input);
}

55
rust/src/day09/part_1.rs Normal file
View file

@ -0,0 +1,55 @@
use std::collections::HashSet;
use crate::Move;
pub(crate) fn part_1(input: &Vec<Move>) -> usize {
let mut head_position = (0i64, 0i64);
let mut tail_position = (0i64, 0i64);
let mut visited_tail_positions = HashSet::new();
for head_movement in input.iter() {
for _ in 0..head_movement.1 {
match head_movement.0 {
crate::Direction::Up => head_position.1 += 1,
crate::Direction::Down => head_position.1 -= 1,
crate::Direction::Right => head_position.0 += 1,
crate::Direction::Left => head_position.0 -= 1,
}
let point_distance = crate::distance_between_points(&head_position, &tail_position);
match point_distance {
(2, 0) => tail_position.0 += 1,
(-2, 0) => tail_position.0 -= 1,
(0, 2) => tail_position.1 += 1,
(0, -2) => tail_position.1 -= 1,
(_, 2) => tail_position = (head_position.0, tail_position.1 + 1),
(2, _) => tail_position = (tail_position.0 + 1, head_position.1),
(_, -2) => tail_position = (head_position.0, tail_position.1 - 1),
(-2, _) => tail_position = (tail_position.0 - 1, head_position.1),
_ => {}
};
visited_tail_positions.insert(tail_position);
}
}
println!("Part 1: {:?}", visited_tail_positions.len());
visited_tail_positions.len()
}
#[cfg(test)]
mod tests {
const SAMPLE_INPUT: &str = include_str!("sample_input.txt");
#[test]
fn test_with_solution() {
assert_eq!(super::part_1(&crate::parse_input(crate::INPUT)), 0);
}
#[test]
fn test_with_sample_solution() {
assert_eq!(super::part_1(&crate::parse_input(SAMPLE_INPUT)), 13);
}
}

82
rust/src/day09/part_2.rs Normal file
View file

@ -0,0 +1,82 @@
use std::collections::HashSet;
use crate::Move;
const TAIL_LENGTH: usize = 9;
#[derive(Clone, Debug)]
pub(crate) struct Rope<const N: usize = 1> {
head: (i64, i64),
tails: [(i64, i64); N],
tail_history: HashSet<(i64, i64)>,
}
impl<const N: usize> Rope<N> {
fn new() -> Self {
Rope {
head: (0, 0),
tails: [(0, 0); N],
tail_history: HashSet::new(),
}
}
fn pos_diff(&self, index: usize) -> (i64, i64) {
let head = if index == 0 {
self.head
} else {
self.tails[index - 1]
};
let tail = self.tails[index];
(tail.0 - head.0, tail.1 - head.1)
}
fn execute(&mut self, Move(dir, count): &Move) {
let dir = <(i64, i64)>::from(*dir);
for _ in 0..*count {
self.head.0 += dir.0;
self.head.1 += dir.1;
for i in 0..N {
let tail_movement = tail_update(self.pos_diff(i));
self.tails[i].0 += tail_movement.0;
self.tails[i].1 += tail_movement.1;
}
self.tail_history.insert(self.tails[N - 1]);
}
}
}
fn tail_update(diff: (i64, i64)) -> (i64, i64) {
let reaction = (-diff.0.signum(), -diff.1.signum());
let distance = diff.0.abs() + diff.1.abs();
let has_zero_component = diff.0.abs() == 0 || diff.1.abs() == 0;
match (has_zero_component, distance) {
(true, 2) | (_, 3..) => reaction,
_ => (0, 0),
}
}
pub(crate) fn part_2(input: &Vec<Move>) -> usize {
let mut rope = Rope::<TAIL_LENGTH>::new();
for head_movement in input {
rope.execute(head_movement);
}
let result = rope.tail_history.len();
println!("Part 2: {result}");
result
}
#[cfg(test)]
mod tests {
const SAMPLE_INPUT: &str = include_str!("sample_input.txt");
#[test]
fn test_with_solution() {
assert_eq!(super::part_2(&crate::parse_input(crate::INPUT)), 2458);
}
#[test]
fn test_with_sample_solution() {
assert_eq!(super::part_2(&crate::parse_input(SAMPLE_INPUT)), 1);
}
}

View file

@ -0,0 +1,8 @@
R 4
U 4
L 3
D 1
R 4
D 1
L 5
R 2