day23: problem 1 solution

This commit is contained in:
2023-12-22 22:33:34 -08:00
parent d6d4c0d056
commit 98456ed98d
3 changed files with 375 additions and 0 deletions

236
23/src/main.rs Normal file
View File

@ -0,0 +1,236 @@
use itertools::Itertools;
use ndarray::prelude::*;
use petgraph::algo::all_simple_paths;
use petgraph::prelude::*;
use std::collections::HashMap;
use std::fmt::{Debug, Display, Write};
use std::fs::File;
use std::io::{BufRead, BufReader, Lines};
use std::time::Instant;
// BOILERPLATE
type InputIter = Lines<BufReader<File>>;
fn get_input() -> InputIter {
let f = File::open("input").unwrap();
let br = BufReader::new(f);
br.lines()
}
fn main() {
let start = Instant::now();
let ans1 = problem1(get_input());
let duration = start.elapsed();
println!("Problem 1 solution: {} [{}s]", ans1, duration.as_secs_f64());
let start = Instant::now();
let ans2 = problem2(get_input());
let duration = start.elapsed();
println!("Problem 2 solution: {} [{}s]", ans2, duration.as_secs_f64());
}
// PARSE
#[derive(Debug, Clone)]
enum EdgeType {
FromPath,
FromSlope,
}
#[derive(Debug, Clone)]
struct Node {
c: char,
pos: Position,
}
type Position = (usize, usize);
#[derive(Clone)]
struct ForestMap {
map: Array2<char>,
indexes: HashMap<Position, NodeIndex>,
graph: DiGraph<Node, EdgeType>,
start: Position,
end: Position,
}
const ADJACENCIES: [(isize, isize); 4] = [(-1, 0), (1, 0), (0, -1), (0, 1)];
fn offset_pos(map: &Array2<char>, pos: Position, ofs: (isize, isize)) -> Option<Position> {
let new_pos = (pos.0 as isize + ofs.0, pos.1 as isize + ofs.1);
if new_pos.0 >= 0
&& new_pos.0 < map.len_of(Axis(0)) as isize
&& new_pos.1 >= 0
&& new_pos.1 < map.len_of(Axis(1)) as isize
{
Some((new_pos.0 as usize, new_pos.1 as usize))
} else {
None
}
}
fn adjacent_to(map: &Array2<char>, pos: Position) -> Vec<Position> {
ADJACENCIES
.iter()
.filter_map(|ofs| offset_pos(map, pos, *ofs))
.collect()
}
impl<T: BufRead> From<Lines<T>> for ForestMap {
fn from(lines: Lines<T>) -> Self {
let rows = lines.map(|line| line.unwrap().chars().collect_vec()).collect_vec();
let map = Array::from_shape_vec([rows[0].len(), rows.len()], rows.into_iter().flatten().collect_vec())
.unwrap()
.reversed_axes();
let start = (map.slice(s![.., 0]).iter().position(|c| *c == '.').unwrap(), 0);
let end = (
map.slice(s![.., map.len_of(Axis(1)) - 1])
.iter()
.position(|c| *c == '.')
.unwrap(),
map.len_of(Axis(1)) - 1,
);
let mut graph = Graph::default();
let mut indexes = HashMap::new();
for (pos, c) in map.indexed_iter() {
if *c != '#' {
indexes.insert(pos, graph.add_node(Node { c: *c, pos }));
}
}
for (pos, c) in map.indexed_iter() {
match c {
'#' => continue,
'.' => {
adjacent_to(&map, pos).iter().for_each(|adj| {
if indexes.contains_key(&adj) {
graph.add_edge(indexes[&pos], indexes[adj], EdgeType::FromPath);
}
});
}
'^' => {
if let Some(adj) = offset_pos(&map, pos, (0, -1)) {
if indexes.contains_key(&adj) {
graph.add_edge(indexes[&pos], indexes[&adj], EdgeType::FromSlope);
}
}
}
'>' => {
if let Some(adj) = offset_pos(&map, pos, (1, 0)) {
if indexes.contains_key(&adj) {
graph.add_edge(indexes[&pos], indexes[&adj], EdgeType::FromSlope);
}
}
}
'v' => {
if let Some(adj) = offset_pos(&map, pos, (0, 1)) {
if indexes.contains_key(&adj) {
graph.add_edge(indexes[&pos], indexes[&adj], EdgeType::FromSlope);
}
}
}
'<' => {
if let Some(adj) = offset_pos(&map, pos, (-1, 0)) {
if indexes.contains_key(&adj) {
graph.add_edge(indexes[&pos], indexes[&adj], EdgeType::FromSlope);
}
}
}
c => panic!("invalid map character {}", c),
}
}
Self {
map,
start,
end,
graph,
indexes,
}
}
}
impl Debug for ForestMap {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
for y in 0..self.map.len_of(Axis(1)) {
for c in self.map.index_axis(Axis(1), y) {
f.write_char(*c)?;
}
writeln!(f)?;
}
// println!("start: {:?} end: {:?}", self.start, self.end);
// println!("digraph aoc23 {{");
// for node in self.graph.node_indices() {
// println!(
// " \"{},{}\" -> {}",
// self.graph[node].pos.0,
// self.graph[node].pos.1,
// self.graph
// .neighbors(node)
// .map(|n| format!("\"{},{}\"", self.graph[n].pos.0, self.graph[n].pos.1))
// .join(",")
// );
// }
// println!("}}");
Ok(())
}
}
// PROBLEM 1 solution
fn problem1<T: BufRead>(input: Lines<T>) -> u64 {
let map = ForestMap::from(input);
println!("{:?}", map);
let paths = all_simple_paths::<Vec<_>, _>(&map.graph, map.indexes[&map.start], map.indexes[&map.end], 0, None)
.collect_vec();
let longest = paths.iter().max_by_key(|path| path.len()).unwrap();
longest.len() as u64 - 1
}
// PROBLEM 2 solution
fn problem2<T: BufRead>(input: Lines<T>) -> u64 {
0
}
#[cfg(test)]
mod tests {
use crate::*;
use std::io::Cursor;
const EXAMPLE: &str = &"#.#####################
#.......#########...###
#######.#########.#.###
###.....#.>.>.###.#.###
###v#####.#v#.###.#.###
###.>...#.#.#.....#...#
###v###.#.#.#########.#
###...#.#.#.......#...#
#####.#.#.#######.#.###
#.....#.#.#.......#...#
#.#####.#.#.#########v#
#.#...#...#...###...>.#
#.#.#v#######v###.###v#
#...#.>.#...>.>.#.###.#
#####v#.#.###v#.#.###.#
#.....#...#...#.#.#...#
#.#########.###.#.#.###
#...###...#...#...#.###
###.###.#.###v#####v###
#...#...#.#.>.>.#.>.###
#.###.###.#.###.#.#v###
#.....###...###...#...#
#####################.#";
#[test]
fn problem1_example() {
let c = Cursor::new(EXAMPLE);
assert_eq!(problem1(c.lines()), 94);
}
#[test]
fn problem2_example() {
let c = Cursor::new(EXAMPLE);
assert_eq!(problem2(c.lines()), 0);
}
}