1
0
Fork 0

day(23): add solution

Signed-off-by: Matej Focko <me@mfocko.xyz>
This commit is contained in:
Matej Focko 2024-07-07 20:26:02 +02:00
parent 896a728db6
commit 9a990dc83a
Signed by: mfocko
SSH key fingerprint: SHA256:5YXD7WbPuK60gxnG6DjAwJiS9+swoWj33/HFu8g8JVo
2 changed files with 203 additions and 0 deletions

23
samples/day23.txt Normal file
View file

@ -0,0 +1,23 @@
#.#####################
#.......#########...###
#######.#########.#.###
###.....#.>.>.###.#.###
###v#####.#v#.###.#.###
###.>...#.#.#.....#...#
###v###.#.#.#########.#
###...#.#.#.......#...#
#####.#.#.#######.#.###
#.....#.#.#.......#...#
#.#####.#.#.#########v#
#.#...#...#...###...>.#
#.#.#v#######v###.###v#
#...#.>.#...>.>.#.###.#
#####v#.#.###v#.#.###.#
#.....#...#...#.#.#...#
#.#########.###.#.#.###
#...###...#...#...#.###
###.###.#.###v#####v###
#...#...#.#.>.>.#.>.###
#.###.###.#.###.#.#v###
#.....###...###...#...#
#####################.#

180
src/bin/day23.rs Normal file
View file

@ -0,0 +1,180 @@
use std::collections::{HashMap, HashSet, VecDeque};
use aoc_2023::*;
use itertools::iproduct;
type Output1 = isize;
type Output2 = Output1;
type Coord = Vector2D<isize>;
lazy_static! {
static ref LEFT: Coord = Coord::new(-1, 0);
static ref RIGHT: Coord = Coord::new(1, 0);
static ref UP: Coord = Coord::new(0, -1);
static ref DOWN: Coord = Coord::new(0, 1);
static ref DIRECTIONS: Vec<Coord> = vec![*LEFT, *RIGHT, *UP, *DOWN];
static ref EXITS: HashMap<char, Vec<Coord>> = HashMap::from([
('<', vec![*LEFT]),
('>', vec![*RIGHT]),
('^', vec![*UP]),
('v', vec![*DOWN]),
('.', DIRECTIONS.clone()),
('#', vec![]),
]);
}
#[derive(Debug, Clone, Copy)]
struct Edge {
start: usize,
end: usize,
distance: usize,
}
#[derive(Debug, Clone)]
struct Graph {
nodes: Vec<usize>,
edges: Vec<Edge>,
}
struct Day23 {
graph: Graph,
graph_no_slopes: Graph,
}
fn char_identity(c: char) -> char {
c
}
fn slope_remover(c: char) -> char {
if ">v<^".contains(c) {
'.'
} else {
c
}
}
impl Day23 {
fn make_graph(lines: &[String], transform: &dyn Fn(char) -> char) -> Graph {
let map: HashMap<Coord, char> = lines
.iter()
.enumerate()
.flat_map(|(y, row)| {
row.chars()
.enumerate()
.map(move |(x, c)| (Coord::new(x as isize, y as isize), transform(c)))
})
.collect();
let is_free = |p| map.get(&p).is_some_and(|&c| c != '#');
let is_road = |p| is_free(p) && DIRECTIONS.iter().filter(|&&d| is_free(p + d)).count() == 2;
let distance = |src, dst| {
let mut q: VecDeque<(Vector2D<isize>, usize)> = VecDeque::new();
q.push_back((src, 0));
let mut visited = HashSet::new();
visited.insert(src);
while let Some((u, d)) = q.pop_front() {
for direction in &EXITS[&map[&u]] {
let v = u + *direction;
if v == dst {
return Some(d + 1);
} else if is_road(v) && !visited.contains(&v) {
visited.insert(v);
q.push_back((v, d + 1));
}
}
}
None
};
let nodes_coords = map
.keys()
.cloned()
.filter(|p| is_free(*p) && !is_road(*p))
.sorted_by_key(|p| (p.y(), p.x()))
.collect_vec();
let nodes = (0..nodes_coords.len()).map(|i| 1_usize << i).collect_vec();
let edges = iproduct!(0..nodes_coords.len(), 0..nodes_coords.len())
.filter(|(i, j)| i != j)
.filter_map(|(i, j)| {
distance(nodes_coords[i], nodes_coords[j]).map(|d| Edge {
start: nodes[i],
end: nodes[j],
distance: d,
})
})
.collect_vec();
Graph { nodes, edges }
}
fn solve(g: &Graph) -> isize {
let start = *g.nodes.first().unwrap();
let goal = *g.nodes.last().unwrap();
fn longest_path(
cache: &mut HashMap<(usize, usize), isize>,
edges: &[Edge],
goal: usize,
u: usize,
visited: usize,
) -> isize {
if u == goal {
return 0;
} else if (visited & u) != 0 {
return i32::MIN as isize;
}
let key = (u, visited);
let missing = !cache.contains_key(&key);
let distance = missing.then(|| {
edges
.iter()
.filter_map(|e| {
(e.start == u).then(|| {
e.distance as isize
+ longest_path(cache, edges, goal, e.end, visited | u)
})
})
.max()
.unwrap()
});
*cache.entry(key).or_insert_with(|| distance.unwrap())
}
let mut cache: HashMap<(usize, usize), isize> = HashMap::new();
longest_path(&mut cache, &g.edges, goal, start, 0)
}
}
impl Solution<Output1, Output2> for Day23 {
fn new<P: AsRef<Path>>(pathname: P) -> Self {
let lines: Vec<String> = file_to_lines(pathname);
let graph = Self::make_graph(&lines, &char_identity);
let graph_no_slopes = Self::make_graph(&lines, &slope_remover);
Self {
graph,
graph_no_slopes,
}
}
fn part_1(&mut self) -> Output1 {
Self::solve(&self.graph)
}
fn part_2(&mut self) -> Output2 {
Self::solve(&self.graph_no_slopes)
}
}
fn main() -> Result<()> {
Day23::main()
}
test_sample!(day_23, Day23, 94, 154);