diff --git a/samples/day09.txt b/samples/day09.txt new file mode 100644 index 0000000..cbea2b3 --- /dev/null +++ b/samples/day09.txt @@ -0,0 +1,8 @@ +R 4 +U 4 +L 3 +D 1 +R 4 +D 1 +L 5 +R 2 \ No newline at end of file diff --git a/src/bin/day09.rs b/src/bin/day09.rs new file mode 100644 index 0000000..626c5a6 --- /dev/null +++ b/src/bin/day09.rs @@ -0,0 +1,212 @@ +use std::{collections::BTreeSet, str::FromStr}; + +use aoc_2022::*; + +use color_eyre::{eyre::eyre, Report}; +use itertools::Itertools; + +type Input = Vec; +type Output = usize; + +enum Direction { + Left, + Right, + Down, + Up, +} + +impl Direction { + fn apply(&self, pos: Position) -> Position { + pos + match self { + Direction::Left => Position::new(-1, 0), + Direction::Right => Position::new(1, 0), + Direction::Down => Position::new(0, -1), + Direction::Up => Position::new(0, 1), + } + } +} + +impl FromStr for Direction { + type Err = Report; + + fn from_str(s: &str) -> Result { + match s { + "L" => Ok(Direction::Left), + "R" => Ok(Direction::Right), + "D" => Ok(Direction::Down), + "U" => Ok(Direction::Up), + _ => Err(eyre!("Invalid direction {}", s)), + } + } +} + +struct Instruction { + direction: Direction, + steps: i32, +} + +impl FromStr for Instruction { + type Err = Report; + + fn from_str(s: &str) -> Result { + let parts = s.split(' ').collect_vec(); + let (direction, steps) = (parts[0].parse::()?, parts[1].parse::()?); + + Ok(Self { direction, steps }) + } +} + +type Position = Vector2D; + +#[derive(Debug, Clone)] +struct State { + knots: Vec, +} + +impl State { + fn new(knots: i32) -> State { + Self { + knots: (0..knots).map(|_| Position::new(0, 0)).collect_vec(), + } + } + + fn execute_step(&self, visited: &mut BTreeSet, direction: &Direction) -> State { + let mut knots = self.knots.clone(); + + // move head + knots[0] = direction.apply(knots[0]); + + // index of tail + let tail = knots.len() - 1; + + // add old position of the tail to the set + visited.insert(knots[tail]); + + // move parts towards head + for i in 1..knots.len() { + let v = knots[i - 1] - knots[i]; + if v.x().abs() > 1 || v.y().abs() > 1 { + let d = Position::new(v.x().signum(), v.y().signum()); + knots[i] = knots[i] + d; + } + } + // debug!("\n{}", State { head, tail }.show(6, 5)); + + // add new position of the tail to the set + visited.insert(knots[tail]); + + State { knots } + } + + fn execute(&self, i: &Instruction) -> (BTreeSet, State) { + let mut visited = BTreeSet::new(); + + // debug!("\n{}", self.show(6, 5)); + let mut state: State = self.clone(); + for _ in 0..i.steps { + state = state.execute_step(&mut visited, &i.direction); + } + + (visited, state) + } + + // fn show(&self, width: i32, height: i32) -> String { + // (0..height) + // .rev() + // .map(|y| { + // (0..width) + // .map(|x| { + // let pos = Position::new(x, y); + + // if pos == self.head { + // 'H' + // } else if pos == self.tail { + // 'T' + // } else { + // '.' + // } + // }) + // .join("") + // }) + // .join("\n") + // } +} + +fn execute(knots: i32, input: &Input) -> Output { + let mut visited = BTreeSet::::new(); + + input.iter().fold(State::new(knots), |state, instruction| { + let (mut v, s) = state.execute(instruction); + visited.append(&mut v); + + s + }); + + visited.len() +} + +struct Day09; +impl Solution for Day09 { + fn parse_input>(pathname: P) -> Input { + file_to_structs(pathname) + } + + fn part_1(input: &Input) -> Output { + execute(2, input) + } + + fn part_2(input: &Input) -> Output { + execute(10, input) + } +} + +fn main() -> Result<()> { + Day09::main() +} + +test_sample!(day_09, Day09, 13, 1); + +#[cfg(test)] +mod day_09_extended { + use super::*; + + #[test] + fn test_part_2_bigger_sample() { + let input = vec![ + Instruction { + direction: Direction::Right, + steps: 5, + }, + Instruction { + direction: Direction::Up, + steps: 8, + }, + Instruction { + direction: Direction::Left, + steps: 8, + }, + Instruction { + direction: Direction::Down, + steps: 3, + }, + Instruction { + direction: Direction::Right, + steps: 17, + }, + Instruction { + direction: Direction::Down, + steps: 10, + }, + Instruction { + direction: Direction::Left, + steps: 25, + }, + Instruction { + direction: Direction::Up, + steps: 20, + }, + ]; + + assert_eq!(Day09::part_2(&input), 36); + } +}