Skip to content

Commit c0a218b

Browse files
committed
Day 12 puzzle 2 (with help)
1 parent 1aeab5d commit c0a218b

File tree

3 files changed

+81
-90
lines changed

3 files changed

+81
-90
lines changed

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,4 @@ linked_hash_set = "0.1.4"
1919
num = "0.4.1"
2020
petgraph = "0.6.4"
2121
ndarray = "0.15.6"
22+
cached = "0.46.1"

README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
| 9 | [src/solutions/day09.rs](src/solutions/day09.rs) | ⭐️⭐️ |
2121
| 10 | [src/solutions/day10.rs](src/solutions/day10.rs) | ⭐️ |
2222
| 11 | [src/solutions/day11.rs](src/solutions/day11.rs) | ⭐️⭐️ |
23-
| 12 | [src/solutions/day12.rs](src/solutions/day12.rs) | ⭐️ |
23+
| 12 | [src/solutions/day12.rs](src/solutions/day12.rs) | ⭐️⭐️ |
2424
| 13 | [src/solutions/day13.rs](src/solutions/day13.rs) | ⭐️⭐️ |
2525
<!-- | 14 | [src/solutions/day14.rs](src/solutions/day14.rs) | ⭐️⭐️ | -->
2626
<!-- | 15 | [src/solutions/day15.rs](src/solutions/day15.rs) | ⭐️⭐️ | -->
@@ -34,6 +34,8 @@
3434
<!-- | 23 | [src/solutions/day23.rs](src/solutions/day23.rs) | ⭐️⭐️ | -->
3535
<!-- | 24 | [src/solutions/day24.rs](src/solutions/day24.rs) | ⭐️⭐️ | -->
3636

37+
Used this Reddit post for Day 12 part 2: <https://www.reddit.com/r/adventofcode/comments/18hbbxe/2023_day_12python_stepbystep_tutorial_with_bonus/>
38+
3739
## Setup
3840

3941
```bash

src/solutions/day12.rs

Lines changed: 77 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use crate::data::load;
2+
use cached::proc_macro::cached;
23
use itertools::Itertools;
3-
use std::{fmt::Display, iter::zip, num::ParseIntError};
4+
use std::{fmt::Display, num::ParseIntError};
45
use thiserror::Error;
56

67
#[derive(Error, Debug, PartialEq, Eq)]
@@ -40,13 +41,6 @@ impl Display for Condition {
4041
}
4142
}
4243

43-
// fn display_vec<T>(conditions: &[T]) -> String
44-
// where
45-
// T: Display,
46-
// {
47-
// conditions.iter().map(|c| format!("{}", c)).join("")
48-
// }
49-
5044
#[derive(Debug, Clone)]
5145
struct Row {
5246
conditions: Vec<Condition>,
@@ -92,112 +86,106 @@ fn _split_condition_vec(conditions: &[Condition]) -> Vec<&[Condition]> {
9286
.collect::<Vec<_>>()
9387
}
9488

95-
impl Row {
96-
fn _final_validation(&self, prop_conds: &[Condition]) -> bool {
97-
let splits = _split_condition_vec(prop_conds);
98-
if self.groups.len() != splits.len() {
99-
return false;
100-
}
101-
for (seq, gr) in zip(splits.iter(), self.groups.iter()) {
102-
if seq.len() != *gr {
103-
return false;
104-
}
105-
}
106-
true
107-
}
89+
fn parse_input(input: &str) -> Result<Vec<Row>, PuzzleErr> {
90+
input
91+
.trim()
92+
.lines()
93+
.map(Row::try_from)
94+
.collect::<Result<Vec<_>, PuzzleErr>>()
95+
}
10896

109-
fn _in_progress_validation(&self, prop_conds: &[Condition]) -> bool {
110-
let splits = _split_condition_vec(prop_conds);
111-
if self.groups.len() < splits.len() {
112-
return false;
113-
}
114-
for (i, seq) in splits.iter().enumerate() {
115-
if seq.len() > self.groups[i] {
116-
return false;
117-
}
118-
}
119-
true
97+
fn add_operational(record: Vec<Condition>, groups: Vec<usize>) -> usize {
98+
count_solutions(record[1..].to_vec(), groups)
99+
}
100+
101+
fn add_damaged(record: Vec<Condition>, groups: Vec<usize>, next_group: usize) -> usize {
102+
// This current group must be all "#" or "?" (no "." allowed).
103+
if record.len() < next_group {
104+
return 0;
105+
}
106+
if record[..next_group]
107+
.iter()
108+
.any(|c| c == &Condition::Operational)
109+
{
110+
return 0;
120111
}
121112

122-
fn validate(&self, prop_conds: &Vec<Condition>) -> bool {
123-
if prop_conds.len() == self.conditions.len() {
124-
self._final_validation(prop_conds)
113+
// If the remaining record is the length of the group, then successful if there are
114+
// no more remaining groups.
115+
if record.len() == next_group {
116+
if groups.len() == 1 {
117+
return 1;
125118
} else {
126-
self._in_progress_validation(prop_conds)
119+
return 0;
127120
}
128121
}
129122

130-
fn prune(&self, prop_conditions: &[Vec<Condition>]) -> Vec<Vec<Condition>> {
131-
prop_conditions
132-
.iter()
133-
.filter(|cond| self.validate(cond))
134-
.cloned()
135-
.collect::<Vec<_>>()
123+
// The next character after the group cannot be "#".
124+
match record[next_group] {
125+
Condition::Damaged => 0,
126+
_ => count_solutions(record[(next_group + 1)..].to_vec(), groups[1..].to_vec()),
136127
}
128+
}
137129

138-
fn num_solutions(&self) -> usize {
139-
let mut prop_conds: Vec<Vec<Condition>> = Vec::new();
140-
prop_conds.push(Vec::new());
141-
for cond in self.conditions.iter() {
142-
match cond {
143-
Condition::Unknown => {
144-
let _prec_prop_conds = prop_conds.clone();
145-
prop_conds.clear();
146-
for new_cond in [&Condition::Damaged, &Condition::Operational] {
147-
for _cond in _prec_prop_conds.iter() {
148-
let mut _new_prop_cond = _cond.clone();
149-
_new_prop_cond.push(*new_cond);
150-
prop_conds.push(_new_prop_cond);
151-
}
152-
}
153-
}
154-
_ => {
155-
prop_conds.iter_mut().for_each(|c| c.push(*cond));
156-
}
157-
}
158-
prop_conds = self.prune(&prop_conds);
130+
#[cached]
131+
fn count_solutions(record: Vec<Condition>, groups: Vec<usize>) -> usize {
132+
// If there are no more groups and no more "#", then successful.
133+
let Some(next_group) = groups.first() else {
134+
if record.iter().any(|c| c == &Condition::Damaged) {
135+
return 0;
136+
} else {
137+
return 1;
138+
}
139+
};
140+
141+
// If there are no more items left in record, then unsuccessful.
142+
let Some(next_cond) = record.first() else {
143+
return 0;
144+
};
145+
146+
match next_cond {
147+
Condition::Damaged => add_damaged(record.clone(), groups.clone(), *next_group),
148+
Condition::Operational => add_operational(record.clone(), groups.clone()),
149+
Condition::Unknown => {
150+
add_damaged(record.clone(), groups.clone(), *next_group)
151+
+ add_operational(record.clone(), groups.clone())
159152
}
160-
prop_conds.len()
161153
}
162154
}
163155

164-
fn parse_input(input: &str) -> Result<Vec<Row>, PuzzleErr> {
165-
input
166-
.trim()
167-
.lines()
168-
.map(Row::try_from)
169-
.collect::<Result<Vec<_>, PuzzleErr>>()
170-
}
171-
172156
pub fn puzzle_1(input: &str) -> Result<usize, PuzzleErr> {
173-
let rows = parse_input(input)?;
174-
Ok(rows.into_iter().map(|r| r.num_solutions()).sum())
157+
Ok(parse_input(input)?
158+
.iter()
159+
.map(|r| count_solutions(r.conditions.clone(), r.groups.clone()))
160+
.sum())
175161
}
176162

177-
pub fn puzzle_2(input: &str) -> Result<usize, PuzzleErr> {
178-
let rows = parse_input(input)?
163+
fn parse_and_expand_input(input: &str, n_reps: usize) -> Result<Vec<Row>, PuzzleErr> {
164+
Ok(parse_input(input)?
179165
.iter()
180166
.map(|r| {
181-
let mut c = (0..5)
167+
let mut c = (0..n_reps)
182168
.map(|_| {
183169
let mut new_c = r.conditions.clone();
184170
new_c.push(Condition::Unknown);
185171
new_c
186172
})
187173
.concat();
188174
let _ = c.pop();
189-
let g = (0..5).map(|_| r.groups.clone()).concat();
175+
let g = (0..n_reps).map(|_| r.groups.clone()).concat();
190176
Row {
191177
conditions: c,
192178
groups: g,
193179
}
194180
})
195-
.collect::<Vec<Row>>();
181+
.collect::<Vec<Row>>())
182+
}
196183

197-
Ok(rows.into_iter().map(|r| r.num_solutions()).sum())
198-
// for row in rows.iter() {
199-
// log::debug!("{}", row);
200-
// }
184+
pub fn puzzle_2(input: &str) -> Result<usize, PuzzleErr> {
185+
Ok(parse_and_expand_input(input, 5)?
186+
.iter()
187+
.map(|r| count_solutions(r.conditions.clone(), r.groups.clone()))
188+
.sum())
201189
// Ok(0)
202190
}
203191

@@ -214,10 +202,10 @@ pub fn main(data_dir: &str) {
214202
assert_eq!(answer_1, Ok(7716));
215203

216204
// Puzzle 2.
217-
// let answer_2 = puzzle_2(&data);
218-
// match answer_2 {
219-
// Ok(x) => println!(" Puzzle 2: {}", x),
220-
// Err(e) => panic!("No solution to puzzle 2: {}", e),
221-
// }
222-
// assert_eq!(answer_2, Ok(933))
205+
let answer_2 = puzzle_2(&data);
206+
match answer_2 {
207+
Ok(x) => println!(" Puzzle 2: {}", x),
208+
Err(e) => panic!("No solution to puzzle 2: {}", e),
209+
}
210+
assert_eq!(answer_2, Ok(18716325559999))
223211
}

0 commit comments

Comments
 (0)