@@ -2,68 +2,49 @@ use std::{collections::HashSet, convert::identity};
2
2
3
3
use aoc_lib:: { direction:: cardinal:: Direction , matrix:: Matrix } ;
4
4
use common:: { solution, Answer } ;
5
- use nd_vec:: Vec2 ;
5
+ use itertools:: Itertools ;
6
+ use nd_vec:: { vector, Vec2 } ;
6
7
7
8
solution ! ( "Garden Groups" , 12 ) ;
8
9
9
10
fn part_a ( input : & str ) -> Answer {
10
11
let mut garden = Garden :: parse ( input) ;
11
-
12
- let mut sum = 0 ;
13
-
14
- for pos in garden. matrix . clone ( ) . iter ( ) . map ( |( pos, _) | pos) {
15
- let ( area, perimeter) = garden. flood ( pos) ;
16
- sum += area * perimeter;
17
- }
18
-
19
- sum. into ( )
12
+ garden
13
+ . points ( )
14
+ . filter_map ( |x| garden. flood ( x) )
15
+ . map ( |( area, perimeter) | area. len ( ) * perimeter)
16
+ . sum :: < usize > ( )
17
+ . into ( )
20
18
}
21
19
22
20
fn part_b ( input : & str ) -> Answer {
23
21
let mut garden = Garden :: parse ( input) ;
24
22
25
23
let mut sum = 0 ;
26
-
27
- for pos in garden. matrix . clone ( ) . iter ( ) . map ( |( pos, _) | pos) {
28
- let plant = * garden. matrix . get ( pos) . unwrap ( ) ;
29
- let ( area, perimeter) = garden. flood_b ( pos) ;
30
- if perimeter. is_empty ( ) {
31
- continue ;
32
- }
33
-
24
+ for ( area, _) in garden. points ( ) . filter_map ( |x| garden. flood ( x) ) {
34
25
let mut corners = 0 ;
35
26
36
27
for & point in area. iter ( ) {
37
- for ( a, b) in [
38
- ( Direction :: Up , Direction :: Right ) ,
39
- ( Direction :: Right , Direction :: Down ) ,
40
- ( Direction :: Down , Direction :: Left ) ,
41
- ( Direction :: Left , Direction :: Up ) ,
42
- ] {
43
- // if a and b are both not in area +1
44
- if !area. contains ( & a. advance ( point) ) && !area. contains ( & b. advance ( point) ) {
45
- corners += 1 ;
46
- }
28
+ // Count convex corners by checking to see that the wall is not in
29
+ // any cardinal direction and a direction orthogonal to that
30
+ for a in Direction :: ALL {
31
+ corners += ( !area. contains ( & a. wrapping_advance ( point) )
32
+ && !area. contains ( & a. turn_right ( ) . wrapping_advance ( point) ) )
33
+ as u32 ;
47
34
}
48
35
49
- for ( a, b) in [
50
- ( Direction :: Up , Direction :: Right ) ,
51
- ( Direction :: Right , Direction :: Down ) ,
52
- ( Direction :: Down , Direction :: Left ) ,
53
- ( Direction :: Left , Direction :: Up ) ,
54
- ] {
55
- let e = a. as_vector :: < i32 > ( ) + b. as_vector ( ) ;
56
- if area. contains ( & a. advance ( point) )
57
- && area. contains ( & b. advance ( point) )
58
- && !area. contains ( & ( point + e) )
59
- {
60
- corners += 1 ;
61
- }
36
+ // Count the concave angles by looking for when both the orthogonal
37
+ // directions are in the area, but not the diagonal between them.
38
+ for a in Direction :: ALL {
39
+ let b = a. turn_right ( ) ;
40
+ corners += ( area. contains ( & a. wrapping_advance ( point) )
41
+ && area. contains ( & b. wrapping_advance ( point) )
42
+ && !area. contains ( & b. wrapping_advance ( a. wrapping_advance ( point) ) ) )
43
+ as u32 ;
62
44
}
63
45
}
64
46
65
- println ! ( "{} * {corners} [{plant}]" , area. len( ) ) ;
66
- sum += area. len ( ) * corners;
47
+ sum += area. len ( ) as u32 * corners;
67
48
}
68
49
69
50
sum. into ( )
@@ -84,78 +65,46 @@ impl Garden {
84
65
}
85
66
}
86
67
87
- // -> (area, perimeter)
88
- fn flood ( & mut self , start : Vec2 < usize > ) -> ( u32 , u32 ) {
89
- let ( mut area , mut perimeter ) = ( 1 , 0 ) ;
90
- let plant = self . matrix . get ( start ) . unwrap ( ) ;
91
-
92
- let mut queue = Vec :: new ( ) ;
68
+ fn points ( & self ) -> impl Iterator < Item = Vec2 < usize > > {
69
+ let size = self . matrix . size ;
70
+ ( 0 ..size . x ( ) )
71
+ . cartesian_product ( 0 ..size . y ( ) )
72
+ . map ( | ( x , y ) | vector ! ( x , y ) )
73
+ }
93
74
75
+ fn flood ( & mut self , start : Vec2 < usize > ) -> Option < ( HashSet < Vec2 < usize > > , usize ) > {
94
76
if !self . seen . insert ( start) {
95
- return ( 0 , 0 ) ;
96
- }
97
- queue. push ( start) ;
98
-
99
- while let Some ( pos) = queue. pop ( ) {
100
- for dir in Direction :: ALL . into_iter ( ) {
101
- let Some ( next) = dir. try_advance ( pos) else {
102
- perimeter += 1 ;
103
- continue ;
104
- } ;
105
- if !self . matrix . contains ( next) {
106
- perimeter += 1 ;
107
- continue ;
108
- }
109
-
110
- if self . matrix . get ( next) . unwrap ( ) == plant {
111
- if self . seen . insert ( next) {
112
- area += 1 ;
113
- queue. push ( next) ;
114
- }
115
- } else {
116
- perimeter += 1
117
- }
118
- }
77
+ return None ;
119
78
}
120
79
121
- ( area, perimeter)
122
- }
123
-
124
- fn flood_b ( & mut self , start : Vec2 < usize > ) -> ( HashSet < Vec2 < i32 > > , HashSet < Vec2 < i32 > > ) {
125
- let ( mut area, mut perimeter) = ( HashSet :: new ( ) , HashSet :: new ( ) ) ;
126
- area. insert ( start. try_cast :: < i32 > ( ) . unwrap ( ) ) ;
127
- let plant = self . matrix . get ( start) . unwrap ( ) ;
80
+ let mut area = HashSet :: new ( ) ;
81
+ let mut perimeter = 0 ;
128
82
129
83
let mut queue = Vec :: new ( ) ;
84
+ let plant = self . matrix . get ( start) . unwrap ( ) ;
130
85
131
- if !self . seen . insert ( start) {
132
- return ( HashSet :: new ( ) , HashSet :: new ( ) ) ;
133
- }
86
+ area. insert ( start) ;
134
87
queue. push ( start) ;
135
88
136
89
while let Some ( pos) = queue. pop ( ) {
137
- for dir in Direction :: ALL . into_iter ( ) {
138
- let Some ( next) = dir. try_advance ( pos) else {
139
- perimeter. insert ( dir. advance ( pos. try_cast :: < i32 > ( ) . unwrap ( ) ) ) ;
140
- continue ;
141
- } ;
90
+ for next in Direction :: ALL . map ( |x| x. wrapping_advance ( pos) ) {
142
91
if !self . matrix . contains ( next) {
143
- perimeter. insert ( next . try_cast :: < i32 > ( ) . unwrap ( ) ) ;
92
+ perimeter += 1 ;
144
93
continue ;
145
94
}
146
95
147
96
if self . matrix . get ( next) . unwrap ( ) == plant {
148
97
if self . seen . insert ( next) {
149
- area. insert ( next. try_cast :: < i32 > ( ) . unwrap ( ) ) ;
98
+ area. insert ( next) ;
150
99
queue. push ( next) ;
151
100
}
152
101
} else {
153
- perimeter. insert ( next . try_cast :: < i32 > ( ) . unwrap ( ) ) ;
102
+ perimeter += 1 ;
154
103
}
155
104
}
156
105
}
157
106
158
- ( area, perimeter)
107
+ Some ( ( area, perimeter) )
159
108
}
160
109
}
161
110
0 commit comments