day12 finished
This commit is contained in:
parent
6509fc79ea
commit
0b388cbd13
4 changed files with 343 additions and 1 deletions
5
data/day12/example01.txt
Normal file
5
data/day12/example01.txt
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
Sabqponm
|
||||
abcryxxl
|
||||
accszExk
|
||||
acctuvwj
|
||||
abdefghi
|
||||
41
data/day12/input.txt
Normal file
41
data/day12/input.txt
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
abcccaaaaaaccccccccaaaaaccccccaaaaaaccccccaaaaaaaacccaaaaaaaccaaaacccccccccccccccccccccccccaaaaaacccccccccccccccccccccccccccccaaaaaa
|
||||
abcccaaaaaacccccccaaaaaaccccaaaaaaaacccccccaaaaaaaaaaaaaaaaccaaaaacccccccccccccccccccccccccaaaaaacccccccccccccccccccccccccccccaaaaaa
|
||||
abccccaaaaacaaaccaaaaaaaacccaaaaaaaaacccccccaaaaaaaaaaaaaaaacaaaaaacccccccccaaacccccccccccaaaaaaaaccccccccccaaccccccccccccccccaaaaaa
|
||||
abccccaaaaccaaaaaaaaaaaaacccaaaaaaaaaacccccaaaaaaaaaaaaaaaaaaacaaaacccccccccaaaacccccccccaaaaaaaaaacccccccccaaaccccccccccccccccccaaa
|
||||
abcccccccccaaaaaacccaacccccccccaaacaaaccccccaacccccccaaaaaaaaacaacccccccccccaaaacccccccccaaaaaaaaaacccccccccaaaccacaaccccccccccccaaa
|
||||
abcccccccccaaaaaacccaacccccccccaaacccccccccccccccccccaaaacaaaacccccccaacaaccaaaccccccccccaccaaaaacacccccccccaaaacaaaaccccccccccccaac
|
||||
abccccccccccaaaaacccccccccccccccacccaaaacccccccccccccaaaacccccccccccccaaaacccccccccccaacccccaaaaccccccccjjjjaaaaaaaaaccccccccccccccc
|
||||
abccccccccccaaaacccccccccccccccccccaaaaacccccccccccccaaaccccccccccccccaaaaacccccccccaaaaaacccaaccccccccjjjjjjkkaaaacccccccccaacccccc
|
||||
abcccccaaccccccccccccccccccccccccccaaaaaacccccccccccccaacccccccccccccaaaaaaccccccccccaaaaaccccccccccccjjjjjjjkkkkaacccccaacaaacccccc
|
||||
abccaaaacccccccccccccccccccccccccccaaaaaaccccccccccccccccccccccccccccaaaacaccccccccaaaaaaaccccaacccccjjjjoooookkkkkkkklllaaaaaaacccc
|
||||
abccaaaaaacccccccccccccccccccccccccaaaaacccccccccccccccccccccccccccccccaaccccccccccaaaaaaaaccaaaaccccjjjoooooookkkkkkkllllaaaaaacccc
|
||||
abcccaaaaacccccccccccccccccccccccccccaaaccccccccaaaacccccccccccccccccccccccccccccccaaaaaaaaccaaaaccccjjooooooooppkkppplllllaccaacccc
|
||||
abccaaaaaccccccccccccaccccccccccccccccccccccccccaaaacccccccccccccccccccccccccccccccccaaacacccaaaacccijjooouuuuoppppppppplllccccccccc
|
||||
abcccccaacccccccccccaaaaaaaaccccccccccccccccccccaaaaccccaaccccccccaaacccccccccccccaacaaccccccccccccciijoouuuuuuppppppppplllcccaccccc
|
||||
abcccccccccccccccccccaaaaaaccccccccccccccccccccccaaccccaaaacccccccaaaaccccccccccaaaaaaccccccccccccciiiiootuuuuuupuuuvvpppllccccccccc
|
||||
abcccccccccccccccccccaaaaaaccaaaaacccccccccccccccccccccaaaacccccccaaaaccccccccccaaaaaaccccccccccccciiinnotuuxxxuuuuvvvpppllccccccccc
|
||||
abccccccccccccccacccaaaaaaaacaaaaaaacccccccccccccccccccaaaacccccccaaacccccaaaaccaaaaaccccaaccccccciiiinnnttxxxxuuyyyvvqqqllccccccccc
|
||||
abcccccccccccaaaaccaaaaaaaaaaaaaaaaaaccaacccccccccccccccccccccccccccccccccaaaacccaaaaaccaaacccccciiinnnnnttxxxxxyyyyvvqqqllccccccccc
|
||||
abaaaacccccccaaaaaaaaaaaaaaaaaaaaaaaaaaaacccccccccccccccccccccccccccccccccaaaacccaaaaaacaaaccccciiinnnnttttxxxxxyyyyvvqqmmmccccccccc
|
||||
abaaaaccccccccaaaaacccaaaaacaaaaaacaaaaaaccccccccccccccccaaccccccccccccccccaacccccccaaaaaaaaaaciiinnnnttttxxxxxyyyyvvqqqmmmccccccccc
|
||||
SbaaaacccccccaaaaaccccaaaaaccaaaaaaaaaaaccccccccccccccccaaacaacccccccccccccccccccccccaaaaaaaaachhhnnntttxxxEzzzzyyvvvqqqmmmccccccccc
|
||||
abaaaacccccccaacaacccccaaaaaaaacaaaaaaaaaccccccccccccccccaaaaaccccccccccccccccccccccccaaaaaaacchhhnnntttxxxxxyyyyyyvvvqqmmmdddcccccc
|
||||
abaaaacccccccccccccccccccaaaaaacaaaaaaaaaacccccccccccccaaaaaaccccccccaaaccccccccccccccaaaaaaccchhhnnntttxxxxywyyyyyyvvvqqmmmdddccccc
|
||||
abaacccccccccccccccccccaaaaaaacccccaaaaaaacccccccccccccaaaaaaaacccccaaaacccccccccccccaaaaaaacaahhhmmmttttxxwwyyyyyyyvvvqqmmmdddccccc
|
||||
abcccccccccccccccccccccaaaaaaacaaccaaacccccccccccccccccaacaaaaacccccaaaacccccccccccccaaacaaaaaahhhmmmmtsssswwyywwwwvvvvqqqmmdddccccc
|
||||
abcccccccccccccccaaaccccaaaaaaaaaacaaccaaccccccccccccccccaaacaccccccaaaacccccccccccccccccaaaaacahhhmmmmmsssswwywwwwwvvrrqqmmdddccccc
|
||||
abcccccccccccccaaaaaaccccaaaaaaaaaccaaaacccccccccccccccccaacccccccccccccccccccccccaaaccccaaaaaaahhhhhmmmmssswwwwwrrrrrrrrmmmmddccccc
|
||||
abcccccccccccccaaaaaaccccaaaaaaaaaaaaaaaaaccccccccccccccccccccccccccccccccccccccaaaaaacccccaaaaachhhhhmmmmsswwwwrrrrrrrrrkkmdddccccc
|
||||
abccccccccccccccaaaaaccccccaaaaaaaaaaaaaaaccccccccccccccccccccccccccccccccccccccaaaaaaccccaaaaacccchhggmmmssswwrrrrrkkkkkkkkdddacccc
|
||||
abccaaaacccccccaaaaacccccccccaaaaaacaaaaacccccccccccccccccccccccccccccccccccccccaaaaaaccccaacaaaccccggggmmsssssrrlkkkkkkkkkdddaccccc
|
||||
abccaaaacccccccaaaaacccccccccaaaaaaccccaacccccccccccccccccccccccccccccccccccccccaaaaaccccccccaaccccccgggmllssssrllkkkkkkkeeeddaccccc
|
||||
abccaaaacccccccaaacccccccccccaaaaaacccccccccccccccccccaacccccccccccccccccccccccaaaaaacccccccccccccccccggllllssslllkkeeeeeeeeeaaacccc
|
||||
abcccaaccccccccaaacaaaccccccaaaaaaaaaaacccccccccccccaaaaaacccccccccccccccccccccaaacaaacccccaacccccccccggglllllllllfeeeeeeeeaaaaacccc
|
||||
abccccccccccaaaaaaaaaaccccccccccccaccaaaccacccccccccaaaaaaccccaaccaacccaaccccccaaaaaaacccccaaccccccccccggglllllllfffeeecccaaaaaacccc
|
||||
abccccccccccaaaaaaaaacccccccccccccccaaaaaaaccccccccccaaaaaccccaaaaaacccaaaaaaccaaaaaacccaaaaaaaacccccccggggllllfffffccccccaacccccccc
|
||||
abcccccccccccaaaaaaacccccccccccccccccaaaaaaccaacccccaaaaaccccccaaaaacccaaaaaacaaaaaaacccaaaaaaaaccccccccgggffffffffccccccccccccccccc
|
||||
abccccccccccccaaaaaaacccccccccccccaaaaaaaaacaaaaccccaaaaacaaaaaaaaaacaaaaaaacaaaaaaaaaccccaaaacccccccccccggffffffacccccccccccccccaaa
|
||||
abccccccccccccaaaaaaacaaccccccccccaaaaaaaaacaaaacccccaaaaaaaaaaaaaaaaaaaaaaacaaaaaaaaaacccaaaaacccccccccccaffffaaaaccccccccccccccaaa
|
||||
abccccccccccccaaacaaaaaacccccccccccaaaaaaaacaaaaaaaaaaaaaaaaaaaaaaaaacaaaaaaacccaaacaaaccaaaaaacccccccccccccccccaaaccccccccccccccaaa
|
||||
abccccccccccccaaccaaaaaccccccccccccccaaaaaaaccccaaaaaaaaaaaaccccaacccccaaaaaacccaaaccccccaaccaacccccccccccccccccaaacccccccccccaaaaaa
|
||||
abcccccccccccccccaaaaaaaaccccccccccccaacccacccccccaaaaaaaaaaccccaacccccaaccccccccaccccccccccccccccccccccccccccccccccccccccccccaaaaaa
|
||||
294
src/days/day12/mod.rs
Normal file
294
src/days/day12/mod.rs
Normal file
|
|
@ -0,0 +1,294 @@
|
|||
use std::collections::{BinaryHeap, HashSet};
|
||||
|
||||
use crate::common::pos::Pos;
|
||||
|
||||
use super::template::{DayTrait, ResultType};
|
||||
use thiserror::Error;
|
||||
|
||||
const DAY_NUMBER: usize = 12;
|
||||
|
||||
pub struct Day;
|
||||
|
||||
impl DayTrait for Day {
|
||||
fn get_day_number(&self) -> usize {
|
||||
DAY_NUMBER
|
||||
}
|
||||
|
||||
fn part1(&self, lines: &[String]) -> anyhow::Result<ResultType> {
|
||||
let valley = Valley::parse(lines)?;
|
||||
Ok(ResultType::Integer(valley.walk()? as i64))
|
||||
}
|
||||
|
||||
fn part2(&self, lines: &[String]) -> anyhow::Result<ResultType> {
|
||||
let valley = Valley::parse(lines)?;
|
||||
Ok(ResultType::Integer(valley.walk_short()? as i64))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
enum ValleyError {
|
||||
#[error("Not a legal terrain char: {0}")]
|
||||
NotALegalCharacter(char),
|
||||
#[error("Valley needs to be rectangle")]
|
||||
NotAReactangleValley,
|
||||
#[error("Valley map conatins no data")]
|
||||
EmptyValley,
|
||||
#[error("Could not find start point")]
|
||||
NoStartFound,
|
||||
#[error("Could not find exit point")]
|
||||
NoExitFound,
|
||||
#[error("No path found")]
|
||||
NoPathFound,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
struct Path {
|
||||
length: usize,
|
||||
height: char,
|
||||
pos: Pos<usize>,
|
||||
}
|
||||
|
||||
impl PartialOrd for Path {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
||||
match other.length.partial_cmp(&self.length) {
|
||||
Some(core::cmp::Ordering::Equal) => {}
|
||||
ord => return ord,
|
||||
}
|
||||
match other.height.partial_cmp(&self.height) {
|
||||
Some(core::cmp::Ordering::Equal) => {}
|
||||
ord => return ord,
|
||||
}
|
||||
match self.pos.x().partial_cmp(&other.pos.x()) {
|
||||
Some(core::cmp::Ordering::Equal) => {}
|
||||
ord => return ord,
|
||||
}
|
||||
self.pos.y().partial_cmp(&other.pos.y())
|
||||
}
|
||||
}
|
||||
|
||||
impl Ord for Path {
|
||||
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
||||
other
|
||||
.length
|
||||
.cmp(&self.length)
|
||||
.then_with(|| other.height.cmp(&self.height))
|
||||
.then_with(|| self.pos.x().cmp(&other.pos.x()))
|
||||
.then_with(|| self.pos.y().cmp(&other.pos.y()))
|
||||
}
|
||||
}
|
||||
|
||||
impl Path {
|
||||
pub fn new(length: usize, height: char, pos: Pos<usize>) -> Self {
|
||||
Path {
|
||||
length,
|
||||
height,
|
||||
pos,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn next_path<'a>(&'a self, valley: &'a Valley) -> Neighbors<'a> {
|
||||
Neighbors::new(self, valley)
|
||||
}
|
||||
}
|
||||
|
||||
struct Neighbors<'a> {
|
||||
path: &'a Path,
|
||||
valley: &'a Valley,
|
||||
state: usize,
|
||||
}
|
||||
|
||||
impl<'a> Neighbors<'a> {
|
||||
pub fn new(path: &'a Path, valley: &'a Valley) -> Self {
|
||||
Neighbors {
|
||||
path,
|
||||
state: 0,
|
||||
valley,
|
||||
}
|
||||
}
|
||||
|
||||
fn next_pos(&mut self) -> Option<Pos<usize>> {
|
||||
while self.state < 4 {
|
||||
self.state += 1;
|
||||
match self.state {
|
||||
1 => {
|
||||
if self.path.pos.x() < self.valley.width() - 1 {
|
||||
return Some(Pos::new(self.path.pos.x() + 1, self.path.pos.y()));
|
||||
}
|
||||
}
|
||||
2 => {
|
||||
if self.path.pos.y() > 0 {
|
||||
return Some(Pos::new(self.path.pos.x(), self.path.pos.y() - 1));
|
||||
}
|
||||
}
|
||||
3 => {
|
||||
if self.path.pos.x() > 0 {
|
||||
return Some(Pos::new(self.path.pos.x() - 1, self.path.pos.y()));
|
||||
}
|
||||
}
|
||||
4 => {
|
||||
if self.path.pos.y() < self.valley.height() - 1 {
|
||||
return Some(Pos::new(self.path.pos.x(), self.path.pos.y() + 1));
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl Iterator for Neighbors<'_> {
|
||||
type Item = Path;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
while let Some(pos) = self.next_pos() {
|
||||
let height = self.valley.get_height(pos);
|
||||
if height as u32 + 1 >= self.path.height as u32 {
|
||||
return Some(Path::new(self.path.length + 1, height, pos));
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
struct Valley {
|
||||
map: Vec<Vec<char>>,
|
||||
start: Pos<usize>,
|
||||
exit: Pos<usize>,
|
||||
width: usize,
|
||||
}
|
||||
|
||||
impl Valley {
|
||||
pub fn parse(lines: &[String]) -> Result<Valley, ValleyError> {
|
||||
let mut map = Vec::new();
|
||||
let mut start = None;
|
||||
let mut exit = None;
|
||||
let mut valley_width = None;
|
||||
for (y, row) in lines.iter().enumerate() {
|
||||
let mut height_row = Vec::new();
|
||||
for (x, height_char) in row.chars().enumerate() {
|
||||
match height_char {
|
||||
'S' => {
|
||||
start = Some(Pos::new(x, y));
|
||||
height_row.push('a')
|
||||
}
|
||||
'E' => {
|
||||
exit = Some(Pos::new(x, y));
|
||||
height_row.push('z')
|
||||
}
|
||||
'a'..='z' => height_row.push(height_char),
|
||||
_ => return Err(ValleyError::NotALegalCharacter(height_char)),
|
||||
}
|
||||
}
|
||||
if let Some(width) = valley_width {
|
||||
if width != height_row.len() {
|
||||
return Err(ValleyError::NotAReactangleValley);
|
||||
}
|
||||
} else {
|
||||
valley_width = Some(height_row.len());
|
||||
}
|
||||
map.push(height_row);
|
||||
}
|
||||
let Some(width) = valley_width else {
|
||||
return Err(ValleyError::EmptyValley);
|
||||
};
|
||||
let Some(start) = start else {
|
||||
return Err(ValleyError::NoStartFound);
|
||||
};
|
||||
let Some(exit) = exit else {
|
||||
return Err(ValleyError::NoExitFound);
|
||||
};
|
||||
Ok(Valley {
|
||||
map,
|
||||
start,
|
||||
exit,
|
||||
width,
|
||||
})
|
||||
}
|
||||
|
||||
fn get_height(&self, pos: Pos<usize>) -> char {
|
||||
self.map[pos.y()][pos.x()]
|
||||
}
|
||||
|
||||
fn do_walk<F>(&self, check: F) -> Result<usize, ValleyError>
|
||||
where
|
||||
F: Fn(Pos<usize>) -> bool,
|
||||
{
|
||||
let mut shortest = HashSet::with_capacity(self.width * self.map.len());
|
||||
let mut queue = BinaryHeap::new();
|
||||
queue.push(Path::new(0, 'z', self.exit));
|
||||
while let Some(current) = queue.pop() {
|
||||
if check(current.pos) {
|
||||
return Ok(current.length);
|
||||
}
|
||||
if shortest.contains(¤t.pos) {
|
||||
continue;
|
||||
}
|
||||
shortest.insert(current.pos);
|
||||
for next in current.next_path(self) {
|
||||
queue.push(next);
|
||||
}
|
||||
}
|
||||
|
||||
Err(ValleyError::NoPathFound)
|
||||
}
|
||||
|
||||
pub fn walk(&self) -> Result<usize, ValleyError> {
|
||||
self.do_walk(|pos| pos == self.start)
|
||||
}
|
||||
|
||||
pub fn walk_short(&self) -> Result<usize, ValleyError> {
|
||||
self.do_walk(|pos| self.get_height(pos) == 'a')
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn width(&self) -> usize {
|
||||
self.width
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn height(&self) -> usize {
|
||||
self.map.len()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use crate::common::file::read_lines;
|
||||
use anyhow::Result;
|
||||
|
||||
#[test]
|
||||
fn test_parse() -> Result<()> {
|
||||
let day = Day {};
|
||||
let lines = read_lines(day.get_day_number(), "example01.txt")?;
|
||||
let valley = Valley::parse(&lines)?;
|
||||
assert_eq!(valley.width, 8);
|
||||
assert_eq!(valley.start, Pos::new(0, 0));
|
||||
assert_eq!(valley.exit, Pos::new(5, 2));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_part1() -> Result<()> {
|
||||
let day = Day {};
|
||||
let lines = read_lines(day.get_day_number(), "example01.txt")?;
|
||||
let expected = ResultType::Integer(31);
|
||||
let result = day.part1(&lines)?;
|
||||
assert_eq!(result, expected);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_part2() -> Result<()> {
|
||||
let day = Day {};
|
||||
let lines = read_lines(day.get_day_number(), "example01.txt")?;
|
||||
let expected = ResultType::Integer(29);
|
||||
let result = day.part2(&lines)?;
|
||||
assert_eq!(result, expected);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
@ -9,6 +9,7 @@ mod day08;
|
|||
mod day09;
|
||||
mod day10;
|
||||
mod day11;
|
||||
mod day12;
|
||||
mod template;
|
||||
|
||||
pub use template::DayTrait;
|
||||
|
|
@ -18,7 +19,7 @@ pub mod day_provider {
|
|||
use super::*;
|
||||
use thiserror::Error;
|
||||
|
||||
const MAX_DAY: usize = 11;
|
||||
const MAX_DAY: usize = 12;
|
||||
|
||||
pub fn get_day(day_num: usize) -> Result<Box<dyn DayTrait>, ProviderError> {
|
||||
match day_num {
|
||||
|
|
@ -33,6 +34,7 @@ pub mod day_provider {
|
|||
9 => Ok(Box::new(day09::Day)),
|
||||
10 => Ok(Box::new(day10::Day)),
|
||||
11 => Ok(Box::new(day11::Day)),
|
||||
12 => Ok(Box::new(day12::Day)),
|
||||
_ => Err(ProviderError::InvalidNumber(day_num)),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue