day-23 done but could be optimized to quicken part 2
This commit is contained in:
21
2023/Cargo.lock
generated
21
2023/Cargo.lock
generated
@@ -230,6 +230,17 @@ dependencies = [
|
|||||||
"nom",
|
"nom",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "day-23"
|
||||||
|
version = "2023.0.0"
|
||||||
|
dependencies = [
|
||||||
|
"glam",
|
||||||
|
"itertools",
|
||||||
|
"nom",
|
||||||
|
"nom_locate",
|
||||||
|
"petgraph",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "day-3"
|
name = "day-3"
|
||||||
version = "2023.0.0"
|
version = "2023.0.0"
|
||||||
@@ -733,6 +744,16 @@ dependencies = [
|
|||||||
"thiserror",
|
"thiserror",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "petgraph"
|
||||||
|
version = "0.6.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e1d3afd2628e69da2be385eb6f2fd57c8ac7977ceeff6dc166ff1657b0e386a9"
|
||||||
|
dependencies = [
|
||||||
|
"fixedbitset",
|
||||||
|
"indexmap",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pin-project-lite"
|
name = "pin-project-lite"
|
||||||
version = "0.2.13"
|
version = "0.2.13"
|
||||||
|
|||||||
15
2023/day-23/Cargo.toml
Normal file
15
2023/day-23/Cargo.toml
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
[package]
|
||||||
|
name = "day-23"
|
||||||
|
version.workspace = true
|
||||||
|
edition.workspace = true
|
||||||
|
authors.workspace = true
|
||||||
|
repository.workspace = true
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
nom = { workspace = true }
|
||||||
|
itertools = {workspace = true }
|
||||||
|
glam.workspace = true
|
||||||
|
nom_locate.workspace = true
|
||||||
|
petgraph = "0.6.4"
|
||||||
4
2023/day-23/src/lib.rs
Normal file
4
2023/day-23/src/lib.rs
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
pub mod part1;
|
||||||
|
pub use crate::part1::*;
|
||||||
|
pub mod part2;
|
||||||
|
pub use crate::part2::*;
|
||||||
12
2023/day-23/src/main.rs
Normal file
12
2023/day-23/src/main.rs
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
#![warn(clippy::all, clippy::pedantic)]
|
||||||
|
|
||||||
|
use day_23::part1;
|
||||||
|
use day_23::part2;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let input = include_str!("./input.txt");
|
||||||
|
let part1_result = part1(input);
|
||||||
|
println!("part 1: {part1_result}");
|
||||||
|
let part2_result = part2(input);
|
||||||
|
println!("part 2: {part2_result}");
|
||||||
|
}
|
||||||
107
2023/day-23/src/part1.rs
Normal file
107
2023/day-23/src/part1.rs
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
#![warn(clippy::all, clippy::pedantic)]
|
||||||
|
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use glam::IVec2;
|
||||||
|
use petgraph::{prelude::*, algo};
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
enum PointType {
|
||||||
|
Any,
|
||||||
|
OnlyDown,
|
||||||
|
OnlyLeft,
|
||||||
|
OnlyRight,
|
||||||
|
OnlyUp,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PointType {
|
||||||
|
fn next_possibles(self) -> Vec<IVec2> {
|
||||||
|
match self {
|
||||||
|
PointType::Any => vec![IVec2::X, IVec2::Y, IVec2::NEG_X, IVec2::NEG_Y],
|
||||||
|
PointType::OnlyDown => vec![IVec2::Y],
|
||||||
|
PointType::OnlyLeft => vec![IVec2::NEG_X],
|
||||||
|
PointType::OnlyRight => vec![IVec2::X],
|
||||||
|
PointType::OnlyUp => vec![IVec2::NEG_Y],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn part1(input: &str) -> String {
|
||||||
|
let maze = parse_input(input);
|
||||||
|
//get the start position (assuming there is only one)
|
||||||
|
let start = *maze.keys().find(|pos| pos.y == 0).unwrap();
|
||||||
|
let end = maze.keys().fold(IVec2::splat(0), | max, current| if max.y.max(current.y) == current.y { *current } else {max});
|
||||||
|
let mut maze_graph = DiGraph::<&PointType, u32>::new();
|
||||||
|
let node_map = maze.iter().map(|(pos, point_type)| (pos, maze_graph.add_node(point_type)) ).collect::<HashMap<_,_>>();
|
||||||
|
|
||||||
|
maze.iter().flat_map(|(pos, point_type)| {
|
||||||
|
point_type.next_possibles().iter().copied().filter_map(|dir| {
|
||||||
|
let next_pos = dir + *pos;
|
||||||
|
node_map.get(&next_pos).is_some().then(|| (node_map[pos], node_map[&next_pos], 1))
|
||||||
|
}).collect::<Vec<_>>()
|
||||||
|
})
|
||||||
|
.for_each(|(a, b, weight)| {
|
||||||
|
maze_graph.add_edge(a,b,weight);
|
||||||
|
});
|
||||||
|
|
||||||
|
(algo::all_simple_paths::<Vec<_>,_>(&maze_graph, node_map[&start], node_map[&end], 0, None).max_by(|a, b| a.len().cmp(&b.len())).unwrap().len() -1).to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_input(input: &str) -> HashMap<IVec2, PointType> {
|
||||||
|
input
|
||||||
|
.lines()
|
||||||
|
.enumerate()
|
||||||
|
.flat_map(|(y, row)| {
|
||||||
|
row.chars().enumerate().filter_map(move |(x, c)| {
|
||||||
|
let pos = IVec2::new(i32::try_from(x).unwrap(), i32::try_from(y).unwrap());
|
||||||
|
match c {
|
||||||
|
'.' => Some((pos, PointType::Any)),
|
||||||
|
'>' => Some((pos, PointType::OnlyRight)),
|
||||||
|
'v' => Some((pos, PointType::OnlyDown)),
|
||||||
|
'^' => Some((pos, PointType::OnlyUp)),
|
||||||
|
'<' => Some((pos, PointType::OnlyLeft)),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
const INPUT: &str = "#.#####################
|
||||||
|
#.......#########...###
|
||||||
|
#######.#########.#.###
|
||||||
|
###.....#.>.>.###.#.###
|
||||||
|
###v#####.#v#.###.#.###
|
||||||
|
###.>...#.#.#.....#...#
|
||||||
|
###v###.#.#.#########.#
|
||||||
|
###...#.#.#.......#...#
|
||||||
|
#####.#.#.#######.#.###
|
||||||
|
#.....#.#.#.......#...#
|
||||||
|
#.#####.#.#.#########v#
|
||||||
|
#.#...#...#...###...>.#
|
||||||
|
#.#.#v#######v###.###v#
|
||||||
|
#...#.>.#...>.>.#.###.#
|
||||||
|
#####v#.#.###v#.#.###.#
|
||||||
|
#.....#...#...#.#.#...#
|
||||||
|
#.#########.###.#.#.###
|
||||||
|
#...###...#...#...#.###
|
||||||
|
###.###.#.###v#####v###
|
||||||
|
#...#...#.#.>.>.#.>.###
|
||||||
|
#.###.###.#.###.#.#v###
|
||||||
|
#.....###...###...#...#
|
||||||
|
#####################.#";
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn part1_works() {
|
||||||
|
let result = part1(INPUT);
|
||||||
|
assert_eq!(result, "94".to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
103
2023/day-23/src/part2.rs
Normal file
103
2023/day-23/src/part2.rs
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
#![warn(clippy::all, clippy::pedantic)]
|
||||||
|
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use glam::IVec2;
|
||||||
|
use petgraph::{prelude::*, algo};
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
enum PointType {
|
||||||
|
Any,
|
||||||
|
OnlyDown,
|
||||||
|
OnlyLeft,
|
||||||
|
OnlyRight,
|
||||||
|
OnlyUp,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PointType {
|
||||||
|
fn next_possibles(self) -> Vec<IVec2> {
|
||||||
|
match self {
|
||||||
|
_ => vec![IVec2::X, IVec2::Y, IVec2::NEG_X, IVec2::NEG_Y],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn part2(input: &str) -> String {
|
||||||
|
let maze = parse_input(input);
|
||||||
|
//get the start position (assuming there is only one)
|
||||||
|
let start = *maze.keys().find(|pos| pos.y == 0).unwrap();
|
||||||
|
let end = maze.keys().fold(IVec2::splat(0), | max, current| if max.y.max(current.y) == current.y { *current } else {max});
|
||||||
|
let mut maze_graph = DiGraph::<&PointType, u32>::new();
|
||||||
|
let node_map = maze.iter().map(|(pos, point_type)| (pos, maze_graph.add_node(point_type)) ).collect::<HashMap<_,_>>();
|
||||||
|
|
||||||
|
maze.iter().flat_map(|(pos, point_type)| {
|
||||||
|
point_type.next_possibles().iter().copied().filter_map(|dir| {
|
||||||
|
let next_pos = dir + *pos;
|
||||||
|
node_map.get(&next_pos).is_some().then(|| (node_map[pos], node_map[&next_pos], 1))
|
||||||
|
}).collect::<Vec<_>>()
|
||||||
|
})
|
||||||
|
.for_each(|(a, b, weight)| {
|
||||||
|
maze_graph.add_edge(a,b,weight);
|
||||||
|
});
|
||||||
|
|
||||||
|
(algo::all_simple_paths::<Vec<_>,_>(&maze_graph, node_map[&start], node_map[&end], 0, None).max_by(|a, b| a.len().cmp(&b.len())).unwrap().len() -1).to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_input(input: &str) -> HashMap<IVec2, PointType> {
|
||||||
|
input
|
||||||
|
.lines()
|
||||||
|
.enumerate()
|
||||||
|
.flat_map(|(y, row)| {
|
||||||
|
row.chars().enumerate().filter_map(move |(x, c)| {
|
||||||
|
let pos = IVec2::new(i32::try_from(x).unwrap(), i32::try_from(y).unwrap());
|
||||||
|
match c {
|
||||||
|
'.' => Some((pos, PointType::Any)),
|
||||||
|
'>' => Some((pos, PointType::OnlyRight)),
|
||||||
|
'v' => Some((pos, PointType::OnlyDown)),
|
||||||
|
'^' => Some((pos, PointType::OnlyUp)),
|
||||||
|
'<' => Some((pos, PointType::OnlyLeft)),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
const INPUT: &str = "#.#####################
|
||||||
|
#.......#########...###
|
||||||
|
#######.#########.#.###
|
||||||
|
###.....#.>.>.###.#.###
|
||||||
|
###v#####.#v#.###.#.###
|
||||||
|
###.>...#.#.#.....#...#
|
||||||
|
###v###.#.#.#########.#
|
||||||
|
###...#.#.#.......#...#
|
||||||
|
#####.#.#.#######.#.###
|
||||||
|
#.....#.#.#.......#...#
|
||||||
|
#.#####.#.#.#########v#
|
||||||
|
#.#...#...#...###...>.#
|
||||||
|
#.#.#v#######v###.###v#
|
||||||
|
#...#.>.#...>.>.#.###.#
|
||||||
|
#####v#.#.###v#.#.###.#
|
||||||
|
#.....#...#...#.#.#...#
|
||||||
|
#.#########.###.#.#.###
|
||||||
|
#...###...#...#...#.###
|
||||||
|
###.###.#.###v#####v###
|
||||||
|
#...#...#.#.>.>.#.>.###
|
||||||
|
#.###.###.#.###.#.#v###
|
||||||
|
#.....###...###...#...#
|
||||||
|
#####################.#";
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn part2_works() {
|
||||||
|
let result = part2(INPUT);
|
||||||
|
assert_eq!(result, "154".to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
Reference in New Issue
Block a user