From 0ffae234810aeea78fbf40ed16c804390165bcd4 Mon Sep 17 00:00:00 2001 From: Christian Ulrich Date: Tue, 13 Dec 2022 03:01:31 +0100 Subject: [PATCH] add day 12, part 1 --- day12/common.rs | 184 ++++++++++++++++++++++++++++++++++++++++++++++++ day12/input | 41 +++++++++++ day12/part1.rs | 26 +++++++ day12/testinput | 5 ++ 4 files changed, 256 insertions(+) create mode 100644 day12/common.rs create mode 100644 day12/input create mode 100644 day12/part1.rs create mode 100644 day12/testinput diff --git a/day12/common.rs b/day12/common.rs new file mode 100644 index 0000000..c6ef906 --- /dev/null +++ b/day12/common.rs @@ -0,0 +1,184 @@ +// Copyright 2022 Christian Ulrich +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +use std::collections::HashSet; +use std::io; + +pub struct Map { + squares: Vec>, + start_pos: (usize, usize), + end_pos: (usize, usize), +} + +#[derive(Debug, Clone)] +struct DijkstraInfo { + pos: (usize, usize), + shortest_distance: usize, + previous: Option<(usize, usize)>, +} + +#[derive(Debug)] +pub struct ShortestPath { + dijkstra_table: Vec, + current: (usize, usize), + start_pos: (usize, usize), +} + +impl Map { + pub fn from_stdin() -> Result { + const ERROR: &str = "cannot parse map"; + let squares: Result>, _> = io::stdin() + .lines() + .map(|line| Ok(line.map_err(|_| ERROR)?.into_bytes())) + .collect(); + let mut squares = squares?; + let mut start = None; + let mut end = None; + for y in 0..squares.len() { + if squares[y].len() != squares[0].len() { + return Err(ERROR); + } + for x in 0..squares[y].len() { + match squares[y][x] as char { + 'S' if { start.is_none() } => { + start = Some((x, y)); + squares[y][x] = 'a' as u8; + } + 'S' if { start.is_some() } => return Err(ERROR), + _ => (), + } + match squares[y][x] as char { + 'E' if { end.is_none() } => { + end = Some((x, y)); + squares[y][x] = 'z' as u8; + } + 'E' if { end.is_some() } => return Err(ERROR), + _ => (), + } + } + } + Ok(Self { + squares: squares, + start_pos: start.ok_or(ERROR)?, + end_pos: end.ok_or(ERROR)?, + }) + } + + pub fn square_height(&self, pos: (usize, usize)) -> Option { + let (x, y) = pos; + self.squares.get(y)?.get(x).cloned() + } + + pub fn start_pos(&self) -> (usize, usize) { + self.start_pos + } + + pub fn end_pos(&self) -> (usize, usize) { + self.end_pos + } + + fn neighbors(&self, pos: (usize, usize)) -> Option> { + let mut result = vec![]; + let height = self.square_height(pos)?; + let (x, y) = pos; + if x > 0 { + result.push((x - 1, y)); + } + if x < self.squares[0].len() - 1 { + result.push((x + 1, y)); + } + if y > 0 { + result.push((x, y - 1)); + } + if y < self.squares.len() - 1 { + result.push((x, y + 1)); + } + let is_accessible = |pos: (usize, usize)| { + let neighbor_height = self.square_height(pos).unwrap(); + u8::abs_diff(neighbor_height, height) <= 1 || neighbor_height > height + }; + Some( + result + .iter() + .filter(|&pos| is_accessible(*pos)) + .cloned() + .collect(), + ) + } + + fn dijkstra_table(&self, start_pos: (usize, usize)) -> Vec { + let mut result = Vec::with_capacity(self.squares.len() * self.squares[0].len()); + for y in 0..self.squares.len() { + for x in 0..self.squares[0].len() { + let info = DijkstraInfo { + pos: (x, y), + shortest_distance: if (x, y) == start_pos { 0 } else { usize::MAX }, + previous: None, + }; + result.push(info); + } + } + result + } + + pub fn shortest_path(&self) -> ShortestPath { + // we reverse start_pos and end_pos so the ShortestPath will have the right order + let start_pos = self.end_pos; + let end_pos = self.start_pos; + + let mut table = self.dijkstra_table(start_pos); + let mut unvisited: HashSet<(usize, usize)> = table.iter().map(|i| i.pos).collect(); + while let Some(info) = table + .iter() + .filter(|i| i.shortest_distance < usize::MAX && unvisited.contains(&i.pos)) + .min_by_key(|i| i.shortest_distance) + .cloned() + { + for neighbor_pos in self.neighbors(info.pos).unwrap() { + let neighbor_info = table.iter_mut().find(|n| n.pos == neighbor_pos).unwrap(); + let distance = info.shortest_distance + 1; + if distance < neighbor_info.shortest_distance { + neighbor_info.shortest_distance = distance; + neighbor_info.previous = Some(info.pos); + } + } + unvisited.remove(&info.pos); + } + + ShortestPath { + dijkstra_table: table, + current: end_pos, + start_pos: start_pos, + } + } +} + +impl Iterator for ShortestPath { + type Item = (usize, usize); + + fn next(&mut self) -> Option { + if self.current == self.start_pos { + return None; + } + let result = self.current; + let info = self + .dijkstra_table + .iter() + .find(|i| i.pos == self.current) + .unwrap(); + self.current = info.previous.unwrap(); + Some(result) + } +} diff --git a/day12/input b/day12/input new file mode 100644 index 0000000..3bff85e --- /dev/null +++ b/day12/input @@ -0,0 +1,41 @@ +abaaaaacaaaacccccccccaaaaaaccccccccccccccccccccccccccccccccccaaaaaa +abaaaaacaaaaccccaaaaaaaaaacccccccccccccccccccccccccccccccccccaaaaaa +abaaacccaaaaccccaaaaaaaaaaacccaacccccccccccaacccccccccccccccccaaaaa +abaaaacccaacccccaaaaaaaaaaaaaaaaacccccccccccacccccccccccccccccccaaa +abacaacccccccccccaaaaaaaaaaaaaaaaccccccccccaacccccccccccccccccccaaa +abcccacccccccccccaaaaaaaccaaaaaaaccccccccccclllcccccacccccccccccaac +abccccccccccccccccaaaaaccccccccccccccccccclllllllcccccccccccccccccc +abaaacccccccccccccaaaaaccccccccccccccccaakklllllllcccccccccaacccccc +abaaacccccccccccacccaaaccccccccccccccccakkklpppllllccddaaacaacccccc +abaaacccaaacccccaacaaaccccccccccccccccckkkkpppppllllcddddaaaacccccc +abaacccaaaacccccaaaaaccccccccccccccccckkkkpppppppllmmddddddaaaacccc +abaaaccaaaaccccccaaaaaacaaacccccccccckkkkpppuuuppplmmmmdddddaaacccc +abaaacccaaaccccaaaaaaaacaaaaccccccckkkkkoppuuuuuppqmmmmmmdddddacccc +abcccccccccccccaaaaaaaacaaaacccccjkkkkkooppuuuuuuqqqmmmmmmmddddcccc +abccccccccccccccccaaccccaaaccccjjjjkoooooouuuxuuuqqqqqqmmmmmddecccc +abacaaccccccccccccaacccccccccccjjjjoooooouuuxxxuvvqqqqqqqmmmeeecccc +abaaaacccccccacccaccccccccccccjjjjoootuuuuuuxxxyvvvvvqqqqmmmeeecccc +abaaaaacccccaaacaaacccccccccccjjjoooottuuuuuxxyyvvvvvvvqqmnneeecccc +abaaaaaccaaaaaaaaaaccccccccaccjjjooottttxxxxxxyyyyyyvvvqqnnneeecccc +abaaaccccaaaaaaaaaacccccccaaccjjjoootttxxxxxxxyyyyyyvvqqqnnneeecccc +SbcaaccccaaaaaaaaaaccccaaaaacajjjnnntttxxxxEzzzyyyyvvvrrqnnneeccccc +abcccccccaaaaaaaaaaacccaaaaaaaajjjnnntttxxxxyyyyyvvvvrrrnnneeeccccc +abcccccccaaaaaaaaaaacccccaaaaccjjjnnnnttttxxyyyyywvvrrrnnneeecccccc +abcccccccccaaaaaaccaccccaaaaaccciiinnnnttxxyyyyyyywwrrnnnneeecccccc +abccccccccccccaaacccccccaacaaaccciiinnnttxxyywwyyywwrrnnnffeccccccc +abccccccccccccaaacccccccaccaaaccciiinnnttwwwwwwwwwwwrrrnnfffccccccc +abccccccccccccccccccccccccccccccciiinnnttwwwwsswwwwwrrrnnfffccccccc +abaaaccaaccccccccccccccccccccccccciinnnttswwwssswwwwrrroofffacccccc +abaaccaaaaaacccccccccccccccccaaacciinnntssssssssssrrrrooofffacccccc +abaccccaaaaacccccccaaacccccccaaaaciinnnssssssmmssssrrrooofffacccccc +abaacaaaaaaacccccccaaaaccccccaaaaciiinmmmssmmmmmoosroooooffaaaacccc +abaaaaaaaaaaaccccccaaaaccccccaaacciiimmmmmmmmmmmoooooooofffaaaacccc +abcaaaaaaaaaaccccccaaaaccccccccccccihhmmmmmmmhggoooooooffffaaaccccc +abcccccaaacaccccccccaaccccccccccccchhhhhhhhhhhggggggggggffaaacccccc +abaccccaacccccccccccaaaccccccccccccchhhhhhhhhhgggggggggggcaaacccccc +abaaaccccaccccccccccaaaacccaacccccccchhhhhhhaaaaaggggggcccccccccccc +abaaaccccaaacaaaccccaaaacaaaacccccccccccccccaaaacccccccccccccccaaac +abaacccccaaaaaaaccccaaaaaaaaacccccccccccccccaaacccccccccccccccccaaa +abaaaccccaaaaaaccccaaaaaaaaccccccccccccccccccaacccccccccccccccccaaa +abccccccaaaaaaaaaaaaaaaaaaacccccccccccccccccaaccccccccccccccccaaaaa +abcccccaaaaaaaaaaaaaaaaaaaaacccccccccccccccccccccccccccccccccaaaaaa diff --git a/day12/part1.rs b/day12/part1.rs new file mode 100644 index 0000000..97b979e --- /dev/null +++ b/day12/part1.rs @@ -0,0 +1,26 @@ +// Copyright 2022 Christian Ulrich +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// usage: ./part1 < input + +pub mod common; + +use common::Map; + +fn main() -> Result<(), &'static str> { + let map = Map::from_stdin()?; + println!("{:?}", map.shortest_path().count()); + Ok(()) +} diff --git a/day12/testinput b/day12/testinput new file mode 100644 index 0000000..86e9cac --- /dev/null +++ b/day12/testinput @@ -0,0 +1,5 @@ +Sabqponm +abcryxxl +accszExk +acctuvwj +abdefghi