1
+ use std:: collections:: { BTreeSet , BTreeMap } ;
2
+
3
+ #[ allow( dead_code) ]
4
+ fn print_positions ( positions : & BTreeSet < ( isize , isize ) > ) {
5
+ let pos_bounds = position_bounds ( positions) ;
6
+ // println!("{:?}", pos_bounds);
7
+ ( pos_bounds. 0 . 1 ..=pos_bounds. 1 . 1 ) . rev ( ) . for_each (
8
+ |y| {
9
+ ( pos_bounds. 0 . 0 ..=pos_bounds. 1 . 0 ) . for_each (
10
+ |x| {
11
+ if positions. contains ( & ( x, y) ) {
12
+ print ! ( "#" )
13
+ } else {
14
+ print ! ( "." )
15
+ }
16
+ }
17
+ ) ;
18
+ println ! ( ) ;
19
+ }
20
+ )
21
+ }
22
+
23
+ fn position_bounds ( positions : & BTreeSet < ( isize , isize ) > ) -> ( ( isize , isize ) , ( isize , isize ) ) {
24
+ (
25
+ (
26
+ positions. iter ( ) . map ( |( x, _) | * x) . min_by ( |lx, rx| lx. cmp ( rx) ) . unwrap ( ) ,
27
+ positions. iter ( ) . map ( |( _, y) | * y) . min_by ( |ly, ry| ly. cmp ( ry) ) . unwrap ( )
28
+ ) ,
29
+ (
30
+ positions. iter ( ) . map ( |( x, _) | * x) . max_by ( |lx, rx| lx. cmp ( rx) ) . unwrap ( ) ,
31
+ positions. iter ( ) . map ( |( _, y) | * y) . max_by ( |ly, ry| ly. cmp ( ry) ) . unwrap ( )
32
+ )
33
+ )
34
+ }
35
+
36
+ fn initial_positions ( plots : & str ) -> BTreeSet < ( isize , isize ) > {
37
+ let mut bt = BTreeSet :: new ( ) ;
38
+ plots. lines ( ) . rev ( ) . enumerate ( )
39
+ . for_each (
40
+ |( y, l) | {
41
+ l. chars ( ) . enumerate ( )
42
+ . for_each (
43
+ |( x, c) | {
44
+ if c == '#' {
45
+ bt. insert ( ( x as isize , y as isize ) ) ;
46
+ }
47
+ }
48
+ )
49
+ }
50
+ ) ;
51
+ bt
52
+ }
53
+
54
+ fn check_north ( pos : & ( isize , isize ) , positions : & BTreeSet < ( isize , isize ) > ) -> bool {
55
+ let nw = !positions. contains ( & ( pos. 0 - 1 , pos. 1 + 1 ) ) ;
56
+ let n = !positions. contains ( & ( pos. 0 , pos. 1 + 1 ) ) ;
57
+ let ne = !positions. contains ( & ( pos. 0 + 1 , pos. 1 + 1 ) ) ;
58
+ nw && n && ne
59
+ }
60
+
61
+ fn check_south ( pos : & ( isize , isize ) , positions : & BTreeSet < ( isize , isize ) > ) -> bool {
62
+ let sw = !positions. contains ( & ( pos. 0 - 1 , pos. 1 - 1 ) ) ;
63
+ let s = !positions. contains ( & ( pos. 0 , pos. 1 - 1 ) ) ;
64
+ let se = !positions. contains ( & ( pos. 0 + 1 , pos. 1 - 1 ) ) ;
65
+ sw && s && se
66
+ }
67
+
68
+ fn check_east ( pos : & ( isize , isize ) , positions : & BTreeSet < ( isize , isize ) > ) -> bool {
69
+ let ne = !positions. contains ( & ( pos. 0 + 1 , pos. 1 + 1 ) ) ;
70
+ let e = !positions. contains ( & ( pos. 0 + 1 , pos. 1 ) ) ;
71
+ let se = !positions. contains ( & ( pos. 0 + 1 , pos. 1 - 1 ) ) ;
72
+ ne && e && se
73
+ }
74
+
75
+ fn check_west ( pos : & ( isize , isize ) , positions : & BTreeSet < ( isize , isize ) > ) -> bool {
76
+ let nw = !positions. contains ( & ( pos. 0 - 1 , pos. 1 + 1 ) ) ;
77
+ let w = !positions. contains ( & ( pos. 0 - 1 , pos. 1 ) ) ;
78
+ let sw = !positions. contains ( & ( pos. 0 - 1 , pos. 1 - 1 ) ) ;
79
+ nw && w && sw
80
+ }
81
+
82
+ fn round ( positions : & BTreeSet < ( isize , isize ) > , round : usize ) -> BTreeSet < ( isize , isize ) > {
83
+ let intents: BTreeMap < _ , _ > = positions. iter ( )
84
+ . map (
85
+ |pos| {
86
+ let round_tests = vec ! [
87
+ ( check_north( pos, positions) , ( pos. 0 , pos. 1 + 1 ) ) ,
88
+ ( check_south( pos, positions) , ( pos. 0 , pos. 1 - 1 ) ) ,
89
+ ( check_west( pos, positions) , ( pos. 0 - 1 , pos. 1 ) ) ,
90
+ ( check_east( pos, positions) , ( pos. 0 + 1 , pos. 1 ) )
91
+ ] ;
92
+ if round_tests. iter ( ) . all ( |( b, _) | * b) {
93
+ ( pos, * pos)
94
+ } else {
95
+ let round_cycle = round_tests. iter ( ) . cycle ( ) ;
96
+ let mut round_intent = None ;
97
+ for ( test, intent) in round_cycle. skip ( round) . take ( 4 ) {
98
+ if * test {
99
+ round_intent = Some ( intent) ;
100
+ break ;
101
+ }
102
+ }
103
+ let fround_intent = round_intent. unwrap_or ( pos) ;
104
+ ( pos, * fround_intent)
105
+ }
106
+ }
107
+ )
108
+ . collect ( ) ;
109
+ let unique_intents = intents. values ( )
110
+ . fold (
111
+ ( BTreeSet :: new ( ) , BTreeSet :: new ( ) ) ,
112
+ |( mut uniq, mut dup) , int| {
113
+ if dup. contains ( int) {
114
+ ( )
115
+ } else if uniq. contains ( int) {
116
+ uniq. remove ( int) ;
117
+ dup. insert ( int) ;
118
+ } else {
119
+ uniq. insert ( int) ;
120
+ } ;
121
+ ( uniq, dup)
122
+ }
123
+ ) . 0 ;
124
+
125
+ intents. iter ( )
126
+ . filter_map (
127
+ |( pos, int) | {
128
+ if unique_intents. contains ( & int) {
129
+ Some ( * int)
130
+ } else {
131
+ Some ( * * pos)
132
+ }
133
+ }
134
+ )
135
+ . collect ( )
136
+ }
137
+
138
+ fn empty_plots ( positions : & BTreeSet < ( isize , isize ) > ) -> u64 {
139
+ let ( minbounds, maxbounds) = position_bounds ( positions) ;
140
+ ( minbounds. 0 ..=maxbounds. 0 ) . fold (
141
+ 0 ,
142
+ |cum, x| {
143
+ cum + ( minbounds. 1 ..=maxbounds. 1 ) . fold (
144
+ 0 ,
145
+ |ycum, y| {
146
+ if positions. contains ( & ( x, y) ) {
147
+ ycum
148
+ } else {
149
+ ycum + 1
150
+ }
151
+ }
152
+ )
153
+ }
154
+ )
155
+ }
156
+
157
+ fn game_of_plating ( plots : & str , rounds : usize ) -> ( u64 , u64 ) {
158
+ let mut positions = initial_positions ( plots) ;
159
+ let mut stable_round = 0 ;
160
+ for r in 0 ..rounds {
161
+ let tmp = round ( & positions, r) ;
162
+ stable_round = r + 1 ;
163
+ if tmp. eq ( & positions) {
164
+ break ;
165
+ } else {
166
+ positions = tmp;
167
+ }
168
+ }
169
+ ( empty_plots ( & positions) , stable_round as u64 )
170
+ }
171
+
172
+ pub fn elfs_game_of_plating ( input_path : & str ) -> u64 {
173
+ let content = std:: fs:: read_to_string ( input_path) ;
174
+ match content {
175
+ Ok ( content) => game_of_plating ( & content, 10 ) . 0 ,
176
+ Err ( er) => {
177
+ println ! ( "{}" , er) ;
178
+ 0
179
+ }
180
+ }
181
+ }
182
+
183
+ pub fn elfs_game_of_plating_till_stable ( input_path : & str ) -> u64 {
184
+ let content = std:: fs:: read_to_string ( input_path) ;
185
+ match content {
186
+ Ok ( content) => game_of_plating ( & content, std:: usize:: MAX ) . 1 ,
187
+ Err ( er) => {
188
+ println ! ( "{}" , er) ;
189
+ 0
190
+ }
191
+ }
192
+ }
193
+
194
+ #[ cfg( test) ]
195
+ mod tests {
196
+ use super :: * ;
197
+
198
+ const TEST_INP1 : & str =
199
+ r#"....#..
200
+ ..###.#
201
+ #...#.#
202
+ .#...##
203
+ #.###..
204
+ ##.#.##
205
+ .#..#.."# ;
206
+
207
+ #[ test]
208
+ fn test_input1 ( ) {
209
+ assert_eq ! ( game_of_plating( TEST_INP1 , 10 ) . 0 , 110 )
210
+ }
211
+
212
+ #[ test]
213
+ fn test_input1_part2 ( ) {
214
+ assert_eq ! ( game_of_plating( TEST_INP1 , std:: usize :: MAX ) . 1 , 20 )
215
+ }
216
+ }
0 commit comments