From fe253537efafdc40fb64e5a03f15118204d003a3 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Wed, 18 Dec 2024 18:34:41 +0100 Subject: [PATCH] chore: WIP on day 16 --- data/examples/16.txt | 17 ++++ src/bin/16.rs | 193 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 210 insertions(+) create mode 100644 data/examples/16.txt create mode 100644 src/bin/16.rs diff --git a/data/examples/16.txt b/data/examples/16.txt new file mode 100644 index 0000000..bc61c57 --- /dev/null +++ b/data/examples/16.txt @@ -0,0 +1,17 @@ +################# +#...#...#...#..E# +#.#.#.#.#.#.#.#.# +#.#.#.#...#...#.# +#.#.#.#.###.#.#.# +#...#.#.#.....#.# +#.#.#.#.#.#####.# +#.#...#.#.#.....# +#.#.#####.#.###.# +#.#.#.......#...# +#.#.###.#####.### +#.#.#...#.....#.# +#.#.#.#####.###.# +#.#.#.........#.# +#.#.#.#########.# +#S#.............# +################# diff --git a/src/bin/16.rs b/src/bin/16.rs new file mode 100644 index 0000000..99903bc --- /dev/null +++ b/src/bin/16.rs @@ -0,0 +1,193 @@ +advent_of_code::solution!(16); + +use std::collections::HashMap; +use std::collections::vec_deque::VecDeque; + +#[derive(Debug, Clone, Hash, PartialEq, Eq)] +struct Pos { + x: usize, + y: usize, +} + +impl Pos { + fn neighbors(&self) -> Vec { + let mut res: Vec = Vec::new(); + if self.x > 0 { + res.push(Step { pos: Pos { x: self.x - 1, y: self.y }, dir: Dir::Left }); + } + if self.y > 0 { + res.push(Step { pos: Pos { x: self.x, y: self.y - 1 }, dir: Dir::Up }); + } + res.push(Step { pos: Pos { x: self.x + 1, y: self.y }, dir: Dir::Right }); + res.push(Step { pos: Pos { x: self.x, y: self.y + 1 }, dir: Dir::Down }); + res + } +} + +#[derive(Debug)] +struct State { + map: HashMap, + start: Pos, + end: Pos, + size: Pos, +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +enum Dir { + Up, + Down, + Left, + Right, +} + +#[derive(Debug, Clone)] +struct Step { + pos: Pos, + dir: Dir, +} + +#[derive(Debug, Clone)] +struct Path { + steps: VecDeque +} + +impl Path { + fn init(pos: Pos) -> Self { + Self { + steps: VecDeque::from(vec![Step {pos, dir: Dir::Right}]), + } + } + + fn current(&self) -> Pos { + self.steps.back().unwrap().pos.clone() + } + fn have_visited(&self, pos: &Pos) -> bool { + self.steps.iter().any(|s| s.pos == *pos) + } + fn push(&mut self, step: Step) { + self.steps.push_back(step); + } + fn score(&self) -> u32 { + let mut score = 0u32; + let mut cur_dir: Option = None; + let mut steps = self.steps.clone(); + steps.pop_back(); + for step in &steps { + if cur_dir.is_some() { + if cur_dir == Some(step.dir) { + score += 1; + } else { + score += 1001; + } + } + cur_dir = Some(step.dir); + } + score + 1 // duh?! + } +} + +impl TryFrom<&str> for State { + type Error = (); + fn try_from(input: &str) -> Result { + let input = input.lines().collect::>(); + let size = Pos { x: input[0].len(), y: input.len() }; + let input: Vec<(Pos, char)> = input.iter() + .enumerate() + .flat_map(|(y, l)| + l + .chars() + .enumerate() + .filter_map(move |(x, c)| + if c == '#' { + None + } else { + Some((Pos {x, y}, c)) + })) + .collect(); + let start = input.iter().find(|(_, c)| *c == 'S').ok_or(())?.0.clone(); + let end = input.iter().find(|(_, c)| *c == 'E').ok_or(())?.0.clone(); + let mut map = HashMap::new(); + for (k, v) in input.into_iter() { + map.insert(k, v); + } + Ok(State { + map, + start, + end, + size, + }) + } +} + +impl State { + fn compute_all_paths(&self, path: Path, all_paths: &mut Vec) { + let cur = path.current(); + if self.map.get(&cur) == Some(&'E') { + println!("Finished a path"); + println!("Score {:?}", path.score()); + all_paths.push(path); + return; + } + for next in cur.neighbors().iter() + .filter(|p| self.map.contains_key(&p.pos) && !path.have_visited(&p.pos)) { + let mut new_path = path.clone(); + new_path.push(next.clone()); + self.compute_all_paths(new_path, all_paths); + } + } + + fn get_paths(&self) -> Vec { + let initial_path = Path::init(self.start.clone()); + let mut all_paths: Vec = Vec::new(); + self.compute_all_paths(initial_path, &mut all_paths); + all_paths + } + + fn print_path(&self, path: &Path) { + println!(); + println!("Path score: {}", path.score()); + for y in 0..self.size.y { + for x in 0..self.size.x { + let p = Pos { x, y }; + if path.have_visited(&p) { + print!("o"); + } else if self.map.contains_key(&p) { + print!(" "); + } else { + print!("#"); + } + } + println!(); + } + println!() + } +} + +pub fn part_one(input: &str) -> Option { + let st = State::try_from(input).ok()?; + // for p in st.get_paths() { + // st.print_path(&p); + // } + st.get_paths().iter().map(|p|p.score()).min() +} + +pub fn part_two(input: &str) -> Option { + None +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_part_one() { + let result = part_one(&advent_of_code::template::read_file("examples", DAY)); + assert_eq!(result, Some(11048)); + } + + #[test] + fn test_part_two() { + let result = part_two(&advent_of_code::template::read_file("examples", DAY)); + assert_eq!(result, None); + } +}