Advertisement
nairby

Day 12 Code

Dec 12th, 2021 (edited)
218
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Rust 4.16 KB | None | 0 0
  1. use std::env;
  2. use std::io::{self, prelude::*, BufReader};
  3. use std::fs::File;
  4. use std::collections::{VecDeque,HashSet,HashMap};
  5.  
  6. enum CaveType {
  7.     Start,
  8.     End,
  9.     Large,
  10.     Small,
  11. }
  12. impl CaveType {
  13.     pub fn type_from(name: &str) -> Self {
  14.         match name {
  15.             "start" => CaveType::Start,
  16.             "end"   => CaveType::End,
  17.             other => match other.chars().next() {
  18.                 Some('a'..='z') => CaveType::Small,
  19.                 Some('A'..='Z') => CaveType::Large,
  20.                 other => panic!("Unknown character: {:?}", other),
  21.             }
  22.         }
  23.     }
  24. }
  25.  
  26. struct Cave {
  27.     name: String,
  28.     connections: Vec<String>,
  29. }
  30. impl From<&str> for Cave {
  31.     fn from(input: &str) -> Self {
  32.         Self {
  33.             name: input.to_string(),
  34.             connections: Vec::new(),
  35.         }
  36.     }
  37. }
  38.  
  39. // Checks whether any small caves have been revisited along this path already
  40. fn has_revisited_small_cave(path: &String) -> bool {
  41.     let mut small_cave_visits: HashMap<String,usize> = HashMap::new();
  42.     let small_caves = path.split("-").filter(|c| match CaveType::type_from(c) { CaveType::Small => true, _ => false});
  43.     for small_cave in small_caves {
  44.         *small_cave_visits.entry(small_cave.to_string()).or_insert(0) += 1;
  45.     }
  46.     small_cave_visits.iter().filter(|&(_,v)| *v > 1).count() > 0
  47. }
  48.  
  49. fn count_paths(caves: &Vec<Cave>, allow_revisit: bool) -> usize {
  50.     let mut paths: HashSet<String> = HashSet::new();
  51.     let mut q: VecDeque<String> = VecDeque::new();
  52.     q.push_back("start".to_string());
  53.     while q.len() > 0 {
  54.        
  55.         // Get current cave name and data
  56.         let current_path = q.pop_front().unwrap();
  57.         let current_name = current_path.split("-").last().unwrap();
  58.         let current_cave = &caves.iter().filter(|c| c.name == current_name).next().unwrap();
  59.        
  60.         // Investigate connections
  61.         for connection in &current_cave.connections {
  62.             let new_path = format!("{}-{}",current_path,connection);
  63.             match CaveType::type_from(&connection) {
  64.                 CaveType::Start => {}, // can't revisit start
  65.                 CaveType::End   => { paths.insert(new_path); },
  66.                 CaveType::Large => { q.push_back(new_path); },
  67.                 CaveType::Small => {
  68.                     // Count instances of this connection's name in current path
  69.                     let count = current_path.split("-").filter(|c| c == connection).count();
  70.                     match count {
  71.                         0 => q.push_back(new_path), // first visit to this small cave
  72.                         1 if allow_revisit && !has_revisited_small_cave(&current_path) => q.push_back(new_path), // first small cave revisit
  73.                         _ => {},
  74.                     }
  75.                 },
  76.             }
  77.         }
  78.     }
  79.     paths.len()
  80. }
  81.  
  82. fn solve(input: &str) -> io::Result<()> {
  83.     let file = File::open(input).expect("Input file not found.");
  84.     let reader = BufReader::new(file);
  85.  
  86.     // Input
  87.     let input: Vec<String> = match reader.lines().collect() {
  88.         Err(err) => panic!("Unknown error reading input: {}", err),
  89.         Ok(result) => result,
  90.     };
  91.  
  92.     // Make caves
  93.     let mut caves: Vec<Cave> = Vec::new();
  94.     for line in &input {
  95.         for cave in line.split('-') {
  96.             if caves.iter().filter(|c| c.name == cave).count() == 0 {
  97.                 caves.push(Cave::from(cave));
  98.             }
  99.         }
  100.     }
  101.  
  102.     // Connect caves
  103.     for line in &input {
  104.         let mut both_caves = line.split('-');
  105.         let cave1 = both_caves.next().unwrap();
  106.         let cave2 = both_caves.next().unwrap();
  107.         caves.iter_mut().filter(|c| c.name == cave1).for_each(|c| c.connections.push(cave2.to_string()));
  108.         caves.iter_mut().filter(|c| c.name == cave2).for_each(|c| c.connections.push(cave1.to_string()));
  109.     }
  110.  
  111.     // Solve parts 1 & 2
  112.     println!("Part 1: {}", count_paths(&caves,false)); // 4411
  113.     println!("Part 2: {}", count_paths(&caves,true)); // 136767
  114.  
  115.     Ok(())
  116. }
  117.  
  118. fn main() {
  119.     let args: Vec<String> = env::args().collect();
  120.     let filename = &args[1];
  121.     solve(&filename).unwrap();
  122. }
  123.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement