Day 12
This commit is contained in:
parent
3928623854
commit
72f7b2b42c
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,306 @@
|
|||
use std::collections::HashMap;
|
||||
|
||||
pub fn run() {
|
||||
let input = include_str!("../input/day12.txt");
|
||||
// let input = r#"???.### 1,1,3
|
||||
// .??..??...?##. 1,1,3
|
||||
// ?#?#?#?#?#?#?#? 1,3,1,6
|
||||
// ????.#...#... 4,1,1
|
||||
// ????.######..#####. 1,6,5
|
||||
// ?###???????? 3,2,1"#;
|
||||
|
||||
let mut lines = input
|
||||
.trim()
|
||||
.lines()
|
||||
.map(|l| parse_line(l))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let count_sum = lines
|
||||
.iter()
|
||||
.map(|(cs, runs)| {
|
||||
count_arrangements(
|
||||
CountInputs {
|
||||
cs: &cs,
|
||||
assuming_first_char: None,
|
||||
in_run: false,
|
||||
runs: runs.clone(),
|
||||
},
|
||||
&mut HashMap::new(),
|
||||
)
|
||||
})
|
||||
.sum::<usize>();
|
||||
dbg!(count_sum);
|
||||
|
||||
for l in lines.iter_mut() {
|
||||
unfold_line(&mut l.0, &mut l.1);
|
||||
}
|
||||
|
||||
let count_sum2 = lines
|
||||
.iter()
|
||||
.map(|(cs, runs)| {
|
||||
let res = count_arrangements(
|
||||
CountInputs {
|
||||
cs: &cs,
|
||||
assuming_first_char: None,
|
||||
in_run: false,
|
||||
runs: runs.clone(),
|
||||
},
|
||||
&mut HashMap::new(),
|
||||
);
|
||||
println!("finished line");
|
||||
res
|
||||
})
|
||||
.sum::<usize>();
|
||||
dbg!(count_sum2);
|
||||
}
|
||||
|
||||
fn parse_line(s: &str) -> (Vec<char>, Vec<u8>) {
|
||||
let (l, r) = s.trim().split_once(" ").unwrap();
|
||||
(
|
||||
l.chars().collect(),
|
||||
r.split(",").map(|s| s.parse().unwrap()).collect(),
|
||||
)
|
||||
}
|
||||
|
||||
fn unfold_line(cs: &mut Vec<char>, runs: &mut Vec<u8>) {
|
||||
let orig_cs = cs.to_vec();
|
||||
let orig_runs = runs.to_vec();
|
||||
for _ in 0..4 {
|
||||
cs.push('?');
|
||||
cs.extend(orig_cs.iter());
|
||||
runs.extend(orig_runs.iter());
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
struct CountInputs<'a> {
|
||||
cs: &'a [char],
|
||||
assuming_first_char: Option<char>,
|
||||
in_run: bool,
|
||||
runs: Vec<u8>,
|
||||
}
|
||||
|
||||
fn count_arrangements<'a: 'b, 'b>(
|
||||
inputs: CountInputs<'a>,
|
||||
// cs: &[char],
|
||||
// assuming_first_char: Option<char>,
|
||||
// // mut accepted: Vec<char>,
|
||||
// in_run: bool,
|
||||
// runs: &[u8],
|
||||
cache: &mut HashMap<CountInputs<'b>, usize>,
|
||||
) -> usize {
|
||||
if let Some(result) = cache.get(&inputs) {
|
||||
return *result;
|
||||
}
|
||||
|
||||
if inputs.runs.is_empty() {
|
||||
assert!(!inputs.in_run);
|
||||
for &c in inputs.cs {
|
||||
if c == '#' {
|
||||
// invalid, since there are remaining broken springs with no broken runs
|
||||
cache.insert(inputs, 0);
|
||||
return 0;
|
||||
} else {
|
||||
// accepted.push('.');
|
||||
}
|
||||
}
|
||||
// valid
|
||||
// println!("{}", accepted.iter().collect::<String>());
|
||||
cache.insert(inputs, 1);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if inputs.cs.is_empty() {
|
||||
// if we're out of chars, valid iff there are no runs remaining
|
||||
if inputs.runs.is_empty() {
|
||||
assert!(!inputs.in_run);
|
||||
cache.insert(inputs, 1);
|
||||
return 1;
|
||||
} else {
|
||||
cache.insert(inputs, 0);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
let first = inputs.assuming_first_char.unwrap_or(inputs.cs[0]);
|
||||
|
||||
if first == '#' {
|
||||
if inputs.runs[0] == 1 {
|
||||
// current run should be finished
|
||||
if inputs.cs.len() > 1 {
|
||||
if inputs.cs[1] == '#' {
|
||||
// if the next char is #, invalid b/c the current run would continue
|
||||
return 0;
|
||||
} else if inputs.cs[1] == '.' || inputs.cs[1] == '?' {
|
||||
// let mut accepted = accepted.to_vec();
|
||||
// accepted.push('#');
|
||||
// accepted.push('.');
|
||||
// current run does indeed end
|
||||
return count_arrangements(
|
||||
CountInputs {
|
||||
cs: &inputs.cs[2..],
|
||||
assuming_first_char: None,
|
||||
in_run: false,
|
||||
runs: inputs.runs[1..].iter().copied().collect::<Vec<_>>(),
|
||||
},
|
||||
cache,
|
||||
);
|
||||
} else {
|
||||
panic!("invalid char {}", inputs.cs[1]);
|
||||
}
|
||||
} else {
|
||||
// end of string, valid iff there are no more runs
|
||||
if inputs.runs.len() == 1 {
|
||||
// println!(
|
||||
// "{}#, last run: {}",
|
||||
// accepted.iter().collect::<String>(),
|
||||
// runs[0]
|
||||
// );
|
||||
cache.insert(inputs, 1);
|
||||
return 1;
|
||||
} else {
|
||||
cache.insert(inputs, 0);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// accepted.push('#');
|
||||
// current run continues
|
||||
let mut runs_copy = inputs.runs.to_vec();
|
||||
runs_copy[0] -= 1;
|
||||
// println!(
|
||||
// "{}, current run continuing, {:?}",
|
||||
// accepted.iter().collect::<String>(),
|
||||
// runs_copy
|
||||
// );
|
||||
return count_arrangements(
|
||||
CountInputs {
|
||||
cs: &inputs.cs[1..],
|
||||
assuming_first_char: None,
|
||||
in_run: true,
|
||||
runs: runs_copy,
|
||||
},
|
||||
cache,
|
||||
);
|
||||
}
|
||||
} else if first == '?' {
|
||||
// let mut accepted_dot = accepted.clone();
|
||||
// accepted_dot.push('.');
|
||||
let res = count_arrangements(
|
||||
CountInputs {
|
||||
cs: inputs.cs,
|
||||
assuming_first_char: Some('.'),
|
||||
in_run: inputs.in_run,
|
||||
runs: inputs.runs.clone(),
|
||||
},
|
||||
cache,
|
||||
) + count_arrangements(
|
||||
CountInputs {
|
||||
cs: inputs.cs,
|
||||
assuming_first_char: Some('#'),
|
||||
in_run: inputs.in_run,
|
||||
runs: inputs.runs.clone(),
|
||||
},
|
||||
cache,
|
||||
);
|
||||
cache.insert(inputs, res);
|
||||
return res;
|
||||
} else if first == '.' {
|
||||
if inputs.in_run {
|
||||
// the current run ended prematurely
|
||||
cache.insert(inputs, 0);
|
||||
return 0;
|
||||
} else {
|
||||
// accepted.push('.');
|
||||
return count_arrangements(
|
||||
CountInputs {
|
||||
cs: &inputs.cs[1..],
|
||||
assuming_first_char: None,
|
||||
in_run: false,
|
||||
runs: inputs.runs,
|
||||
},
|
||||
cache,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
panic!("unexpected char {}", first);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
fn count(cs: &str, runs: Vec<u8>, expected: usize) {
|
||||
let cs = cs.chars().collect::<Vec<_>>();
|
||||
assert_eq!(
|
||||
count_arrangements(
|
||||
CountInputs {
|
||||
cs: &cs,
|
||||
assuming_first_char: None,
|
||||
in_run: false,
|
||||
runs,
|
||||
},
|
||||
&mut HashMap::new(),
|
||||
),
|
||||
expected
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test1() {
|
||||
count("???.###", vec![1, 1, 3], 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test2() {
|
||||
count(".??..??...?##.", vec![1, 1, 3], 4);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test3() {
|
||||
count("?#?#?#?#?#?#?#?", vec![1, 3, 1, 6], 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test4() {
|
||||
count("????.#...#...", vec![4, 1, 1], 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test5() {
|
||||
count("????.######..#####", vec![1, 6, 5], 4);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test6() {
|
||||
count("?###????????", vec![3, 2, 1], 10);
|
||||
}
|
||||
|
||||
fn unfold(cs: &str, mut runs: Vec<u8>, expected: usize) {
|
||||
let mut cs = cs.chars().collect::<Vec<_>>();
|
||||
unfold_line(&mut cs, &mut runs);
|
||||
assert_eq!(
|
||||
count_arrangements(
|
||||
CountInputs {
|
||||
cs: &cs,
|
||||
assuming_first_char: None,
|
||||
in_run: false,
|
||||
runs,
|
||||
},
|
||||
&mut HashMap::new()
|
||||
),
|
||||
expected
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unfolded() {
|
||||
unfold("???.###", vec![1, 1, 3], 1);
|
||||
unfold(".??..??...?##.", vec![1, 1, 3], 16384);
|
||||
unfold("?#?#?#?#?#?#?#?", vec![1, 3, 1, 6], 1);
|
||||
unfold("????.#...#...", vec![4, 1, 1], 16);
|
||||
unfold("????.######..#####.", vec![1, 6, 5], 2500);
|
||||
unfold("?###????????", vec![3, 2, 1], 506250);
|
||||
}
|
||||
}
|
|
@ -12,7 +12,8 @@ mod day08;
|
|||
mod day09;
|
||||
mod day10;
|
||||
mod day11;
|
||||
mod day12;
|
||||
|
||||
fn main() {
|
||||
day11::run();
|
||||
day12::run();
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue