Rust Day 12
This commit is contained in:
parent
e7a06642aa
commit
2f1282652a
7 changed files with 272 additions and 2 deletions
2
rust/Cargo.lock
generated
2
rust/Cargo.lock
generated
|
@ -4,4 +4,4 @@ version = 3
|
|||
|
||||
[[package]]
|
||||
name = "advent-of-code"
|
||||
version = "22.11.2"
|
||||
version = "22.12.2"
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "advent-of-code"
|
||||
version = "22.11.2"
|
||||
version = "22.12.2"
|
||||
edition = "2021"
|
||||
resolver = "2"
|
||||
|
||||
|
@ -67,6 +67,10 @@ path = "src/day10/main.rs"
|
|||
name = "day11"
|
||||
path = "src/day11/main.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "day12"
|
||||
path = "src/day12/main.rs"
|
||||
|
||||
[dependencies]
|
||||
# Required for one of the day06 solutions (commented out)
|
||||
# phf = { version = "0.11.1", features = ["macros"] }
|
41
rust/src/day12/input.txt
Normal file
41
rust/src/day12/input.txt
Normal file
|
@ -0,0 +1,41 @@
|
|||
abcccccccccccccccccaaaaaaaaccccccacccaaccccccccccccccccccaaaaaaaaaacccccccccccccccccccccccccccccccaaaaaaccccccccccccccccccccccccccccccccccaaaaa
|
||||
abccccccccccccccccccaaaaacccccccaaaaaaacccccccccccaaccaaaaaaaaaaaaaccccccccccccccccccccccccccccccccaaaaacccccccccccccccccccccccccccccccccaaaaaa
|
||||
abccccccccccccaaccccaaaaaacccccccaaaaaaaaccccccacaaaccaaaaaaaaaaaaaaaccccccccccccccccccaaacccccccaaaaaaaccccccccccccccccaaaccccccccccccccaaaaaa
|
||||
abccccccccacccaaccccaaaaaacccccccaaaaaaaaaccccaaaaaaaaacaaaaaaaaaaaaacccccccccccccccccccaacccccccaaaaaaaacccccccccccccccaaaccccccccccccccaccaaa
|
||||
abaacccccaaaaaaaccccaaaccacccccccaaaaaaaaaccccaaaaaaaaccccaaaaaaaaaaaccccccccccccccccaacaaaaaccccaaaaaaaacccccccccccccccaaacccccccccccccccccaaa
|
||||
abaaccccccaaaaaaaacccccccccccccccaaaaaaaaccccccaaaaaacccccaaaacaaaaccccccccccccccccccaaaaaaaaccccccaaacaccccccccccccccccaaakccaaaccccccccccccaa
|
||||
abaaacccccaaaaaaaaaccccccccccccccaaaaaaacccccccaaaaaccccccaaaccaaaaccccccccccccaacacccaaaaaccccccccaaacccccccccccccacacckkkkkkkaacccccccccccccc
|
||||
abaaacccccaaaaaaaaaccccccccccccccaccaaaaaccccccaaaaaacccccaaacaaaccccccccccccccaaaaccccaaaaacccccccccccccccccccccccaaaakkkkkkkkkacccaaaccaccccc
|
||||
abacacccccaaaaaaaccccccccccccccccccccaaaaaaaccccccaaccccccaaaaaaaaccccccccccccaaaaacccaaacaacccccccccccccccccccccccaajkkkkppkkkkccccaaaaaaccccc
|
||||
abacccccccaaaaaaacccccccccccccccccccaaaaaaaaccccccccccccccccaaaaaaccccccccccccaaaaaacccaacccccccccccccccccccccccccccjjkkooppppkllccccaaaaaccccc
|
||||
abccccccccaccaaaccccccccccccccccccccaaaaaaaacccccccccccccccccaaaaaccccccccccccacaaaacccccccccccccccccccccccccccccjjjjjjoooppppklllcacaaaaaccccc
|
||||
abcccaacccccccaaacccccccccccccccccccaaaaaaacccccccccccccccccaaaaacccccccccccccccaacaccccccccccccccccccccccccccjjjjjjjjoooopuppplllcccccaaaacccc
|
||||
abcccaacccccccccccccccccaaacccccccccccaaaaaaccccccaaaaacccccaaaaaccccccccccccaaacaaacccccaaaccccccccccccccccijjjjjjjjooouuuuuppllllcccccaaacccc
|
||||
abaaaaaaaaccccccccccccccaaaaccccccccccaacaaaccccccaaaaaccccccccccccccccccccccaaaaaaacccccaaacacccccccccccccciijjoooooooouuuuuppplllllccccaccccc
|
||||
abaaaaaaaaccccccccccccccaaaaccccccccccaacccccccccaaaaaacccccccccccccccccccccccaaaaaacccaaaaaaaacccccccccccciiiqqooooooouuuxuuuppplllllccccccccc
|
||||
abccaaaaccccccccccccccccaaaccccccccccccccccccccccaaaaaacccccccccccccccccccccccaaaaaaaccaaaaaaaacccccccccccciiiqqqqtttuuuuxxxuupppqqllllmccccccc
|
||||
abcaaaaacccaaaccccccccccccccccccccccccaccccccccccaaaaaacccccccccccccccccccccaaaaaaaaaaccaaaaaaccccccccccccciiiqqqtttttuuuxxxuuvpqqqqmmmmccccccc
|
||||
abcaacaaaccaaacaaccccccccccccccccccccaaaacaaaccccccaacccaaaaacccccccccccccccaaaaaaaaaacccaaaaacccaaaccccccciiiqqttttxxxxxxxyuvvvvqqqqmmmmcccccc
|
||||
abcacccaaccaaaaaaccccccccccccccccccccaaaaaaaacccccccccccaaaaacccccccccccccccaaacaaacccccaaaaaaccaaaacccccaaiiiqqtttxxxxxxxxyyvvvvvvqqqmmmdddccc
|
||||
abcccccccaaaaaaaccccccccccccccccccccccaaaaaaaaacccccccccaaaaaaccccccccccccccccccaaaccccccaacccccaaaacccaaaaiiiqqqttxxxxxxxyyyyyyvvvqqqmmmdddccc
|
||||
SbccccccccaaaaaccccccccaacaaccccccccaaaaaaaaaaccccccccccaaaaaaccccccccccccaaacccaaccccccccccccccaaaacccaaaaaiiiqqtttxxxxEzzyyyyvvvvqqqmmmdddccc
|
||||
abaccccccccaaaaacccccccaaaaacccccccaaaaaaaaaaaccccccccccaaaaaaccccccccccaaaaaacccccccccccccccccccccccccaaaaaiiiqqqtttxxxyyyyyyvvvvqqqmmmdddcccc
|
||||
abaacccccccaacaaaccccccaaaaaacccccccaaaaaaaaaaccccccccccccaaacccccccccccaaaaaaccccccccccccccccccccccccccaaaahhhqqqqttxxyyyyyyvvvvqqqmmmddddcccc
|
||||
abaccccccccaaccccccccccaaaaaacccaacaaccaaaaaaaaaccccccccccccccccccccccccaaaaaaccccccccccccccccccccccccccaaaachhhqqtttxwyyyyyywvrqqqmmmmdddccccc
|
||||
abaaaccccccccccccccccccaaaaaacccaaaaaccaaaaacaaaccccccccccccccccccccccccaaaaaccccaaaaccccaaaccccccccccccccccchhhppttwwwywwyyywwrrrnmmmdddcccccc
|
||||
abaaaccccccccccccccccccccaaaccccaaaaaacaaaaaaaaaccccccccaaacccccccccccccaaaaaccccaaaaccccaaaccccccccccccccccchhpppsswwwwwwwwywwrrrnnndddccccccc
|
||||
abaaacccccccccccccccccccccccccccaaaaaacccaaaaaacccccccccaaaaacccccaacccccccccccccaaaacaaaaaaaaccccccccccccccchhpppsswwwwsswwwwwrrrnneeddccccccc
|
||||
abaccccccccaaaacccccccccccccccccaaaaaaccccaaaaaaaacccccaaaaaaccaacaaacccccccccccccaaccaaaaaaaaccccccccccccccchhpppssssssssrwwwwrrrnneeecaaccccc
|
||||
abaccccccccaaaacccccccccccccccccccaaaccccaaaaaaaaacccccaaaaaaccaaaaaccccccccccccccccccccaaaaacccccccccccccccchhpppssssssssrrrwrrrnnneeeaaaccccc
|
||||
abcccccccccaaaacccccccccccccccccccccccccaaaaaaaaaaccccccaaaaacccaaaaaacccccccccccccccccaaaaaacccccccccccccccchhpppppsssooorrrrrrrnnneeeaaaccccc
|
||||
abcccccccccaaaccccccccccccccccccccccccccaaacaaacccccccccaacaacaaaaaaaacccccccccccccccccaaaaaacaaccccccccccccchhhppppppoooooorrrrnnneeeaaaaacccc
|
||||
abccccccccccccccccccccccccccccccccccccccccccaaaccaaaacccccccccaaaaacaaccccaacccccccccacaaaaaacaaccccccccccccchhhgpppppoooooooonnnnneeeaaaaacccc
|
||||
abcccccccaacccccccccccccccccccccccccccccccccaaacaaaaaccccccccccacaaaccccccaacccccccccaacaaaaaaaaaaacccccaaccccgggggggggggfooooonnneeeeaaaaacccc
|
||||
abcccccccaaacaaccccccccccccaacccccccccccccccccccaaaaaaccccaacccccaaacccaaaaaaaaccccccaaaaacaaaaaaaaccccaaacccccggggggggggfffooonneeeecaaacccccc
|
||||
abcccccccaaaaaaccccaacccccaaacccccccccccccccccccaaaaaaccccaaaccccccccccaaaaaaaacccccccaaaaaccaaaaccccaaaaaaaacccggggggggfffffffffeeeecaaccccccc
|
||||
abcccccaaaaaaaccaaaaacaaaaaaacccccccccccccccccccaaaaacccccaaaacccaaccccccaaaacccccccaaaaaaaacaaaaacccaaaaaaaaccccccccccaaaffffffffecccccccccccc
|
||||
abcaaacaaaaaaacccaaaaaaaaaaaaaaaccccccccccccccccccaaacccccaaaacaaaacaacccaaaaaccccccaaaaaaaaaaaaaaccccaaaaaacccccccccccaaacaafffffccccccccccaaa
|
||||
abaaaacccaaaaaaccaaaaacaaaaaaaaaccccccccccccaaacccccccccccaaaaaaaaacaaccaaacaacccccccccaacccaaccaaccccaaaaaaccccccccccaaaaccaaacccccccccccccaaa
|
||||
abaaaacccaacaaacaaaaacccaaaaaaacccccccccccccaaaacccccccccaaaaaaaaaaaaaccaacccacccccccccaacccccccccccccaaaaaaccccccccccaaacccccccccccccccccccaaa
|
||||
abcaaacccaacccccccaaaccaaaaaacccccccccccccccaaaaccccccaaaaaaaaaaaaaaaaaaccccccccccccccccccccccccccccccaaccaaccccccccccaaaccccccccccccccccaaaaaa
|
||||
abcccccccccccccccccccccaaaaaaaccccccccccccccaaacccccccaaaaaaaaaaaaaaaaaacccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccaaaaaa
|
148
rust/src/day12/main.rs
Normal file
148
rust/src/day12/main.rs
Normal file
|
@ -0,0 +1,148 @@
|
|||
const INPUT: &str = include_str!("input.txt");
|
||||
|
||||
mod part_1;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
|
||||
use part_1::part_1;
|
||||
mod part_2;
|
||||
use part_2::part_2;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub(crate) struct ParsedInput {
|
||||
pub(crate) grid: HashMap<(usize, usize), u8>,
|
||||
pub(crate) start_position: (usize, usize),
|
||||
pub(crate) end_position: (usize, usize),
|
||||
}
|
||||
|
||||
fn get_front(frontier: &HashMap<(usize, usize), usize>) -> (usize, usize) {
|
||||
let mut items = frontier.iter().collect::<Vec<_>>();
|
||||
items.sort_unstable_by(|left, right| left.1.cmp(right.1));
|
||||
*items[0].0
|
||||
}
|
||||
|
||||
fn neighbors(
|
||||
position: (usize, usize),
|
||||
grid: &HashMap<(usize, usize), u8>,
|
||||
) -> HashSet<(usize, usize)> {
|
||||
let (x, y) = position;
|
||||
let height = grid[&position];
|
||||
|
||||
[
|
||||
(x + 1, y),
|
||||
(x.saturating_sub(1), y),
|
||||
(x, y + 1),
|
||||
(x, y.saturating_sub(1)),
|
||||
]
|
||||
.iter()
|
||||
.filter_map(|neighbor_position| {
|
||||
if *neighbor_position == position {
|
||||
None
|
||||
} else {
|
||||
grid.get(neighbor_position).and_then(|&neighbor_height| {
|
||||
(neighbor_height <= height + 1).then_some(*neighbor_position)
|
||||
})
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn reconstruct_path(
|
||||
end_position: (usize, usize),
|
||||
start_position: (usize, usize),
|
||||
came_from: &HashMap<(usize, usize), Option<(usize, usize)>>,
|
||||
) -> Option<Vec<(usize, usize)>> {
|
||||
let mut current_position = end_position;
|
||||
let mut path = vec![];
|
||||
|
||||
while current_position != start_position {
|
||||
path.push(current_position);
|
||||
if let Some(next) = came_from.get(¤t_position) {
|
||||
current_position = next.unwrap();
|
||||
} else {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
|
||||
Some(path)
|
||||
}
|
||||
|
||||
pub(crate) fn dijkstra(
|
||||
grid: &HashMap<(usize, usize), u8>,
|
||||
start_position: (usize, usize),
|
||||
end_position: (usize, usize),
|
||||
) -> Option<Vec<(usize, usize)>> {
|
||||
let mut frontier = HashMap::new();
|
||||
frontier.insert(start_position, 0);
|
||||
|
||||
let mut came_from = HashMap::<(_, _), _>::new();
|
||||
let mut cost_so_far = HashMap::<(_, _), _>::new();
|
||||
|
||||
came_from.insert(start_position, None);
|
||||
cost_so_far.insert(start_position, 0);
|
||||
|
||||
// let mut step = 0;
|
||||
while !frontier.is_empty() {
|
||||
let current_position = get_front(&frontier);
|
||||
// step += 1;
|
||||
// println!("Step {step}: {current_position:?}");
|
||||
frontier.remove(¤t_position);
|
||||
|
||||
if current_position == end_position {
|
||||
// println!("Found path after {step} steps");
|
||||
break;
|
||||
}
|
||||
|
||||
let neighbors = neighbors(current_position, grid);
|
||||
// println!("Step {step}: neighbors = {neighbors:?}");
|
||||
for neighbor in neighbors {
|
||||
let new_cost = cost_so_far[¤t_position] + 1;
|
||||
let old_cost = cost_so_far.get(&neighbor);
|
||||
|
||||
if old_cost.is_none() || new_cost < *old_cost.unwrap() {
|
||||
cost_so_far.insert(neighbor, new_cost);
|
||||
frontier.insert(neighbor, new_cost);
|
||||
came_from.insert(neighbor, Some(current_position));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
reconstruct_path(end_position, start_position, &came_from)
|
||||
}
|
||||
|
||||
fn parse_input(input: &'static str) -> ParsedInput {
|
||||
let mut start_position = (0, 0);
|
||||
let mut end_position = (0, 0);
|
||||
|
||||
let matrix = input
|
||||
.lines()
|
||||
.enumerate()
|
||||
.flat_map(|(y, line)| {
|
||||
line.bytes()
|
||||
.enumerate()
|
||||
.map(|(x, byte)| match byte {
|
||||
b'S' => {
|
||||
start_position = (x, y);
|
||||
((x, y), 0)
|
||||
}
|
||||
b'E' => {
|
||||
end_position = (x, y);
|
||||
((x, y), b'z' - b'a')
|
||||
}
|
||||
_ => ((x, y), byte - b'a'),
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
})
|
||||
.collect();
|
||||
|
||||
ParsedInput {
|
||||
grid: matrix,
|
||||
start_position,
|
||||
end_position,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
let input = parse_input(INPUT);
|
||||
part_1(&input);
|
||||
part_2(&input);
|
||||
}
|
30
rust/src/day12/part_1.rs
Normal file
30
rust/src/day12/part_1.rs
Normal file
|
@ -0,0 +1,30 @@
|
|||
use crate::{dijkstra, ParsedInput};
|
||||
|
||||
pub(crate) fn part_1(input: &ParsedInput) -> usize {
|
||||
let ParsedInput {
|
||||
start_position,
|
||||
end_position,
|
||||
grid,
|
||||
} = input;
|
||||
|
||||
let path_length = dijkstra(grid, *start_position, *end_position).expect("All inputs should be solvable").len();
|
||||
// The first step doesn't count, ig?
|
||||
println!("Part 1: {}", path_length);
|
||||
|
||||
path_length
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
const SAMPLE_INPUT: &str = include_str!("sample_input.txt");
|
||||
|
||||
#[test]
|
||||
fn test_with_solution() {
|
||||
assert_eq!(super::part_1(&crate::parse_input(crate::INPUT)), 462);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_with_sample_solution() {
|
||||
assert_eq!(super::part_1(&crate::parse_input(SAMPLE_INPUT)), 31);
|
||||
}
|
||||
}
|
42
rust/src/day12/part_2.rs
Normal file
42
rust/src/day12/part_2.rs
Normal file
|
@ -0,0 +1,42 @@
|
|||
use crate::{dijkstra, ParsedInput};
|
||||
|
||||
pub(crate) fn part_2(input: &ParsedInput) -> usize {
|
||||
let ParsedInput {
|
||||
start_position: _,
|
||||
end_position,
|
||||
grid,
|
||||
} = input;
|
||||
|
||||
let mut idx = 0;
|
||||
let shortest_path_length = grid
|
||||
.iter()
|
||||
.filter_map(|(position, height)| {
|
||||
println!("{idx}");
|
||||
idx += 1;
|
||||
if *height != 0 {
|
||||
None
|
||||
} else {
|
||||
dijkstra(grid, *position, *end_position)
|
||||
}
|
||||
})
|
||||
.fold(usize::MAX, |accumulator, path| accumulator.min(path.len()));
|
||||
// The first step doesn't count, ig?
|
||||
println!("Part 2: {}", shortest_path_length);
|
||||
|
||||
shortest_path_length
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
const SAMPLE_INPUT: &str = include_str!("sample_input.txt");
|
||||
|
||||
#[test]
|
||||
fn test_with_solution() {
|
||||
assert_eq!(super::part_2(&crate::parse_input(crate::INPUT)), 451);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_with_sample_solution() {
|
||||
assert_eq!(super::part_2(&crate::parse_input(SAMPLE_INPUT)), 29);
|
||||
}
|
||||
}
|
5
rust/src/day12/sample_input.txt
Normal file
5
rust/src/day12/sample_input.txt
Normal file
|
@ -0,0 +1,5 @@
|
|||
Sabqponm
|
||||
abcryxxl
|
||||
accszExk
|
||||
acctuvwj
|
||||
abdefghi
|
Loading…
Reference in a new issue