From b80bbc27ce28f5ed10a4218cb3c0ae8a5eda021e Mon Sep 17 00:00:00 2001 From: wieerwill Date: Tue, 12 Dec 2023 20:33:12 +0100 Subject: [PATCH] solve Day12 with rust --- Day12/rust/src/main.rs | 156 +++++++++++++++++++++++------------------ 1 file changed, 86 insertions(+), 70 deletions(-) diff --git a/Day12/rust/src/main.rs b/Day12/rust/src/main.rs index a425978..de6a772 100644 --- a/Day12/rust/src/main.rs +++ b/Day12/rust/src/main.rs @@ -1,82 +1,98 @@ -use std::fs; use std::collections::HashMap; +use std::fs; -fn read_data(file_path: &str) -> Result, String> { - fs::read_to_string(file_path) - .map_err(|e| e.to_string()) - .map(|contents| contents.lines().map(String::from).collect()) +#[derive(Debug, Clone)] +struct Spring { + field: Vec, + springs: Vec, } -fn unfold_record(record: &str) -> (String, Vec) { - let parts: Vec<&str> = record.split(' ').collect(); - let dots = parts[0].repeat(5).split("").collect::>().join("?"); - let blocks = parts[1].split(',').map(|x| x.parse::().unwrap()).collect::>(); - let unfolded_blocks = blocks.iter().flat_map(|&b| vec![b; 5]).collect(); - (dots, unfolded_blocks) +fn main() { + match test_puzzle("../test.txt") { + Ok(_) => println!("Test passed."), + Err(e) => println!("Test failed: {}", e), + }; + + match run_puzzle("../input.txt") { + Ok(result) => println!("Final result: {}", result), + Err(e) => println!("Error: {}", e), + }; } -fn count_arrangements(dots: &str, blocks: &[usize], i: usize, bi: usize, current: usize, memo: &mut HashMap<(usize, usize, usize), usize>) -> usize { - let key = (i, bi, current); - if let Some(&result) = memo.get(&key) { - return result; - } - - if i == dots.len() { - return if bi == blocks.len() && current == 0 { - 1 - } else { - 0 - }; - } - - let mut ans = 0; - let c = dots.chars().nth(i).unwrap(); - - if c == '.' || c == '?' { - if current == 0 { - ans += count_arrangements(dots, blocks, i + 1, bi, 0, memo); - } else if bi < blocks.len() && current == blocks[bi] { - ans += count_arrangements(dots, blocks, i + 1, bi + 1, 0, memo); - } - } - - if c == '#' || c == '?' { - if bi < blocks.len() && (current + 1 <= blocks[bi]) { - ans += count_arrangements(dots, blocks, i + 1, bi, current + 1, memo); - } - } - - memo.insert(key, ans); - ans +fn run_puzzle(file_path: &str) -> Result { + let input = fs::read_to_string(file_path).map_err(|e| e.to_string())?; + let springs = parse(&input); + Ok(springs.iter().map(|s| s.clone().expand().arrangements()).sum()) } -fn solve_puzzle(lines: &[String]) -> usize { - lines.iter().fold(0, |acc, line| { - println!("Processing: {}", line); - let (dots, blocks) = unfold_record(line); - let line_result = count_arrangements(&dots, &blocks, 0, 0, 0, &mut HashMap::new()); - println!("Line result: {}", line_result); - acc + line_result - }) -} - -fn test_puzzle() -> Result<(), String> { - println!("Running tests..."); - let test_lines = read_data("../test.txt")?; - let test_result = solve_puzzle(&test_lines); - println!("Test result: {}", test_result); - assert_eq!(test_result, 525152, "Test failed!"); - println!("Test passed."); +fn test_puzzle(file_path: &str) -> Result<(), String> { + let input = fs::read_to_string(file_path).map_err(|e| e.to_string())?; + let springs = parse(&input); + let result = springs.iter().map(|s| s.clone().expand().arrangements()).sum::(); + assert_eq!(result, 525152, "Test failed!"); Ok(()) } -fn main() -> Result<(), String> { - test_puzzle()?; - - let input_lines = read_data("../input.txt")?; - println!("Processing input data..."); - let result = solve_puzzle(&input_lines); - println!("Final result: {}", result); - - Ok(()) +fn parse(input: &str) -> Vec { + input + .lines() + .map(|line| { + let (field, springs) = line.split_once(' ').unwrap(); + let springs = springs.split(',').map(|s| s.parse().unwrap()).collect::>(); + let mut field = field.chars().collect::>(); + field.push('.'); + Spring { field, springs } + }) + .collect() +} + +impl Spring { + fn arrangements(&self) -> usize { + fn count( + memo: &mut HashMap<(usize, usize, usize), usize>, + spring: &Spring, + pos: usize, + block: usize, + sequences: usize, + ) -> usize { + if let Some(&res) = memo.get(&(pos, block, sequences)) { + return res; + } + + let mut res = 0; + if pos == spring.field.len() { + res = (sequences == spring.springs.len()) as usize; + } else if spring.field[pos] == '#' { + res = count(memo, spring, pos + 1, block + 1, sequences) + } else if spring.field[pos] == '.' || sequences == spring.springs.len() { + if sequences < spring.springs.len() && block == spring.springs[sequences] { + res = count(memo, spring, pos + 1, 0, sequences + 1) + } else if block == 0 { + res = count(memo, spring, pos + 1, 0, sequences) + } + } else { + res += count(memo, spring, pos + 1, block + 1, sequences); + if block == spring.springs[sequences] { + res += count(memo, spring, pos + 1, 0, sequences + 1) + } else if block == 0 { + res += count(memo, spring, pos + 1, 0, sequences) + } + } + + memo.insert((pos, block, sequences), res); + res + } + + count(&mut HashMap::new(), self, 0, 0, 0) + } + + fn expand(&self) -> Self { + let mut new_field = self.field.clone(); + *new_field.last_mut().unwrap() = '?'; + + Self { + field: new_field.repeat(5), + springs: self.springs.repeat(5), + } + } }