1
1
use crate :: data:: load;
2
+ use cached:: proc_macro:: cached;
2
3
use itertools:: Itertools ;
3
- use std:: { fmt:: Display , iter :: zip , num:: ParseIntError } ;
4
+ use std:: { fmt:: Display , num:: ParseIntError } ;
4
5
use thiserror:: Error ;
5
6
6
7
#[ derive( Error , Debug , PartialEq , Eq ) ]
@@ -40,13 +41,6 @@ impl Display for Condition {
40
41
}
41
42
}
42
43
43
- // fn display_vec<T>(conditions: &[T]) -> String
44
- // where
45
- // T: Display,
46
- // {
47
- // conditions.iter().map(|c| format!("{}", c)).join("")
48
- // }
49
-
50
44
#[ derive( Debug , Clone ) ]
51
45
struct Row {
52
46
conditions : Vec < Condition > ,
@@ -92,112 +86,106 @@ fn _split_condition_vec(conditions: &[Condition]) -> Vec<&[Condition]> {
92
86
. collect :: < Vec < _ > > ( )
93
87
}
94
88
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
+ }
108
96
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 ;
120
111
}
121
112
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 ;
125
118
} else {
126
- self . _in_progress_validation ( prop_conds )
119
+ return 0 ;
127
120
}
128
121
}
129
122
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 ( ) ) ,
136
127
}
128
+ }
137
129
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 ( ) )
159
152
}
160
- prop_conds. len ( )
161
153
}
162
154
}
163
155
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
-
172
156
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 ( ) )
175
161
}
176
162
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) ?
179
165
. iter ( )
180
166
. map ( |r| {
181
- let mut c = ( 0 ..5 )
167
+ let mut c = ( 0 ..n_reps )
182
168
. map ( |_| {
183
169
let mut new_c = r. conditions . clone ( ) ;
184
170
new_c. push ( Condition :: Unknown ) ;
185
171
new_c
186
172
} )
187
173
. concat ( ) ;
188
174
let _ = c. pop ( ) ;
189
- let g = ( 0 ..5 ) . map ( |_| r. groups . clone ( ) ) . concat ( ) ;
175
+ let g = ( 0 ..n_reps ) . map ( |_| r. groups . clone ( ) ) . concat ( ) ;
190
176
Row {
191
177
conditions : c,
192
178
groups : g,
193
179
}
194
180
} )
195
- . collect :: < Vec < Row > > ( ) ;
181
+ . collect :: < Vec < Row > > ( ) )
182
+ }
196
183
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 ( ) )
201
189
// Ok(0)
202
190
}
203
191
@@ -214,10 +202,10 @@ pub fn main(data_dir: &str) {
214
202
assert_eq ! ( answer_1, Ok ( 7716 ) ) ;
215
203
216
204
// 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 ) )
223
211
}
0 commit comments