From ed18a04dae8bcc9be14bd62df3b5c86542e0006a Mon Sep 17 00:00:00 2001 From: Patrick Auernig Date: Fri, 9 Dec 2022 23:42:19 +0100 Subject: [PATCH] Add solution for 2022 day 09 part 2 --- 2022/README.md | 2 +- 2022/day-09/inputs/test2.txt | 8 +++ 2022/day-09/src/bin/part_one.rs | 61 ++------------------ 2022/day-09/src/bin/part_two.rs | 47 ++++++++++++++++ 2022/day-09/src/lib.rs | 99 ++++++++++++++++++++++++++++----- README.md | 2 +- 6 files changed, 147 insertions(+), 72 deletions(-) create mode 100644 2022/day-09/inputs/test2.txt create mode 100644 2022/day-09/src/bin/part_two.rs diff --git a/2022/README.md b/2022/README.md index 6fcea80..9d582a1 100644 --- a/2022/README.md +++ b/2022/README.md @@ -12,7 +12,7 @@ | 06 | ✓ | ✓ | [Rust] | | 07 | ✓ | ✓ | [Rust] | | 08 | ✓ | ✓ | [Rust] | -| 09 | ✓ | | [Rust] | +| 09 | ✓ | ✓ | [Rust] | | 10 | | | | | 11 | | | | | 12 | | | | diff --git a/2022/day-09/inputs/test2.txt b/2022/day-09/inputs/test2.txt new file mode 100644 index 0000000..c1eba0a --- /dev/null +++ b/2022/day-09/inputs/test2.txt @@ -0,0 +1,8 @@ +R 5 +U 8 +L 8 +D 3 +R 17 +D 10 +L 25 +U 20 \ No newline at end of file diff --git a/2022/day-09/src/bin/part_one.rs b/2022/day-09/src/bin/part_one.rs index ddf8738..e732ee0 100644 --- a/2022/day-09/src/bin/part_one.rs +++ b/2022/day-09/src/bin/part_one.rs @@ -1,6 +1,6 @@ use std::{env, io}; -use aoc_2022_09::{parse_input, Instruction, Position, VisitedMap}; +use aoc_2022_09::{parse_input, Rope, Visited}; fn main() -> io::Result<()> { let infile_path = env::args().nth(1).expect("input file"); @@ -14,69 +14,18 @@ fn main() -> io::Result<()> { fn solve(path: &str) -> io::Result { let instructions = parse_input(path)?; + let mut rope = Rope::new(2); + let mut tail_visited = Visited::new(); - let mut head_pos = Position(0, 0); - let mut tail_pos = Position(0, 0); - let mut tail_visited = VisitedMap::new(); - - tail_visited.insert(tail_pos.clone(), 1); + tail_visited.insert(rope.tail().unwrap().clone()); for instruction in instructions { - (head_pos, tail_pos) = - apply_instruction(&instruction, &head_pos, &tail_pos, &mut tail_visited); + instruction.apply_to_rope(&mut rope, &mut tail_visited); } Ok(tail_visited.len()) } -fn apply_instruction( - instruction: &Instruction, - head_pos: &Position, - tail_pos: &Position, - tail_visited: &mut VisitedMap, -) -> (Position, Position) { - let mut tmp_head_pos = head_pos.clone(); - let mut tmp_tail_pos = tail_pos.clone(); - - let (change, amount) = match *instruction { - Instruction::Up(dist) => (Position(1, 0), dist), - Instruction::Right(dist) => (Position(0, 1), dist), - Instruction::Left(dist) => (Position(0, -1), dist), - Instruction::Down(dist) => (Position(-1, 0), dist), - }; - - for _ in 1..=amount { - tmp_head_pos += change.clone(); - - let dx = tmp_head_pos.0 - tmp_tail_pos.0; - let dxa = dx.abs(); - let dy = tmp_head_pos.1 - tmp_tail_pos.1; - let dya = dy.abs(); - let same_row = tmp_head_pos.0 == tmp_tail_pos.0; - let same_col = tmp_head_pos.1 == tmp_tail_pos.1; - let mut head_changed = false; - - if (!same_row && !same_col) && (dxa > 1 || dya > 1) { - let x = if dx.is_negative() { -1 } else { 1 }; - let y = if dy.is_negative() { -1 } else { 1 }; - tmp_tail_pos += Position(x, y); - head_changed = true; - } else if dxa > 1 || dya > 1 { - tmp_tail_pos += change.clone(); - head_changed = true; - } - - if head_changed { - tail_visited - .entry(tmp_tail_pos.clone()) - .and_modify(|v| *v += 1) - .or_insert(1); - } - } - - (tmp_head_pos, tmp_tail_pos) -} - #[cfg(test)] mod test { use super::*; diff --git a/2022/day-09/src/bin/part_two.rs b/2022/day-09/src/bin/part_two.rs new file mode 100644 index 0000000..2bfec9b --- /dev/null +++ b/2022/day-09/src/bin/part_two.rs @@ -0,0 +1,47 @@ +use std::{env, io}; + +use aoc_2022_09::{parse_input, Rope, Visited}; + +fn main() -> io::Result<()> { + let infile_path = env::args().nth(1).expect("input file"); + + let result = solve(&infile_path)?; + + println!("{result}"); + + Ok(()) +} + +fn solve(path: &str) -> io::Result { + let instructions = parse_input(path)?; + let mut rope = Rope::new(10); + let mut tail_visited = Visited::new(); + + tail_visited.insert(rope.tail().unwrap().clone()); + + for instruction in instructions { + instruction.apply_to_rope(&mut rope, &mut tail_visited); + } + + Ok(tail_visited.len()) +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn sample() { + let result = solve("inputs/test.txt").unwrap(); + assert_eq!(1, result); + + let result = solve("inputs/test2.txt").unwrap(); + assert_eq!(36, result); + } + + #[test] + fn puzzle() { + let result = solve("inputs/puzzle.txt").unwrap(); + assert_eq!(2273, result); + } +} diff --git a/2022/day-09/src/lib.rs b/2022/day-09/src/lib.rs index b541abd..1eed697 100644 --- a/2022/day-09/src/lib.rs +++ b/2022/day-09/src/lib.rs @@ -1,32 +1,80 @@ use std::{ - collections::HashMap, + collections::HashSet, fs::File, io::{self, BufRead, BufReader}, - ops::{Add, AddAssign}, path::Path, str::FromStr, }; -#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] -pub struct Position(pub isize, pub isize); +pub type Visited = HashSet; -impl Add for Position { - type Output = Self; +#[derive(Debug)] +pub struct Rope(Vec); - fn add(self, rhs: Self) -> Self::Output { - Self(self.0 + rhs.0, self.1 + rhs.0) +impl Rope { + pub fn new(len: usize) -> Self { + Self(vec![Knot::default(); len]) + } + + pub fn move_head(&mut self, change: (isize, isize)) { + let mut knots = self.0.iter_mut(); + + let mut next_a = knots.next(); + + next_a.as_mut().map(|head_knot| { + head_knot.0 += change.0; + head_knot.1 += change.1; + }); + + let mut next_b = knots.next(); + + loop { + if next_a.is_some() && next_b.is_some() { + next_a.as_mut().map(|knot_a| { + next_b.as_mut().map(|knot_b| { + knot_b.follow(&knot_a); + }); + }); + + next_a = next_b; + next_b = knots.next(); + } else { + break; + } + } + } + + pub fn tail(&self) -> Option<&Knot> { + self.0.last() } } -impl AddAssign for Position { - fn add_assign(&mut self, rhs: Self) { - self.0 += rhs.0; - self.1 += rhs.1; +#[derive(Debug, Default, Clone, PartialEq, Eq, Hash)] +pub struct Knot(isize, isize); + +impl Knot { + pub fn follow(&mut self, other: &Self) { + let dx = other.0 - self.0; + let dxa = dx.abs(); + let dy = other.1 - self.1; + let dya = dy.abs(); + let same_row = self.0 == other.0; + let same_col = self.1 == other.1; + + let x = if dx.is_negative() { -1 } else { 1 }; + let y = if dy.is_negative() { -1 } else { 1 }; + + if (!same_row && !same_col) && (dxa > 1 || dya > 1) { + self.0 += x; + self.1 += y; + } else if dxa > 1 { + self.0 += x; + } else if dya > 1 { + self.1 += y; + } } } -pub type VisitedMap = HashMap; - #[derive(Debug)] pub enum Instruction { Up(isize), @@ -59,6 +107,29 @@ impl FromStr for Instruction { } } +impl Instruction { + pub fn apply_to_rope(&self, rope: &mut Rope, tail_visited: &mut Visited) { + let (change, amount) = match *self { + Instruction::Up(dist) => ((1, 0), dist), + Instruction::Right(dist) => ((0, 1), dist), + Instruction::Left(dist) => ((0, -1), dist), + Instruction::Down(dist) => ((-1, 0), dist), + }; + + for _ in 0..amount { + let old_tail = rope.tail().unwrap().clone(); + + rope.move_head(change); + + let new_tail = rope.tail().unwrap(); + + if old_tail != *new_tail { + tail_visited.insert(new_tail.clone()); + } + } + } +} + pub fn parse_input

(path: P) -> io::Result> where P: AsRef, diff --git a/README.md b/README.md index 7508f61..4055d86 100644 --- a/README.md +++ b/README.md @@ -9,4 +9,4 @@ - [2019](2019/README.md) (0% completed) - [2020](2020/README.md) (20% completed) - [2021](2021/README.md) (68% completed) -- [2022](2022/README.md) (34% completed) +- [2022](2022/README.md) (36% completed)