@@ -6,7 +6,7 @@ use std::path::Path;
66
77fn main ( ) {
88 let input = Input :: from_file ( format ! ( "{}/input.txt" , env!( "CARGO_MANIFEST_DIR" ) ) ) . expect ( "failed to read input" ) ;
9- // let input = Input::from_file(format!("{}/example1.txt", env!("CARGO_MANIFEST_DIR"))).expect("failed to read input");
9+ let input = Input :: from_file ( format ! ( "{}/example1.txt" , env!( "CARGO_MANIFEST_DIR" ) ) ) . expect ( "failed to read input" ) ;
1010
1111 // Part 1
1212 println ! ( "Part 1: {}" , part1( & input) ) ;
@@ -36,7 +36,7 @@ fn part1(input: &Input) -> usize {
3636 combinations. push ( count) ;
3737 }
3838
39- println ! ( "{:?}" , combinations) ;
39+ // println!("{:?}", combinations);
4040 combinations. into_iter ( ) . sum ( )
4141}
4242
@@ -69,6 +69,105 @@ fn is_match(state: &[State], groups: &[usize]) -> bool {
6969}
7070
7171fn part2 ( input : & Input ) -> usize {
72+ for ( records, groups) in & input. values {
73+ let mut records = records. clone ( ) ;
74+ println ! ( "{records:?} {groups:?}" ) ;
75+
76+ let total = records. len ( ) ;
77+ let n_damaged: usize = groups. iter ( ) . sum ( ) ;
78+ let n_ok = total - n_damaged;
79+
80+ println ! ( "total: {total}, damaged: {n_damaged}, ok: {n_ok}" ) ;
81+
82+ // We can split these up into groups of ok/broken/ok/.../broken/ok
83+ // N0 + A + (1 + N1) + B + (1 + N2) + ... + Z + Nn = total
84+ // We know that N0 .. Nn must be in the range 0..a
85+ let mut a = n_ok - ( groups. len ( ) - 1 ) ;
86+ println ! ( "a: {a}" ) ;
87+
88+ // There's always at least one OK spring between broken sets
89+ let mut ns = vec ! [ [ 0 , a] ; groups. len( ) + 1 ] ;
90+ for n in 1 ..groups. len ( ) {
91+ ns[ n] [ 0 ] += 1 ;
92+ ns[ n] [ 1 ] += 1 ;
93+ }
94+
95+ let mut ok_known = records. iter ( ) . filter ( |r| matches ! ( r, State :: Operational ) ) . count ( ) ;
96+ let mut damaged_known = records. iter ( ) . filter ( |r| matches ! ( r, State :: Damaged ) ) . count ( ) ;
97+ let mut unknown = records. iter ( ) . filter ( |r| matches ! ( r, State :: Unknown ) ) . count ( ) ;
98+ assert_eq ! ( ok_known + damaged_known + unknown, records. len( ) ) ;
99+
100+ let mut n = 0 ;
101+ let mut ok_seen = 0 ;
102+ let mut damaged_seen = 0 ;
103+ let mut unknown_seen = 0 ;
104+ for i in 0 ..records. len ( ) {
105+ println ! ( "{i:02}: OK={ok_seen:2}, NG={damaged_seen:2}, UNK={unknown_seen:2} {ns:?}" ) ;
106+ match records[ i] {
107+ State :: Damaged => {
108+ damaged_seen += 1 ;
109+
110+ ns[ n] [ 1 ] = ns[ n] [ 1 ] . min ( i) ; // This can probably be more agressive
111+
112+ if damaged_seen == groups[ 0 ] {
113+ if ( i + 1 ) > groups[ n] {
114+ // This set must be preceeded by an undamaged mirror
115+ assert ! ( !matches!( records[ i - groups[ n] ] , State :: Damaged ) ) ;
116+
117+ if matches ! ( records[ i - groups[ n] ] , State :: Unknown ) {
118+ records[ i - groups[ n] ] = State :: Operational ;
119+ ok_known += 1 ;
120+ unknown -= 1 ;
121+ }
122+
123+ ns[ n] [ 0 ] += 1 ;
124+ a = a. saturating_sub ( 1 ) ;
125+ for m in ( 0 ..ns. len ( ) ) . filter ( |& m| m != n) {
126+ ns[ m] [ 1 ] = ns[ m] [ 1 ] . min ( a) ;
127+ }
128+ }
129+
130+ if ( i + 1 ) < records. len ( ) {
131+ // This set must be followed by an undamaged mirror
132+ assert ! ( !matches!( records[ i + 1 ] , State :: Damaged ) ) ;
133+
134+ if matches ! ( records[ i + 1 ] , State :: Unknown ) {
135+ records[ i + 1 ] = State :: Operational ;
136+ ok_known += 1 ;
137+ unknown -= 1 ;
138+ }
139+
140+ assert_eq ! ( ns[ n + 1 ] [ 0 ] , 1 ) ; // already accounted for there being at least 1
141+ }
142+
143+ ok_seen = 0 ;
144+ damaged_seen = 0 ;
145+ unknown_seen = 0 ;
146+
147+ n += 1 ;
148+ }
149+ } ,
150+ State :: Operational => {
151+ ok_seen += 1 ;
152+ } ,
153+ State :: Unknown => {
154+ unknown_seen += 1 ;
155+ } ,
156+ }
157+
158+ // We have to always maintain these invariants
159+ assert ! ( ok_seen <= n_ok) ;
160+ assert ! ( damaged_seen <= n_damaged) ;
161+ assert_eq ! ( ok_known + damaged_known + unknown, records. len( ) ) ;
162+ assert ! ( ns. iter( ) . map( |[ lower, _] | lower) . sum:: <usize >( ) <= n_ok, "violated lower-bounds: {ns:?}" ) ;
163+ assert ! ( ns. iter( ) . map( |[ _, upper] | upper) . sum:: <usize >( ) >= n_ok, "violated upper-bounds: {ns:?}" ) ;
164+ assert ! ( ns. iter( ) . all( |[ lower, upper] | lower <= upper) ) ;
165+ }
166+
167+ // We've done all we can
168+ println ! ( ) ;
169+ }
170+
72171 0
73172}
74173
0 commit comments