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 day09;
|
||||||
mod day10;
|
mod day10;
|
||||||
mod day11;
|
mod day11;
|
||||||
|
mod day12;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
day11::run();
|
day12::run();
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue