add day 12, part 1
This commit is contained in:
parent
f1001ef8ff
commit
0ffae23481
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
use std::collections::HashSet;
|
||||||
|
use std::io;
|
||||||
|
|
||||||
|
pub struct Map {
|
||||||
|
squares: Vec<Vec<u8>>,
|
||||||
|
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<DijkstraInfo>,
|
||||||
|
current: (usize, usize),
|
||||||
|
start_pos: (usize, usize),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Map {
|
||||||
|
pub fn from_stdin() -> Result<Self, &'static str> {
|
||||||
|
const ERROR: &str = "cannot parse map";
|
||||||
|
let squares: Result<Vec<Vec<u8>>, _> = 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<u8> {
|
||||||
|
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<Vec<(usize, usize)>> {
|
||||||
|
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<DijkstraInfo> {
|
||||||
|
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<Self::Item> {
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
//
|
||||||
|
// 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(())
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
Sabqponm
|
||||||
|
abcryxxl
|
||||||
|
accszExk
|
||||||
|
acctuvwj
|
||||||
|
abdefghi
|
Loading…
Reference in New Issue