day12
This commit is contained in:
parent
06e67f551b
commit
493a8d09a3
2 changed files with 1164 additions and 0 deletions
1000
aoc_puzzle_cache/day_12
Normal file
1000
aoc_puzzle_cache/day_12
Normal file
File diff suppressed because it is too large
Load diff
164
src/bin/day12.rs
Normal file
164
src/bin/day12.rs
Normal file
|
@ -0,0 +1,164 @@
|
|||
use aoc23::prelude::*;
|
||||
use derive_builder::Builder;
|
||||
use itertools::Itertools;
|
||||
use rayon::prelude::*;
|
||||
use std::{collections::HashMap, str::FromStr, time::Instant};
|
||||
|
||||
static DAY: u8 = 12;
|
||||
|
||||
fn count_possibilities(
|
||||
layout: &str,
|
||||
contiguous: &[usize],
|
||||
cache: &mut HashMap<(String, Vec<usize>), usize>,
|
||||
) -> usize {
|
||||
if layout.chars().count() == 0 {
|
||||
return if contiguous.len() == 0 {
|
||||
// println!("\tLine is variant: {layout}");
|
||||
1
|
||||
} else {
|
||||
0
|
||||
};
|
||||
}
|
||||
let cache_key = (layout.to_owned(), contiguous.to_vec());
|
||||
if let Some(&result) = cache.get(&cache_key) {
|
||||
return result;
|
||||
}
|
||||
|
||||
// Remove leading dots
|
||||
let result = if layout.starts_with('.') {
|
||||
count_possibilities(layout.to_owned().trim_matches('.'), contiguous, cache)
|
||||
// Try both options for ?
|
||||
} else if layout.starts_with('?') {
|
||||
count_possibilities(&layout.to_owned().replacen('?', &".", 1), contiguous, cache)
|
||||
+ count_possibilities(&layout.to_owned().replacen('?', &"#", 1), contiguous, cache)
|
||||
// If group, check if it matches
|
||||
} else if layout.starts_with("#") {
|
||||
// no groups left to match || not enough # to make the group || not all of this
|
||||
// group are #
|
||||
if contiguous.len() == 0
|
||||
|| layout.chars().count() < contiguous[0]
|
||||
|| layout.chars().take(contiguous[0]).any(|c| c == '.')
|
||||
{
|
||||
0
|
||||
} else if contiguous.len() > 1 {
|
||||
if layout.len() < contiguous[0] + 1
|
||||
|| layout.chars().skip(contiguous[0]).next().unwrap() == '#'
|
||||
{
|
||||
0
|
||||
} else {
|
||||
count_possibilities(
|
||||
&layout.chars().skip(contiguous[0] + 1).collect::<String>(),
|
||||
&contiguous[1..],
|
||||
cache,
|
||||
)
|
||||
}
|
||||
} else {
|
||||
count_possibilities(
|
||||
&layout.chars().skip(contiguous[0]).collect::<String>(),
|
||||
&contiguous[1..],
|
||||
cache,
|
||||
)
|
||||
}
|
||||
} else {
|
||||
panic!("Should not get here.")
|
||||
};
|
||||
cache.insert(cache_key, result);
|
||||
result
|
||||
}
|
||||
|
||||
fn part1(input: String) -> Result<usize> {
|
||||
// parse
|
||||
let start = Instant::now();
|
||||
let answer = input
|
||||
.trim()
|
||||
.lines()
|
||||
.enumerate()
|
||||
.par_bridge()
|
||||
.map(|(idx, line)| {
|
||||
let mut split = line.trim().split_whitespace();
|
||||
let layout = split.next().unwrap();
|
||||
let contiguous = split
|
||||
.next()
|
||||
.unwrap()
|
||||
.split(',')
|
||||
.map(|s| s.parse::<usize>().unwrap())
|
||||
.collect_vec();
|
||||
let count = count_possibilities(&layout, &contiguous, &mut HashMap::new());
|
||||
|
||||
// println!("Line {idx}: {line} = {count}");
|
||||
count
|
||||
})
|
||||
.sum();
|
||||
|
||||
// output
|
||||
println!("part 1: {answer}\t[total: {:?}]", start.elapsed());
|
||||
Ok(answer)
|
||||
}
|
||||
|
||||
fn part2(input: String) -> Result<usize> {
|
||||
// parse
|
||||
let start = Instant::now();
|
||||
let answer = input
|
||||
.trim()
|
||||
.lines()
|
||||
.enumerate()
|
||||
.par_bridge()
|
||||
.map(|(idx, line)| {
|
||||
let mut split = line.trim().split_whitespace();
|
||||
let layout = split.next().unwrap();
|
||||
let contiguous = split
|
||||
.next()
|
||||
.unwrap()
|
||||
.split(',')
|
||||
.map(|s| s.parse::<usize>().unwrap())
|
||||
.collect_vec();
|
||||
let layout = std::iter::repeat(layout).take(5).collect_vec().join("?");
|
||||
let contiguous = std::iter::repeat(contiguous)
|
||||
.take(5)
|
||||
.flatten()
|
||||
.collect_vec();
|
||||
let count = count_possibilities(&layout, &contiguous, &mut HashMap::new());
|
||||
|
||||
// println!("Line {idx}: {line} = {count}");
|
||||
count
|
||||
})
|
||||
.sum();
|
||||
|
||||
// output
|
||||
println!("part 2: {answer}\t[total: {:?}]", start.elapsed());
|
||||
Ok(answer)
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<()> {
|
||||
println!("Day {DAY}");
|
||||
println!("=====");
|
||||
let input = utils::aoc::get_puzzle_input(DAY).await?;
|
||||
part1(input.clone())?;
|
||||
part2(input.clone())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
static DATA: &'static str = "???.### 1,1,3
|
||||
.??..??...?##. 1,1,3
|
||||
?#?#?#?#?#?#?#? 1,3,1,6
|
||||
????.#...#... 4,1,1
|
||||
????.######..#####. 1,6,5
|
||||
?###???????? 3,2,1";
|
||||
|
||||
#[test]
|
||||
fn test_part_1() -> Result<()> {
|
||||
assert_eq!(part1(DATA.to_owned())?, 21);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_part_2() -> Result<()> {
|
||||
assert_eq!(part2(DATA.to_owned())?, 525152);
|
||||
Ok(())
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue