day12 solution. gross.
parent
a985a81d6d
commit
e6cf50fb47
|
@ -0,0 +1,208 @@
|
|||
use aoc_runner_derive::{aoc, aoc_generator};
|
||||
use std::collections::HashMap;
|
||||
use std::str::FromStr;
|
||||
|
||||
type Cache = HashMap<(Vec<Condition>, Vec<usize>), usize>;
|
||||
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Hash)]
|
||||
enum Condition {
|
||||
Unknown,
|
||||
Functional,
|
||||
Damaged,
|
||||
}
|
||||
|
||||
impl FromStr for Condition {
|
||||
type Err = String;
|
||||
fn from_str(input: &str) -> Result<Condition, Self::Err> {
|
||||
match input {
|
||||
"?" => Ok(Condition::Unknown),
|
||||
"." => Ok(Condition::Functional),
|
||||
"#" => Ok(Condition::Damaged),
|
||||
n @ _ => Err(format!("Unknown character '{}'", n)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
struct Spring {
|
||||
list: Vec<Condition>,
|
||||
groups: Vec<usize>,
|
||||
}
|
||||
|
||||
#[aoc_generator(day12)]
|
||||
fn parse(input: &str) -> Vec<Spring> {
|
||||
input
|
||||
.lines()
|
||||
.filter_map(|line| {
|
||||
if !line.is_empty() {
|
||||
let parts = line.split(" ").collect::<Vec<&str>>();
|
||||
let list = parts[0]
|
||||
.split("")
|
||||
.filter_map(|e| {
|
||||
if e.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Condition::from_str(e).ok()
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
let groups = parts[1]
|
||||
.split(",")
|
||||
.map(|e| e.parse::<usize>().unwrap())
|
||||
.collect();
|
||||
Some(Spring { list, groups })
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn calc_solutions(list: &Vec<Condition>, groups: &Vec<usize>, cache: &mut Cache) -> usize {
|
||||
if list.is_empty() {
|
||||
if groups.is_empty() {
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
match list[0] {
|
||||
Condition::Functional => calc_solutions(&list[1..].to_vec(), groups, cache),
|
||||
Condition::Damaged => calc_damaged_solutions(list, groups, cache),
|
||||
Condition::Unknown => {
|
||||
calc_solutions(&list[1..].to_vec(), groups, cache)
|
||||
+ calc_damaged_solutions(list, groups, cache)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn calc_damaged_solutions(list: &Vec<Condition>, groups: &Vec<usize>, cache: &mut Cache) -> usize {
|
||||
if let Some(&result) = cache.get(&(list.clone(), groups.clone())) {
|
||||
return result;
|
||||
}
|
||||
|
||||
if groups.is_empty() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if list.len() < groups[0] {
|
||||
return 0;
|
||||
}
|
||||
|
||||
for i in 0..groups[0] {
|
||||
if list[i] == Condition::Functional {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if list.len() == groups[0] {
|
||||
if groups.len() == 1 {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
if list[groups[0]] == Condition::Damaged {
|
||||
return 0;
|
||||
}
|
||||
let result = calc_solutions(
|
||||
&list[(groups[0] + 1)..].to_vec(),
|
||||
&groups[1..].to_vec(),
|
||||
cache,
|
||||
);
|
||||
cache.insert((list.clone(), groups.clone()), result);
|
||||
result
|
||||
}
|
||||
|
||||
#[aoc(day12, part1)]
|
||||
fn part1(input: &[Spring]) -> usize {
|
||||
let mut result: usize = 0;
|
||||
for src in input.iter() {
|
||||
let mut cache: Cache = HashMap::new();
|
||||
result += calc_solutions(&src.list, &src.groups, &mut cache);
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
#[aoc(day12, part2)]
|
||||
fn part2(input: &[Spring]) -> usize {
|
||||
let mut result: usize = 0;
|
||||
for src in input.iter() {
|
||||
let mut l: Vec<Condition> = Vec::with_capacity(src.groups.len() * 5 + 5);
|
||||
let mut g: Vec<usize> = Vec::with_capacity(src.list.len() * 5);
|
||||
for index in 0..5 {
|
||||
l.append(&mut src.list.clone());
|
||||
if index < 4 {
|
||||
l.push(Condition::Unknown);
|
||||
}
|
||||
g.append(&mut src.groups.clone());
|
||||
}
|
||||
let mut cache: Cache = HashMap::new();
|
||||
let s = calc_solutions(&l, &g, &mut cache);
|
||||
result += s;
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
const SAMPLE_DATA: &'static str = r#"???.### 1,1,3
|
||||
.??..??...?##. 1,1,3
|
||||
?#?#?#?#?#?#?#? 1,3,1,6
|
||||
????.#...#... 4,1,1
|
||||
????.######..#####. 1,6,5
|
||||
?###???????? 3,2,1
|
||||
"#;
|
||||
|
||||
#[test]
|
||||
fn sample_data_part1() {
|
||||
let input = parse(&SAMPLE_DATA);
|
||||
assert_eq!(part1(&input), 21);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sample_data_3() {
|
||||
let line1 = Spring {
|
||||
list: vec![
|
||||
Condition::Unknown,
|
||||
Condition::Unknown,
|
||||
Condition::Unknown,
|
||||
Condition::Functional,
|
||||
Condition::Damaged,
|
||||
Condition::Damaged,
|
||||
Condition::Damaged,
|
||||
],
|
||||
groups: vec![1, 1, 3],
|
||||
};
|
||||
assert_eq!(part2(&[line1]), 1);
|
||||
|
||||
let line2 = Spring {
|
||||
list: vec![
|
||||
Condition::Functional,
|
||||
Condition::Unknown,
|
||||
Condition::Unknown,
|
||||
Condition::Functional,
|
||||
Condition::Functional,
|
||||
Condition::Unknown,
|
||||
Condition::Unknown,
|
||||
Condition::Functional,
|
||||
Condition::Functional,
|
||||
Condition::Functional,
|
||||
Condition::Unknown,
|
||||
Condition::Damaged,
|
||||
Condition::Damaged,
|
||||
Condition::Functional,
|
||||
],
|
||||
groups: vec![1, 1, 3],
|
||||
};
|
||||
assert_eq!(part2(&[line2]), 16384);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sample_data_part2() {
|
||||
let input = parse(&SAMPLE_DATA);
|
||||
assert_eq!(part2(&input), 525152);
|
||||
}
|
||||
}
|
|
@ -11,5 +11,6 @@ mod day08;
|
|||
mod day09;
|
||||
mod day10;
|
||||
mod day11;
|
||||
mod day12;
|
||||
|
||||
aoc_lib! { year = 2023 }
|
||||
|
|
Loading…
Reference in New Issue