Day 11: Reactor
Megathread guidelines
- Keep top level comments as only solutions, if you want to say something other than a solution put it in a new post. (replies to comments can be whatever)
- You can send code in code blocks by using three backticks, the code, and then three backticks or use something such as https://topaz.github.io/paste/ if you prefer sending it through a URL
FAQ
- What is this?: Here is a post with a large amount of details: https://programming.dev/post/6637268
- Where do I participate?: https://adventofcode.com/
- Is there a leaderboard for the community?: We have a programming.dev leaderboard with the info on how to join in this post: https://programming.dev/post/6631465


Rust
Part 1 was very straight forward. I overcomplicated it a bit by using an actual graph library, but I’m used to using it.
I originally tried to brute force Part 2, which of course failed. I then reverted to dynamic programming, which worked well.
use std::collections::{HashMap, VecDeque}; use graphrs::{Graph, GraphSpecs}; use crate::solver::DaySolver; pub struct Day11Solver; impl DaySolver for Day11Solver { fn part1(&self, input: String) -> String { let mut graph: Graph<String, ()> = Graph::new(GraphSpecs::directed_create_missing()); let edges = input.lines() .flat_map(|line| { let (start, end_str) = line.split_once(": ").unwrap(); end_str.split_whitespace() .map(|end| (start.to_string(), end.to_string())) }) .collect(); graph.add_edge_tuples(edges).unwrap(); let mut frontier = VecDeque::from([vec!["you".to_string()]]); let mut path_count = 0; while let Some(path) = frontier.pop_front() { let last = path.last().unwrap(); if last == "out" { path_count += 1; } else { graph .get_successor_node_names(last.to_string()) .unwrap() .into_iter() .filter(|next| !path.contains(next)) .map(|next| { let mut new_path = path.clone(); new_path.push(next.clone()); new_path }) .for_each(|new_path| frontier.push_back(new_path)); } } path_count.to_string() } fn part2(&self, input: String) -> String { let mut graph: Graph<String, ()> = Graph::new(GraphSpecs::directed_create_missing()); let edges = input.lines() .flat_map(|line| { let (start, end_str) = line.split_once(": ").unwrap(); end_str.split_whitespace() .map(|end| (start.to_string(), end.to_string())) }) .collect(); graph.add_edge_tuples(edges).unwrap(); how_many_paths( &mut HashMap::new(), &graph, ("svr".to_string(), false, false), ) .to_string() } } fn how_many_paths( cache: &mut HashMap<(String, bool, bool), usize>, graph: &Graph<String, ()>, current: (String, bool, bool), ) -> usize { if let Some(&c) = cache.get(¤t) { c } else { let (node, has_dac, has_fft) = ¤t; if node == "out" { let count = if *has_dac && *has_fft { 1 } else { 0 }; cache.insert(current, count); count } else { let count = graph .get_successor_node_names(node.clone()) .unwrap() .into_iter() .map(|next| { let next_state = (next.to_string(), *has_dac || next == "dac", *has_fft || next == "fft"); how_many_paths(cache, graph, next_state) }) .sum(); cache.insert(current, count); count } } } #[cfg(test)] mod tests { use super::*; #[test] fn part1() { let input = include_str!("../../inputs/test/11"); let solver = Day11Solver {}; assert_eq!("5", solver.part1(input.to_string())); } #[test] fn part2() { let input = include_str!("../../inputs/test/11-2"); let solver = Day11Solver {}; assert_eq!("2", solver.part2(input.to_string())); } }