From 8a68bb1dc2ab2768704287db9cd54ac93f09f12d Mon Sep 17 00:00:00 2001 From: mflinn-broad Date: Sun, 12 Dec 2021 10:42:46 -0500 Subject: [PATCH] finish day 12 --- Cargo.lock | 39 +++++++++++++ Cargo.toml | 1 + src/days/day11.rs | 36 ++++++------ src/days/day12.rs | 144 ++++++++++++++++++++++++++++++++++++++++++++++ src/days/mod.rs | 1 + src/main.rs | 7 +++ 6 files changed, 210 insertions(+), 18 deletions(-) create mode 100644 src/days/day12.rs diff --git a/Cargo.lock b/Cargo.lock index 976f527..cfcebd6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7,14 +7,43 @@ name = "advent-2021" version = "0.1.0" dependencies = [ "itertools", + "petgraph", ] +[[package]] +name = "autocfg" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" + [[package]] name = "either" version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" +[[package]] +name = "fixedbitset" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "398ea4fabe40b9b0d885340a2a991a44c8a645624075ad966d21f88688e2b69e" + +[[package]] +name = "hashbrown" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" + +[[package]] +name = "indexmap" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc633605454125dec4b66843673f01c7df2b89479b32e0ed634e43a91cff62a5" +dependencies = [ + "autocfg", + "hashbrown", +] + [[package]] name = "itertools" version = "0.10.3" @@ -23,3 +52,13 @@ checksum = "a9a9d19fa1e79b6215ff29b9d6880b706147f16e9b1dbb1e4e5947b5b02bc5e3" dependencies = [ "either", ] + +[[package]] +name = "petgraph" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a13a2fa9d0b63e5f22328828741e523766fff0ee9e779316902290dff3f824f" +dependencies = [ + "fixedbitset", + "indexmap", +] diff --git a/Cargo.toml b/Cargo.toml index bf5260b..334aeb2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,3 +7,4 @@ edition = "2021" [dependencies] itertools = "0.10.3" +petgraph = "0.6.0" diff --git a/src/days/day11.rs b/src/days/day11.rs index 7768978..def72b0 100644 --- a/src/days/day11.rs +++ b/src/days/day11.rs @@ -15,7 +15,7 @@ struct Octopus { struct Position { row: usize, - col: usize + col: usize, } impl Octopus { @@ -36,28 +36,28 @@ fn get_adjacent_octopi(pos: Position, grid: &Vec>) -> Vec if pos.row != 0 { let position = Position { row: pos.row - 1, - col: pos.col + col: pos.col, }; adjacents.push(position); } if pos.col != 0 { let position = Position { row: pos.row, - col: pos.col - 1 + col: pos.col - 1, }; adjacents.push(position); } if pos.row != (grid.len() - 1) { let position = Position { row: pos.row + 1, - col: pos.col + col: pos.col, }; adjacents.push(position); } if pos.col != (grid[0].len() - 1) { let position = Position { row: pos.row, - col: pos.col + 1 + col: pos.col + 1, }; adjacents.push(position); } @@ -100,19 +100,18 @@ fn propagate_flash(pos: Position, grid: &mut Vec>) { let mut flash_stack: Vec = vec![pos]; while let Some(flash_pos) = flash_stack.pop() { let adjacents = get_adjacent_octopi(flash_pos, &grid); - adjacents.iter() - .for_each(|adjacent| { - if !grid[adjacent.row][adjacent.col].flashed_on_step { - grid[adjacent.row][adjacent.col].increase(); - if grid[adjacent.row][adjacent.col].e_level > 9 { - grid[adjacent.row][adjacent.col].flashed_on_step = true; - flash_stack.push(Position { - row: adjacent.row, - col: adjacent.col, - }); - } + adjacents.iter().for_each(|adjacent| { + if !grid[adjacent.row][adjacent.col].flashed_on_step { + grid[adjacent.row][adjacent.col].increase(); + if grid[adjacent.row][adjacent.col].e_level > 9 { + grid[adjacent.row][adjacent.col].flashed_on_step = true; + flash_stack.push(Position { + row: adjacent.row, + col: adjacent.col, + }); } - }) + } + }) } } @@ -182,7 +181,8 @@ fn part_2(input: Vec>) -> usize { } fn process(input: &str) -> Vec> { - input.lines() + input + .lines() .map(|line| { line.chars() .map(|ch| { diff --git a/src/days/day12.rs b/src/days/day12.rs new file mode 100644 index 0000000..3c123ce --- /dev/null +++ b/src/days/day12.rs @@ -0,0 +1,144 @@ +use crate::util; +use petgraph::graphmap::UnGraphMap; +use std::collections::HashSet; + +pub fn run() { + let raw_input = util::read_input("inputs/day12.txt").unwrap(); + let input = process(&raw_input); + + println!("Part 1: {}", part_1(&input)); + println!("Part 2: {}", part_2(&input)); +} + +fn process(input: &str) -> Vec<(&str, &str)> { + input + .lines() + .map(|line| line.trim().split_once("-").unwrap()) + .collect() +} + +fn part_1(input: &Vec<(&str, &str)>) -> usize { + let cave_map = UnGraphMap::<&str, ()>::from_edges(input); + let mut visit_tracker: HashSet<&str> = HashSet::new(); + visit_tracker.insert("start"); + + count_simple_paths(&cave_map, "start", &mut visit_tracker) +} + +fn part_2(input: &Vec<(&str, &str)>) -> usize { + let cave_map = UnGraphMap::<&str, ()>::from_edges(input); + let mut visit_tracker: HashSet<&str> = HashSet::new(); + visit_tracker.insert("start"); + + count_paths_v2(&cave_map, "start", &mut visit_tracker, &mut None) +} + +fn is_small_cave(cave: &str) -> bool { + cave.chars().all(|c| c.is_lowercase()) +} + +fn is_start(cave: &str) -> bool { + cave == "start" +} + +fn is_end(cave: &str) -> bool { + cave == "end" +} + +fn count_simple_paths<'a>( + cave_system: &UnGraphMap<&'a str, ()>, + curr_node: &'a str, + visited_small_caves: &mut HashSet<&'a str>, +) -> usize { + if curr_node == "end" { + visited_small_caves.remove(curr_node); + return 1; + } + + let mut count = 0; + + for connected_cave in cave_system.neighbors(curr_node) { + if is_small_cave(connected_cave) { + if visited_small_caves.contains(connected_cave) { + continue; + } else { + visited_small_caves.insert(connected_cave); + } + } + count += count_simple_paths(&cave_system, connected_cave, visited_small_caves); + visited_small_caves.remove(connected_cave); + } + count +} + +fn count_paths_v2<'a>( + cave_system: &UnGraphMap<&'a str, ()>, + curr_node: &'a str, + visited_small_caves: &mut HashSet<&'a str>, + twive_visited_cave: &mut Option<&'a str>, +) -> usize { + if is_end(curr_node) { + visited_small_caves.remove(curr_node); + return 1; + } + + let mut count = 0; + for connected_cave in cave_system.neighbors(curr_node) { + if is_small_cave(connected_cave) { + if !visited_small_caves.contains(connected_cave) { + visited_small_caves.insert(connected_cave); + } else if twive_visited_cave.is_none() + && !is_start(connected_cave) + && !is_end(connected_cave) + { + *twive_visited_cave = Some(connected_cave); + } else { + continue; + } + } + + if let Some(_) = twive_visited_cave { + count += count_simple_paths(cave_system, connected_cave, visited_small_caves); + } else { + count += count_paths_v2( + cave_system, + connected_cave, + visited_small_caves, + twive_visited_cave, + ); + } + + if *twive_visited_cave == Some(connected_cave) { + *twive_visited_cave = None; + } else { + visited_small_caves.remove(connected_cave); + } + } + + count +} + +#[cfg(test)] +mod tests { + use super::*; + extern crate test; + use test::Bencher; + + #[bench] + fn bench_part_1(b: &mut Bencher) { + let raw_input = util::read_input("inputs/day12.txt").unwrap(); + b.iter(|| { + let input = process(&raw_input); + part_1(&input); + }); + } + + #[bench] + fn bench_part_2(b: &mut Bencher) { + let raw_input = util::read_input("inputs/day12.txt").unwrap(); + b.iter(|| { + let input = process(&raw_input); + part_2(&input); + }); + } +} diff --git a/src/days/mod.rs b/src/days/mod.rs index 737f5f6..e4818a0 100644 --- a/src/days/mod.rs +++ b/src/days/mod.rs @@ -1,6 +1,7 @@ pub mod day1; pub mod day10; pub mod day11; +pub mod day12; pub mod day2; pub mod day3; pub mod day4; diff --git a/src/main.rs b/src/main.rs index 6bfc300..db7a06f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,8 @@ use advent_2021::days::*; +use std::time::{Duration, Instant}; fn main() { + let start = Instant::now(); println!("Day 1 --------"); day1::run(); println!("Day 2 --------"); @@ -23,4 +25,9 @@ fn main() { day10::run(); println!("Day 11 ---------"); day11::run(); + println!("Day 12 ---------"); + day12::run(); + let duration = start.elapsed(); + + println!("Total time to run all solutions: {:?}", duration); }