Skip to content

Commit c17607e

Browse files
committed
refactor: rename to filled region
1 parent 7adb092 commit c17607e

File tree

4 files changed

+96
-43
lines changed

4 files changed

+96
-43
lines changed

src/solutions/year2024/day12.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ impl Solution for Day12 {
88
Grid::<char>::from(input)
99
.get_all_regions()
1010
.iter()
11-
.map(|region| region.perimeter() * region.area())
11+
.map(|filled_region| filled_region.perimeter() * filled_region.area())
1212
.sum::<usize>()
1313
.to_string()
1414
}
@@ -17,7 +17,7 @@ impl Solution for Day12 {
1717
Grid::<char>::from(input)
1818
.get_all_regions()
1919
.iter()
20-
.map(|region| region.corners() * region.area())
20+
.map(|filled_region| filled_region.corners() * filled_region.area())
2121
.sum::<usize>()
2222
.to_string()
2323
}
Lines changed: 88 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,20 @@
11
use crate::utils::direction::Direction;
22
use 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)]
9896
mod 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
}

src/utils/grid.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use crate::utils::direction::Direction;
2+
use crate::utils::filled_region::FilledRegion;
23
use crate::utils::point::Point;
34
use crate::utils::range::Range;
4-
use crate::utils::region::Region;
55
use crate::utils::surface_range::SurfaceRange;
66
use itertools::Itertools;
77
use std::collections::{BTreeMap, HashMap, HashSet, VecDeque};
@@ -299,11 +299,11 @@ where
299299
.find(|(point, element)| find_func(point, element))
300300
}
301301

302-
pub fn get_all_regions(&self) -> Vec<Region> {
302+
pub fn get_all_regions(&self) -> Vec<FilledRegion> {
303303
let surface = self.surface();
304304
let mut visited: HashSet<Point> = HashSet::new();
305305

306-
let mut regions: Vec<Region> = Vec::new();
306+
let mut regions: Vec<FilledRegion> = Vec::new();
307307

308308
while visited.len() != surface.area() {
309309
let not_visited_func = |point: &Point, _element: &T| !visited.contains(point);
@@ -319,7 +319,7 @@ where
319319
}
320320

321321
/// It is flood fill algorithm implementation
322-
fn extract_region(&self, starting_point: &Point) -> Region {
322+
fn extract_region(&self, starting_point: &Point) -> FilledRegion {
323323
let element = self.get_for_point(starting_point).unwrap();
324324
let mut visited: HashSet<Point> = HashSet::from_iter(vec![*starting_point]);
325325
let mut queue = VecDeque::from(vec![*starting_point]);
@@ -340,7 +340,7 @@ where
340340
}
341341
}
342342

343-
Region::try_from(visited).unwrap()
343+
FilledRegion::try_from(visited).unwrap()
344344
}
345345

346346
pub fn _elements_in_surface(&self, element: T, surface: SurfaceRange) -> Vec<Point> {

src/utils/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
pub mod chain_pattern_finder;
22
pub mod deltoid_surface;
33
pub mod direction;
4+
pub mod filled_region;
45
pub mod graphs;
56
pub mod grid;
67
pub mod line;
@@ -10,7 +11,6 @@ pub mod pair_generator;
1011
pub mod point;
1112
pub mod point3d;
1213
pub mod range;
13-
pub mod region;
1414
pub mod shoelace_formula;
1515
pub mod surface_range;
1616
pub mod vector;

0 commit comments

Comments
 (0)