diff --git a/src/bin/day16.rs b/src/bin/day16.rs index 7db9460..bc6422b 100644 --- a/src/bin/day16.rs +++ b/src/bin/day16.rs @@ -59,7 +59,7 @@ type FlowGraph = UnGraphMap; #[derive(Debug, PartialEq)] enum Action { - Move(String), + Move(RoomId), Open, Idle, } @@ -94,14 +94,18 @@ impl Volcano { FlowGraph::from_edges(&edges) } - fn path_between(&self, start: &str, end: &str) -> Vec { - let start = RoomId::new(start.to_string()); - let end = RoomId::new(end.to_string()); + fn path_between(&self, start: &RoomId, end: &RoomId) -> Vec { let graph = self.graph.clone(); - let path = bfs(&start, |p| successors(p, &graph), |p| p == &end).unwrap(); + let path = bfs(start, |p| successors(p, &graph), |p| p == end).unwrap(); path[1..].to_vec() } + fn path_between_str(&self, start: &str, end: &str) -> Vec { + let start = RoomId::new(start.to_string()); + let end = RoomId::new(end.to_string()); + self.path_between(&start, &end) + } + fn rooms_with_valves(&self) -> Vec { self.rooms .values() @@ -110,8 +114,77 @@ impl Volcano { } fn current_flow(&self, open_valves: &OpenValves) -> usize { - open_valves.iter().map(|room_id| self.rooms.get(room_id).expect("room").flow).sum() + open_valves + .iter() + .map(|room_id| self.rooms.get(room_id).expect("room").flow) + .sum() } + + fn actions(&self, path: &[RoomId]) -> Vec { + let mut actions = vec![]; + for room_id in path.iter() { + actions.push(Action::Move(*room_id)); + } + actions.push(Action::Open); + actions + } +} + +fn to_string(path: &[&RoomId]) -> String { + path.iter() + .map(|r| r.as_ref().to_string()) + .collect::>() + .join(",") +} + +fn to_path(path: &[&str]) -> Vec { + path.iter() + .map(|r| Intern::new(r.to_string())) + .collect::>() +} + +fn to_ref_path<'a>(path: &'a [RoomId]) -> Vec<&'a RoomId> { + path.iter().map(|r| r).collect::>() +} + +fn solve(volcano: &Volcano, start: &RoomId, path: &[&RoomId], limit: usize) -> usize { + let mut total_pressure = 0; + let mut open_valves = OpenValves::default(); + let mut player_location = *start; + let mut time = 1; + for next_destination in path { + let path_to_next_destination = volcano.path_between(&player_location, next_destination); + let actions = volcano.actions(path_to_next_destination.as_slice()); + for action in actions { + let current_flow = volcano.current_flow(&open_valves); + total_pressure += current_flow; + match &action { + Action::Move(t) => { + player_location = *t; + } + + Action::Open => { + open_valves.insert(player_location); + } + + Action::Idle => (), + } + time += 1; + + if time > limit { + // println!("#### solving {:?}", to_string(path)); + // println!("### ran out of time"); + return total_pressure; + } + } + } + while time <= limit { + let current_flow = volcano.current_flow(&open_valves); + total_pressure += current_flow; + time += 1; + } + + total_pressure } fn parse(s: &str) -> Volcano { @@ -172,7 +245,7 @@ impl From<&str> for ExampleStep { let re = Regex::new(r"You ([a-z]+).*valve ([A-Z][A-Z]).").expect("re"); let captures = re.captures(parts[2]).expect("captures"); action = match &captures[1] { - "move" => Action::Move(captures[2].to_string()), + "move" => Action::Move(RoomId::new(captures[2].to_string())), "open" => Action::Open, _ => Action::Idle, }; @@ -218,15 +291,19 @@ fn main() -> Result<(), Error> { let rooms = volcano.rooms_with_valves(); println!("{} rooms, {:?}", rooms.len(), rooms); - for one_permutation in rooms.iter().permutations(rooms.len()) { - println!( - "one_permutation, {:?}", - one_permutation - .iter() - .map(|r_id| r_id.as_ref()) - .collect::>() - ); - } + let start_room = RoomId::new("AA".to_string()); + + let mut solutions: Vec<_> = rooms + .iter() + .permutations(rooms.len().min(6)) + .map(|path| (solve(&volcano, &start_room, path.as_slice(), 30), path.clone())) + .collect(); + + solutions.sort_by_key(|s| s.0); + + solutions.reverse(); + + println!("total pressure = {}", solutions[0].0); } Ok(()) @@ -251,12 +328,18 @@ mod test { .collect(); assert_eq!(example_steps.len(), 30); - assert_eq!(example_steps[0].action, Action::Move("DD".to_string())); + assert_eq!( + example_steps[0].action, + Action::Move(RoomId::new("DD".to_string())) + ); assert_eq!(example_steps[0].pressure, 0); assert_eq!(example_steps[0].open_valves.len(), 0); let middle_step = &example_steps[17]; - assert_eq!(middle_step.action, Action::Move("GG".to_string())); + assert_eq!( + middle_step.action, + Action::Move(RoomId::new("GG".to_string())) + ); assert_eq!(middle_step.pressure, 76); assert_eq!(middle_step.open_valves.len(), 4); @@ -299,23 +382,35 @@ mod test { fn test_volcano() { let mut v = parse(SAMPLE); - let path = v.path_between("AA", "HH"); + let path = v.path_between_str("AA", "HH"); assert_eq!(path.len(), 5); dbg!(&path); } #[test] - #[ignore] - fn test_permute() { - let mut v = parse(SAMPLE); + fn test_permute_solve() { + let v = parse(SAMPLE); + let start_room = RoomId::new("AA".to_string()); let rooms = v.rooms_with_valves(); - let b: Vec<_> = rooms.iter().permutations(rooms.len()).collect(); + let one_path = to_path(&["DD", "BB", "JJ", "HH", "EE", "CC"]); + let one_solution = solve(&v, &start_room, &to_ref_path(one_path.as_slice()), 30); + assert_eq!(one_solution, 1651); + + let mut solutions: Vec<_> = rooms + .iter() + .permutations(rooms.len()) + .map(|path| (solve(&v, &start_room, path.as_slice(), 30), path.clone())) + .collect(); + + solutions.sort_by_key(|s| s.0); + + solutions.reverse(); - println!("{} permutations, {:?}", b.len(), b); + dbg!(&solutions[0..10]); - todo!(); + assert_eq!(solutions[0].1, to_ref_path(one_path.as_slice())); } }