11use crate :: utils:: direction:: Direction ;
22use crate :: utils:: point:: Point ;
3- use std:: collections:: { HashSet , VecDeque } ;
3+ use std:: collections:: HashSet ;
44
5+ /// Represents a contiguous region of points with a filled body.
6+ /// This means that there are no holes in the region and all points are connected.
57#[ derive( Debug , PartialEq ) ]
6- pub struct Region {
8+ pub struct FilledRegion {
79 points : HashSet < Point > ,
810}
911
10- impl TryFrom < HashSet < Point > > for Region {
12+ impl TryFrom < HashSet < Point > > for FilledRegion {
1113 type Error = String ;
1214
1315 fn try_from ( value : HashSet < Point > ) -> Result < Self , Self :: Error > {
1416 if value. is_empty ( ) {
15- return Err ( "Region must have at least one point" . to_string ( ) ) ;
17+ return Err ( "FilledRegion must have at least one point" . to_string ( ) ) ;
1618 }
1719
1820 if value. len ( ) > 1 {
@@ -21,30 +23,26 @@ impl TryFrom<HashSet<Point>> for Region {
2123 . all ( |& p| p. adjacent ( ) . iter ( ) . filter ( |& pp| value. contains ( pp) ) . count ( ) > 0 ) ;
2224
2325 if !has_only_valid {
24- return Err ( "Invalid region. Is built with not adjacent points." . to_string ( ) ) ;
26+ return Err ( "Invalid filled region. Is built with not adjacent points." . to_string ( ) ) ;
2527 }
2628 }
2729
2830 Ok ( Self { points : value } )
2931 }
3032}
3133
32- impl Region {
34+ impl FilledRegion {
3335 pub fn perimeter ( & self ) -> usize {
34- let mut queue: VecDeque < & Point > = VecDeque :: from_iter ( & self . points ) ;
35- let mut perimeter = 0 ;
36-
37- while let Some ( point) = queue. pop_front ( ) {
38- let how_many_surrounding = & point
36+ self . points . iter ( ) . fold ( 0 , |perimeter, point| {
37+ // point inside filled region has 0 surroundings, so it isn't on the perimeter
38+ let how_many_surrounding = point
3939 . adjacent ( )
4040 . iter ( )
4141 . filter ( |p| self . points . contains ( p) )
4242 . count ( ) ;
4343
44- perimeter += 4 - how_many_surrounding;
45- }
46-
47- perimeter
44+ perimeter + 4 - how_many_surrounding
45+ } )
4846 }
4947
5048 pub fn area ( & self ) -> usize {
@@ -96,16 +94,16 @@ impl Region {
9694
9795#[ cfg( test) ]
9896mod test {
97+ use crate :: utils:: filled_region:: FilledRegion ;
9998 use crate :: utils:: grid:: Grid ;
10099 use crate :: utils:: point:: Point ;
101- use crate :: utils:: region:: Region ;
102100 use std:: collections:: HashSet ;
103101
104102 #[ test]
105103 fn try_from_empty_hashset ( ) {
106104 assert_eq ! (
107- Err ( "Region must have at least one point" . to_string( ) ) ,
108- Region :: try_from( HashSet :: new( ) )
105+ Err ( "FilledRegion must have at least one point" . to_string( ) ) ,
106+ FilledRegion :: try_from( HashSet :: new( ) )
109107 ) ;
110108 }
111109
@@ -114,15 +112,15 @@ mod test {
114112 let set = HashSet :: from_iter ( vec ! [ Point :: new( 1 , 1 ) , Point :: new( 1 , 3 ) ] ) ;
115113
116114 assert_eq ! (
117- Err ( "Invalid region. Is built with not adjacent points." . to_string( ) ) ,
118- Region :: try_from( set)
115+ Err ( "Invalid filled region. Is built with not adjacent points." . to_string( ) ) ,
116+ FilledRegion :: try_from( set)
119117 ) ;
120118
121119 let set = HashSet :: from_iter ( vec ! [ Point :: new( 1 , 1 ) , Point :: new( 2 , 2 ) ] ) ;
122120
123121 assert_eq ! (
124- Err ( "Invalid region. Is built with not adjacent points." . to_string( ) ) ,
125- Region :: try_from( set)
122+ Err ( "Invalid filled region. Is built with not adjacent points." . to_string( ) ) ,
123+ FilledRegion :: try_from( set)
126124 ) ;
127125 }
128126
@@ -135,14 +133,14 @@ mod test {
135133 Point :: new( 2 , 3 ) ,
136134 ] ) ;
137135
138- assert ! ( Region :: try_from( set) . is_ok( ) ) ;
136+ assert ! ( FilledRegion :: try_from( set) . is_ok( ) ) ;
139137 }
140138
141139 #[ test]
142140 fn try_from_hashmap_with_one_element ( ) {
143141 let set = HashSet :: from_iter ( vec ! [ Point :: new( 1 , 1 ) ] ) ;
144142
145- assert ! ( Region :: try_from( set) . is_ok( ) ) ;
143+ assert ! ( FilledRegion :: try_from( set) . is_ok( ) ) ;
146144 }
147145
148146 #[ test]
@@ -154,11 +152,11 @@ EEEC"#;
154152
155153 let grid = Grid :: < char > :: from ( EXAMPLE ) ;
156154
157- assert_eq ! ( 4 , region_from_grid ( & grid, 'A' ) . corners( ) ) ;
158- assert_eq ! ( 4 , region_from_grid ( & grid, 'B' ) . corners( ) ) ;
159- assert_eq ! ( 4 , region_from_grid ( & grid, 'D' ) . corners( ) ) ;
160- assert_eq ! ( 4 , region_from_grid ( & grid, 'E' ) . corners( ) ) ;
161- assert_eq ! ( 8 , region_from_grid ( & grid, 'C' ) . corners( ) ) ;
155+ assert_eq ! ( 4 , filled_region_from_grid ( & grid, 'A' ) . corners( ) ) ;
156+ assert_eq ! ( 4 , filled_region_from_grid ( & grid, 'B' ) . corners( ) ) ;
157+ assert_eq ! ( 4 , filled_region_from_grid ( & grid, 'D' ) . corners( ) ) ;
158+ assert_eq ! ( 4 , filled_region_from_grid ( & grid, 'E' ) . corners( ) ) ;
159+ assert_eq ! ( 8 , filled_region_from_grid ( & grid, 'C' ) . corners( ) ) ;
162160 }
163161
164162 #[ test]
@@ -171,7 +169,7 @@ EEEEE"#;
171169
172170 let grid = Grid :: < char > :: from ( EXAMPLE ) ;
173171
174- assert_eq ! ( 12 , region_from_grid ( & grid, 'E' ) . corners( ) ) ;
172+ assert_eq ! ( 12 , filled_region_from_grid ( & grid, 'E' ) . corners( ) ) ;
175173 }
176174
177175 #[ test]
@@ -185,12 +183,67 @@ AAAAAA"#;
185183
186184 let grid = Grid :: < char > :: from ( EXAMPLE ) ;
187185
188- assert_eq ! ( 12 , region_from_grid( & grid, 'A' ) . corners( ) ) ;
186+ assert_eq ! ( 12 , filled_region_from_grid( & grid, 'A' ) . corners( ) ) ;
187+ }
188+
189+ #[ test]
190+ fn perimeter_of_single_point ( ) {
191+ let filled_region = FilledRegion :: try_from ( HashSet :: from ( [ Point :: new ( 0 , 0 ) ] ) ) . unwrap ( ) ;
192+
193+ assert_eq ! ( 4 , filled_region. perimeter( ) ) ;
194+ }
195+
196+ #[ test]
197+ fn perimeter_of_2x1_line ( ) {
198+ let filled_region =
199+ FilledRegion :: try_from ( HashSet :: from ( [ Point :: new ( 0 , 0 ) , Point :: new ( 0 , 1 ) ] ) ) . unwrap ( ) ;
200+
201+ assert_eq ! ( 6 , filled_region. perimeter( ) ) ;
202+ }
203+
204+ #[ test]
205+ fn perimeter_of_2x2_square ( ) {
206+ let filled_region = FilledRegion :: try_from ( HashSet :: from ( [
207+ Point :: new ( 0 , 0 ) ,
208+ Point :: new ( 0 , 1 ) ,
209+ Point :: new ( 1 , 0 ) ,
210+ Point :: new ( 1 , 1 ) ,
211+ ] ) )
212+ . unwrap ( ) ;
213+
214+ assert_eq ! ( 8 , filled_region. perimeter( ) ) ;
215+ }
216+
217+ #[ test]
218+ fn perimeter_of_l_shape ( ) {
219+ let filled_region = FilledRegion :: try_from ( HashSet :: from ( [
220+ Point :: new ( 0 , 0 ) ,
221+ Point :: new ( 1 , 0 ) ,
222+ Point :: new ( 2 , 0 ) ,
223+ Point :: new ( 2 , 1 ) ,
224+ ] ) )
225+ . unwrap ( ) ;
226+
227+ assert_eq ! ( 10 , filled_region. perimeter( ) ) ;
228+ }
229+
230+ #[ test]
231+ fn perimeter_of_plus_shape ( ) {
232+ let filled_region = FilledRegion :: try_from ( HashSet :: from ( [
233+ Point :: new ( 1 , 0 ) ,
234+ Point :: new ( 0 , 1 ) ,
235+ Point :: new ( 1 , 1 ) ,
236+ Point :: new ( 2 , 1 ) ,
237+ Point :: new ( 1 , 2 ) ,
238+ ] ) )
239+ . unwrap ( ) ;
240+
241+ assert_eq ! ( 12 , filled_region. perimeter( ) ) ;
189242 }
190243
191- // todo: move get region to grid??
192- // todo: handle multiple regions with the same element
193- fn region_from_grid ( grid : & Grid < char > , element : char ) -> Region {
194- Region :: try_from ( HashSet :: from_iter ( grid. get_all_positions ( & element) ) ) . unwrap ( )
244+ // todo: move get filled region to grid??
245+ // todo: handle multiple filled regions with the same element
246+ fn filled_region_from_grid ( grid : & Grid < char > , element : char ) -> FilledRegion {
247+ FilledRegion :: try_from ( HashSet :: from_iter ( grid. get_all_positions ( & element) ) ) . unwrap ( )
195248 }
196249}
0 commit comments