day(09): add initial solution
Signed-off-by: Matej Focko <mfocko@redhat.com>
This commit is contained in:
parent
3219400a73
commit
d7d3a8f889
2 changed files with 220 additions and 0 deletions
8
samples/day09.txt
Normal file
8
samples/day09.txt
Normal file
|
@ -0,0 +1,8 @@
|
|||
R 4
|
||||
U 4
|
||||
L 3
|
||||
D 1
|
||||
R 4
|
||||
D 1
|
||||
L 5
|
||||
R 2
|
212
src/bin/day09.rs
Normal file
212
src/bin/day09.rs
Normal file
|
@ -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<Instruction>;
|
||||
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<Self, Self::Err> {
|
||||
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<Self, Self::Err> {
|
||||
let parts = s.split(' ').collect_vec();
|
||||
let (direction, steps) = (parts[0].parse::<Direction>()?, parts[1].parse::<i32>()?);
|
||||
|
||||
Ok(Self { direction, steps })
|
||||
}
|
||||
}
|
||||
|
||||
type Position = Vector2D<i32>;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
struct State {
|
||||
knots: Vec<Position>,
|
||||
}
|
||||
|
||||
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<Position>, 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<Position>, 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::<Position>::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<Input, Output> for Day09 {
|
||||
fn parse_input<P: AsRef<Path>>(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);
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue