This commit is contained in:
Moritz Hölting 2023-12-12 10:49:03 +01:00
parent 68ee92a178
commit 994be7e629
6 changed files with 1275 additions and 10 deletions

8
Cargo.lock generated
View File

@ -143,6 +143,14 @@ dependencies = [
"rayon",
]
[[package]]
name = "day-12"
version = "0.0.0"
dependencies = [
"indoc",
"nom",
]
[[package]]
name = "deprecate-until"
version = "0.1.1"

View File

@ -1,16 +1,10 @@
[workspace]
resolver = "2"
members = [
"day-01",
"day-02",
"day-03",
"day-05",
"day-06",
"day-07",
"day-08",
"day-09",
"day-10",
"day-11"
"day-*"
]
exclude = [
"day-04"
]
[workspace.dependencies]

12
day-12/Cargo.toml Normal file
View File

@ -0,0 +1,12 @@
[package]
name = "day-12"
version = "0.0.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
nom.workspace = true
[dev-dependencies]
indoc.workspace = true

1000
day-12/src/bin/input.txt Normal file

File diff suppressed because it is too large Load Diff

116
day-12/src/bin/part1.rs Normal file
View File

@ -0,0 +1,116 @@
use nom::{branch::alt, character::complete, combinator::value, multi::many1};
fn main() {
println!("{}", part1(include_str!("./input.txt")));
}
fn part1(input: &str) -> u32 {
input.lines().map(process_line).sum()
}
fn process_line(line: &str) -> u32 {
let (springs_str, group_size_str) = line.split_once(' ').expect("Invalid input");
let group_size = group_size_str
.split(',')
.map(|s| s.parse::<usize>().expect("invalid char as number"))
.collect::<Vec<_>>();
let springs = many1(alt((
value(
SpringStatus::Operational,
complete::char::<&str, nom::error::Error<&str>>('.'),
),
value(
SpringStatus::Damaged,
complete::char::<&str, nom::error::Error<&str>>('#'),
),
value(
SpringStatus::Unknown,
complete::char::<&str, nom::error::Error<&str>>('?'),
),
)))(springs_str)
.expect("Invalid input")
.1;
let spring_amount = springs.len();
let groups_amount = group_size.len();
let mut dp = vec![vec![vec![0; spring_amount + 1]; groups_amount + 1]; spring_amount + 1];
dp[spring_amount][groups_amount][0] = 1;
dp[spring_amount][groups_amount - 1][group_size[groups_amount - 1]] = 1;
for pos in (0..spring_amount).rev() {
for (group, &max_count) in group_size.iter().enumerate() {
// try iteratively all possible counts for the current group
for count in 0..=max_count {
// try both operational and damaged for each position
for &c in &[SpringStatus::Operational, SpringStatus::Damaged] {
// only proceed if the spring is of the chosen type or unknown
if springs[pos] == c || springs[pos] == SpringStatus::Unknown {
if c == SpringStatus::Operational && count == 0 {
// if operational and count is 0, then add the value from the next position because
// there is no new combination
dp[pos][group][count] += dp[pos + 1][group][0];
} else if c == SpringStatus::Operational && group_size[group] == count {
// if operational and count is equal to the group size, then add the value from the
// next position and next group
dp[pos][group][count] += dp[pos + 1][group + 1][0];
} else if c == SpringStatus::Damaged {
// if damaged, then add the value from the next position and next count
dp[pos][group][count] += dp[pos + 1][group][count + 1];
}
}
}
}
}
if matches!(
springs[pos],
SpringStatus::Operational | SpringStatus::Unknown
) {
dp[pos][groups_amount][0] += dp[pos + 1][groups_amount][0];
}
}
dp[0][0][0]
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum SpringStatus {
Operational,
Damaged,
Unknown,
}
#[cfg(test)]
mod tests {
use super::*;
use indoc::indoc;
#[test]
fn test_part1() {
assert_eq!(
part1(indoc!(
"
???.### 1,1,3
.??..??...?##. 1,1,3
?#?#?#?#?#?#?#? 1,3,1,6
????.#...#... 4,1,1
????.######..#####. 1,6,5
?###???????? 3,2,1
"
)),
21
);
}
#[test]
fn test_process_line_part1() {
assert_eq!(process_line("???.### 1,1,3"), 1);
assert_eq!(process_line(".??..??...?##. 1,1,3"), 4);
assert_eq!(process_line("?#?#?#?#?#?#?#? 1,3,1,6"), 1);
assert_eq!(process_line("????.#...#... 4,1,1"), 1);
assert_eq!(process_line("????.######..#####. 1,6,5"), 4);
assert_eq!(process_line("?###???????? 3,2,1"), 10);
}
}

135
day-12/src/bin/part2.rs Normal file
View File

@ -0,0 +1,135 @@
use std::vec;
use nom::{branch::alt, character::complete, combinator::value, multi::many1};
fn main() {
println!("{}", part2(include_str!("./input.txt")));
}
fn part2(input: &str) -> u64 {
input.lines().map(process_line).sum()
}
fn process_line(line: &str) -> u64 {
let (springs_str, group_size_str) = line.split_once(' ').expect("Invalid input");
let group_size = group_size_str
.split(',')
.map(|s| s.parse::<usize>().expect("invalid char as number"))
.collect::<Vec<_>>()
.repeat(5);
let springs = many1(alt((
value(
SpringStatus::Operational,
complete::char::<&str, nom::error::Error<&str>>('.'),
),
value(
SpringStatus::Damaged,
complete::char::<&str, nom::error::Error<&str>>('#'),
),
value(
SpringStatus::Unknown,
complete::char::<&str, nom::error::Error<&str>>('?'),
),
)))(springs_str)
.expect("Invalid input")
.1;
let springs = vec![
springs.clone(),
vec![SpringStatus::Unknown],
springs.clone(),
vec![SpringStatus::Unknown],
springs.clone(),
vec![SpringStatus::Unknown],
springs.clone(),
vec![SpringStatus::Unknown],
springs,
]
.into_iter()
.flatten()
.collect::<Vec<_>>();
let spring_amount = springs.len();
let groups_amount = group_size.len();
let mut dp = vec![vec![vec![0; spring_amount + 1]; groups_amount + 1]; spring_amount + 1];
dp[spring_amount][groups_amount][0] = 1;
dp[spring_amount][groups_amount - 1][group_size[groups_amount - 1]] = 1;
for pos in (0..spring_amount).rev() {
for (group, &max_count) in group_size.iter().enumerate() {
// try iteratively all possible counts for the current group
for count in 0..=max_count {
// try both operational and damaged for each position
for &c in &[SpringStatus::Operational, SpringStatus::Damaged] {
// only proceed if the spring is of the chosen type or unknown
if springs[pos] == c || springs[pos] == SpringStatus::Unknown {
if c == SpringStatus::Operational && count == 0 {
// if operational and count is 0, then add the value from the next position because
// there is no new combination
dp[pos][group][count] += dp[pos + 1][group][0];
} else if c == SpringStatus::Operational && group_size[group] == count {
// if operational and count is equal to the group size, then add the value from the
// next position and next group
dp[pos][group][count] += dp[pos + 1][group + 1][0];
} else if c == SpringStatus::Damaged {
// if damaged, then add the value from the next position and next count
dp[pos][group][count] += dp[pos + 1][group][count + 1];
}
}
}
}
}
if matches!(
springs[pos],
SpringStatus::Operational | SpringStatus::Unknown
) {
dp[pos][groups_amount][0] += dp[pos + 1][groups_amount][0];
}
}
dp[0][0][0]
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum SpringStatus {
Operational,
Damaged,
Unknown,
}
#[cfg(test)]
mod tests {
use super::*;
use indoc::indoc;
#[test]
fn test_part2() {
assert_eq!(
part2(indoc!(
"
???.### 1,1,3
.??..??...?##. 1,1,3
?#?#?#?#?#?#?#? 1,3,1,6
????.#...#... 4,1,1
????.######..#####. 1,6,5
?###???????? 3,2,1
"
)),
525152
);
}
#[test]
fn test_process_line_part2() {
assert_eq!(process_line("???.### 1,1,3"), 1);
assert_eq!(process_line(".??..??...?##. 1,1,3"), 16384);
assert_eq!(process_line("?#?#?#?#?#?#?#? 1,3,1,6"), 1);
assert_eq!(process_line("????.#...#... 4,1,1"), 16);
assert_eq!(process_line("????.######..#####. 1,6,5"), 2500);
assert_eq!(process_line("?###???????? 3,2,1"), 506250);
}
}