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