11use anyhow:: { Context , Result , bail} ;
2+ use rayon:: prelude:: * ;
23use std:: {
4+ cmp:: Reverse ,
35 collections:: { HashSet , VecDeque } ,
46 str:: FromStr ,
57} ;
@@ -28,7 +30,7 @@ impl FromStr for InputLine {
2830 . map ( |c| c == b'#' )
2931 . collect :: < Vec < _ > > ( ) ;
3032
31- let wiring: Vec < Vec < usize > > = groups
33+ let mut wiring: Vec < Vec < usize > > = groups
3234 . get ( 1 ..groups. len ( ) - 1 )
3335 . context ( "not enough groups" ) ?
3436 . iter ( )
@@ -40,6 +42,8 @@ impl FromStr for InputLine {
4042 } )
4143 . collect :: < Result < _ , _ > > ( ) ?;
4244
45+ wiring. sort_unstable_by_key ( |w| Reverse ( w. len ( ) ) ) ;
46+
4347 let joltages = groups
4448 . last ( )
4549 . context ( "not enough groups on line" ) ?
@@ -104,15 +108,119 @@ fn calculate_p1_line(line: &InputLine) -> Result<u64> {
104108 bail ! ( "no p1 solution" ) ;
105109}
106110
107- fn calculate ( inp : & str ) -> Result < ( u64 , & str ) > {
111+ fn calc_lower_bound ( state : & [ i32 ] ) -> i32 {
112+ * state. iter ( ) . max ( ) . unwrap_or ( & i32:: MAX )
113+ }
114+
115+ fn calc_upper_bound ( state : & [ i32 ] ) -> i32 {
116+ state. iter ( ) . sum :: < i32 > ( )
117+ }
118+
119+ fn make_inequalities ( line : & InputLine ) -> Result < Vec < Vec < ( usize , usize ) > > > {
120+ // Inequalities where state[a] >= state[b] for a valid solution
121+ let mut inequalities = vec ! [ ] ;
122+
123+ for btn_idx in 0 ..line. wiring . len ( ) {
124+ let mut ineq = vec ! [ ] ;
125+ for a in 0 ..line. joltages . len ( ) {
126+ for b in 0 ..line. joltages . len ( ) {
127+ if a == b {
128+ continue ;
129+ }
130+
131+ if !line
132+ . wiring
133+ . iter ( )
134+ . skip ( btn_idx)
135+ . any ( |btn| btn. contains ( & b) && !btn. contains ( & a) )
136+ {
137+ ineq. push ( ( a, b) ) ;
138+ }
139+ }
140+ }
141+ inequalities. push ( ineq) ;
142+ }
143+ Ok ( inequalities)
144+ }
145+
146+ fn calculate_p2_line ( line : & InputLine ) -> Result < i32 > {
147+ // (state, num_pushes)
148+ let mut q = vec ! [ ] ;
149+
150+ q. push ( ( line. joltages . to_vec ( ) , 0 , 0 ) ) ;
151+
152+ let target_state = vec ! [ 0 ; line. joltages. len( ) ] ;
153+
154+ let mut best = calc_upper_bound ( & line. joltages ) ;
155+
156+ // Inequalities where state[a] >= state[b] for a valid solution
157+ let inequalities = make_inequalities ( line) ?;
158+
159+ while let Some ( ( state, num_pushes, button_idx) ) = q. pop ( ) {
160+ if state == target_state {
161+ if num_pushes < best {
162+ best = num_pushes;
163+ }
164+ continue ;
165+ }
166+
167+ let lower_bound = calc_lower_bound ( & state) ;
168+
169+ if num_pushes + lower_bound >= best
170+ || inequalities
171+ . get ( button_idx)
172+ . unwrap_or ( & vec ! [ ] )
173+ . iter ( )
174+ . any ( |( a, b) | {
175+ if let Some ( a) = state. get ( * a)
176+ && let Some ( b) = state. get ( * b)
177+ {
178+ a < b
179+ } else {
180+ true
181+ }
182+ } )
183+ {
184+ continue ;
185+ }
186+
187+ for button in button_idx..line. wiring . len ( ) {
188+ let mut new_state = state. clone ( ) ;
189+ let mut invalid = false ;
190+ for itm in line. wiring . get ( button) . unwrap_or ( & vec ! [ ] ) . iter ( ) {
191+ let elem = new_state. get_mut ( * itm) . context ( "invalid itm" ) ?;
192+ * elem -= 1 ;
193+ if * elem < 0 {
194+ invalid = true ;
195+ break ;
196+ }
197+ }
198+
199+ if invalid {
200+ continue ;
201+ }
202+
203+ q. push ( ( new_state, num_pushes + 1 , button) ) ;
204+ }
205+ }
206+
207+ Ok ( best)
208+ }
209+
210+ fn calculate ( inp : & str ) -> Result < ( u64 , i32 ) > {
108211 let lines = inp
109212 . lines ( )
110213 . map ( |line| line. parse ( ) )
111214 . collect :: < Result < Vec < InputLine > > > ( ) ?;
112215
113216 let p1 = lines. iter ( ) . map ( calculate_p1_line) . sum :: < Result < u64 > > ( ) ?;
114217
115- Ok ( ( p1, "p2 not implemented yet" ) )
218+ let p2 = lines
219+ . par_iter ( )
220+ . map ( calculate_p2_line)
221+ . sum :: < Result < i32 > > ( ) ?;
222+
223+ Ok ( ( p1, p2) )
116224}
117225
118226pub fn run_2025_10 ( inp : & str ) -> Result < String > {
@@ -132,8 +240,13 @@ mod tests {
132240 assert_eq ! ( calculate( EXAMPLE_DATA ) . unwrap( ) . 0 , 7 ) ;
133241 }
134242
243+ // #[test]
244+ // fn test_real_p1() {
245+ // assert_eq!(calculate(REAL_DATA).unwrap().0, 422);
246+ // }
247+
135248 #[ test]
136- fn test_real_p1 ( ) {
137- assert_eq ! ( calculate( REAL_DATA ) . unwrap( ) . 0 , 422 ) ;
249+ fn test_example_p2 ( ) {
250+ assert_eq ! ( calculate( EXAMPLE_DATA ) . unwrap( ) . 1 , 33 ) ;
138251 }
139252}
0 commit comments