Compare commits
	
		
			2 Commits
		
	
	
		
			96ea3c317d
			...
			a431c4b75f
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| a431c4b75f | |||
| 20e2a9f7be | 
							
								
								
									
										106
									
								
								12/Cargo.lock
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										106
									
								
								12/Cargo.lock
									
									
									
										generated
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,106 @@ | ||||
| # This file is automatically @generated by Cargo. | ||||
| # It is not intended for manual editing. | ||||
| version = 3 | ||||
|  | ||||
| [[package]] | ||||
| name = "autocfg" | ||||
| version = "1.1.0" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" | ||||
|  | ||||
| [[package]] | ||||
| name = "cfg-if" | ||||
| version = "1.0.0" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" | ||||
|  | ||||
| [[package]] | ||||
| name = "crossbeam-deque" | ||||
| version = "0.8.3" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef" | ||||
| dependencies = [ | ||||
|  "cfg-if", | ||||
|  "crossbeam-epoch", | ||||
|  "crossbeam-utils", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "crossbeam-epoch" | ||||
| version = "0.9.15" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "ae211234986c545741a7dc064309f67ee1e5ad243d0e48335adc0484d960bcc7" | ||||
| dependencies = [ | ||||
|  "autocfg", | ||||
|  "cfg-if", | ||||
|  "crossbeam-utils", | ||||
|  "memoffset", | ||||
|  "scopeguard", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "crossbeam-utils" | ||||
| version = "0.8.16" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294" | ||||
| dependencies = [ | ||||
|  "cfg-if", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "day12" | ||||
| version = "0.1.0" | ||||
| dependencies = [ | ||||
|  "itertools", | ||||
|  "rayon", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "either" | ||||
| version = "1.9.0" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" | ||||
|  | ||||
| [[package]] | ||||
| name = "itertools" | ||||
| version = "0.12.0" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "25db6b064527c5d482d0423354fcd07a89a2dfe07b67892e62411946db7f07b0" | ||||
| dependencies = [ | ||||
|  "either", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "memoffset" | ||||
| version = "0.9.0" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c" | ||||
| dependencies = [ | ||||
|  "autocfg", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "rayon" | ||||
| version = "1.8.0" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "9c27db03db7734835b3f53954b534c91069375ce6ccaa2e065441e07d9b6cdb1" | ||||
| dependencies = [ | ||||
|  "either", | ||||
|  "rayon-core", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "rayon-core" | ||||
| version = "1.12.0" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "5ce3fb6ad83f861aac485e76e1985cd109d9a3713802152be56c3b1f0e0658ed" | ||||
| dependencies = [ | ||||
|  "crossbeam-deque", | ||||
|  "crossbeam-utils", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "scopeguard" | ||||
| version = "1.2.0" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" | ||||
							
								
								
									
										8
									
								
								12/Cargo.toml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								12/Cargo.toml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,8 @@ | ||||
| [package] | ||||
| name = "day12" | ||||
| version = "0.1.0" | ||||
| edition = "2021" | ||||
|  | ||||
| [dependencies] | ||||
| itertools = "0.12.0" | ||||
| rayon = "1.8.0" | ||||
							
								
								
									
										219
									
								
								12/src/main.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										219
									
								
								12/src/main.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,219 @@ | ||||
| use itertools::Itertools; | ||||
| use rayon::prelude::*; | ||||
| use std::cell::RefCell; | ||||
| use std::collections::HashMap; | ||||
| use std::fs::File; | ||||
| use std::io::{BufRead, BufReader, Lines}; | ||||
| use std::time::{Duration, 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)] | ||||
| struct SpringRow<'a> { | ||||
|     springs: Vec<char>, | ||||
|     spans: Vec<usize>, | ||||
|     cache: RefCell<HashMap<(usize, &'a [usize], usize), u64>>, | ||||
| } | ||||
|  | ||||
| impl<'a> From<&str> for SpringRow<'a> { | ||||
|     fn from(s: &str) -> Self { | ||||
|         let (springs_s, spans_s) = s.split_once(' ').unwrap(); | ||||
|         SpringRow { | ||||
|             springs: springs_s.chars().collect(), | ||||
|             spans: spans_s.split(',').map(|x| x.parse().unwrap()).collect(), | ||||
|             cache: RefCell::new(HashMap::new()), | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<'a> SpringRow<'a> { | ||||
|     fn possible_arrangements(&self) -> u64 { | ||||
|         let mut permutations = vec![self.springs.clone()]; | ||||
|         while let Some(cur) = permutations.iter().position(|perm| perm.contains(&'?')) { | ||||
|             let perm = permutations.remove(cur); | ||||
|             let rep_pos = perm.iter().position(|c| *c == '?').unwrap(); | ||||
|  | ||||
|             let mut new_elem = perm.clone(); | ||||
|  | ||||
|             new_elem[rep_pos] = '.'; | ||||
|             permutations.push(new_elem.clone()); | ||||
|             new_elem[rep_pos] = '#'; | ||||
|             permutations.push(new_elem); | ||||
|         } | ||||
|  | ||||
|         permutations | ||||
|             .iter() | ||||
|             .filter_map(|perm| { | ||||
|                 let groups: Vec<usize> = perm | ||||
|                     .iter() | ||||
|                     .group_by(|c| **c == '#') | ||||
|                     .into_iter() | ||||
|                     .filter_map(|(key, group)| if key { Some(group.count()) } else { None }) | ||||
|                     .collect(); | ||||
|                 if groups == self.spans { | ||||
|                     Some(()) | ||||
|                 } else { | ||||
|                     None | ||||
|                 } | ||||
|             }) | ||||
|             .count() as u64 | ||||
|     } | ||||
|     fn memoize_arrangements_r( | ||||
|         &self, | ||||
|         idx: usize, | ||||
|         spans_left: &'a [usize], | ||||
|         cur_span_left: usize, | ||||
|     ) -> u64 { | ||||
|         if let Some(value) = self.cache.borrow().get(&(idx, spans_left, cur_span_left)) { | ||||
|             return *value; | ||||
|         } | ||||
|         let result = self.possible_arrangements_r(idx, spans_left, cur_span_left); | ||||
|         self.cache | ||||
|             .borrow_mut() | ||||
|             .insert((idx, spans_left, cur_span_left), result); | ||||
|         result | ||||
|     } | ||||
|     fn possible_arrangements_r( | ||||
|         &self, | ||||
|         idx: usize, | ||||
|         spans_left: &'a [usize], | ||||
|         cur_span_left: usize, | ||||
|     ) -> u64 { | ||||
|         if cur_span_left == 0 && spans_left.len() == 1 { | ||||
|             // everything is consumed. if we're at the end, we good | ||||
|             // if we have stuff left, as long as it doesn't contain a fixed broken spring we good | ||||
|             if idx == self.springs.len() || !self.springs[idx..].contains(&'#') { | ||||
|                 // println!("{:?} valid", self.springs); | ||||
|                 return 1; | ||||
|             } | ||||
|             // something invalid | ||||
|             return 0; | ||||
|         } | ||||
|         if idx >= self.springs.len() { | ||||
|             // reached the end of the string without consuming all our spans | ||||
|             // println!( | ||||
|             //     "{:?} invalid idx: {} spans_left: {:?}", | ||||
|             //     self.springs, idx, spans_left | ||||
|             // ); | ||||
|             return 0; | ||||
|         } | ||||
|         // println!( | ||||
|         //     "call idx: {} str: {:?} spans_left: {:?} cur_span_left: {}", | ||||
|         //     idx, | ||||
|         //     &self.springs[idx..], | ||||
|         //     spans_left, | ||||
|         //     cur_span_left | ||||
|         // ); | ||||
|         return if cur_span_left == 0 { | ||||
|             match self.springs[idx] { | ||||
|                 '#' => 0, // a broken spring after we have completed our span is invalid | ||||
|                 // a space or a ? (becomes a space) starts a new span | ||||
|                 '.' | '?' => self.memoize_arrangements_r(idx + 1, &spans_left[1..], spans_left[1]), | ||||
|                 _ => panic!("invalid spring"), | ||||
|             } | ||||
|         } else { | ||||
|             // we are mid span | ||||
|             match self.springs[idx] { | ||||
|                 // cur_span_left != 0, so we still have work to do with broken springs | ||||
|                 '#' => self.memoize_arrangements_r(idx + 1, spans_left, cur_span_left - 1), | ||||
|                 '.' => { | ||||
|                     if spans_left[0] == cur_span_left { | ||||
|                         self.memoize_arrangements_r(idx + 1, spans_left, cur_span_left) | ||||
|                     } else { | ||||
|                         0 | ||||
|                     } | ||||
|                 } | ||||
|                 '?' => { | ||||
|                     if spans_left[0] == cur_span_left { | ||||
|                         self.memoize_arrangements_r(idx + 1, spans_left, cur_span_left - 1) | ||||
|                             + self.memoize_arrangements_r(idx + 1, spans_left, cur_span_left) | ||||
|                     } else { | ||||
|                         self.memoize_arrangements_r(idx + 1, spans_left, cur_span_left - 1) | ||||
|                     } | ||||
|                 } | ||||
|                 _ => panic!("invalid spring"), | ||||
|             } | ||||
|         }; | ||||
|     } | ||||
| } | ||||
|  | ||||
| // PROBLEM 1 solution | ||||
|  | ||||
| fn problem1<T: BufRead>(input: Lines<T>) -> u64 { | ||||
|     let rows: Vec<SpringRow> = input | ||||
|         .map(|row| SpringRow::from(row.unwrap().as_str())) | ||||
|         .collect(); | ||||
|  | ||||
|     rows.iter() | ||||
|         .map(|row| row.possible_arrangements_r(0, &row.spans, row.spans[0])) | ||||
|         .sum() | ||||
| } | ||||
|  | ||||
| // PROBLEM 2 solution | ||||
| fn problem2<T: BufRead>(input: Lines<T>) -> u64 { | ||||
|     let mut rows: Vec<SpringRow> = input | ||||
|         .map(|row| SpringRow::from(row.unwrap().as_str())) | ||||
|         .collect(); | ||||
|  | ||||
|     for row in &mut rows { | ||||
|         let orig_slice = &row.springs.clone(); | ||||
|         for _i in 0..4 { | ||||
|             row.springs.push('?'); | ||||
|             row.springs.extend_from_slice(orig_slice); | ||||
|         } | ||||
|  | ||||
|         let orig_span_slice = &row.spans.clone(); | ||||
|         for _i in 0..4 { | ||||
|             row.spans.extend_from_slice(orig_span_slice); | ||||
|         } | ||||
|     } | ||||
|     rows.iter() | ||||
|         .map(|row| row.possible_arrangements_r(0, &row.spans, row.spans[0])) | ||||
|         .sum() | ||||
| } | ||||
|  | ||||
| #[cfg(test)] | ||||
| mod tests { | ||||
|     use crate::*; | ||||
|     use std::io::Cursor; | ||||
|  | ||||
|     const EXAMPLE: &str = &"???.### 1,1,3 | ||||
| .??..??...?##. 1,1,3 | ||||
| ?#?#?#?#?#?#?#? 1,3,1,6 | ||||
| ????.#...#... 4,1,1 | ||||
| ????.######..#####. 1,6,5 | ||||
| ?###???????? 3,2,1"; | ||||
|  | ||||
|     #[test] | ||||
|     fn problem1_example() { | ||||
|         let c = Cursor::new(EXAMPLE); | ||||
|         assert_eq!(problem1(c.lines()), 21); | ||||
|     } | ||||
|  | ||||
|     #[test] | ||||
|     fn problem2_example() { | ||||
|         let c = Cursor::new(EXAMPLE); | ||||
|         assert_eq!(problem2(c.lines()), 525152); | ||||
|     } | ||||
| } | ||||
		Reference in New Issue
	
	Block a user