1
1
//! # Garden Groups
2
+ //!
3
+ //! Part one flood fills each region, adding 4 to the perimeter for each plot
4
+ //! then subtracting 2 for each neighbour that we've already added.
5
+ //!
6
+ //! Part two counts corners, as the number of corners equals the number of sides.
7
+ //! We remove a corner when another plot is adjacent either up, down, left or right:
8
+ //!
9
+ //! ```none
10
+ //! .#. ...
11
+ //! .#. ##.
12
+ //! ... ...
13
+ //! ```
14
+ //!
15
+ //! We add back a corner when it's concave, for example where a plot is above, right but
16
+ //! not above and to the right:
17
+ //!
18
+ //! ```none
19
+ //! .#.
20
+ //! .##
21
+ //! ...
22
+ //! ```
2
23
use crate :: util:: grid:: * ;
3
- use crate :: util:: hash:: * ;
4
24
use crate :: util:: point:: * ;
5
25
use std:: collections:: VecDeque ;
6
26
7
- const CLOCKWISE : [ Point ; 5 ] = [ UP , RIGHT , DOWN , LEFT , UP ] ;
8
-
9
27
pub fn parse ( input : & str ) -> Grid < u8 > {
10
28
Grid :: parse ( input)
11
29
}
12
30
13
31
pub fn part1 ( grid : & Grid < u8 > ) -> i32 {
32
+ let mut result = 0 ;
14
33
let mut todo = VecDeque :: new ( ) ;
15
34
let mut seen = grid. same_size_with ( false ) ;
16
35
let mut added = grid. same_size_with ( false ) ;
17
- let mut result = 0 ;
18
36
19
37
for y in 0 ..grid. height {
20
38
for x in 0 ..grid. width {
@@ -23,44 +41,46 @@ pub fn part1(grid: &Grid<u8>) -> i32 {
23
41
continue ;
24
42
}
25
43
44
+ // Flood fill each region.
26
45
let kind = grid[ point] ;
27
46
let mut area = 0 ;
28
- let mut perm = 0 ;
47
+ let mut perimeter = 0 ;
29
48
30
49
todo. push_back ( point) ;
31
50
seen[ point] = true ;
32
51
33
52
while let Some ( point) = todo. pop_front ( ) {
34
- area += 1 ;
35
- perm += 4 ;
36
53
added[ point] = true ;
54
+ area += 1 ;
55
+ perimeter += 4 ;
37
56
38
57
for next in ORTHOGONAL . map ( |o| point + o) {
39
58
if grid. contains ( next) && grid[ next] == kind {
40
59
if !seen[ next] {
41
60
seen[ next] = true ;
42
61
todo. push_back ( next) ;
43
62
}
63
+ // Remove both sides from neighbouring plots.
44
64
if added[ next] {
45
- perm -= 2 ;
65
+ perimeter -= 2 ;
46
66
}
47
67
}
48
68
}
49
69
}
50
70
51
- result += area * perm ;
71
+ result += area * perimeter ;
52
72
}
53
73
}
54
74
55
75
result
56
76
}
57
77
58
- pub fn part2 ( grid : & Grid < u8 > ) -> u32 {
59
- let mut seen = grid. same_size_with ( false ) ;
60
- let mut todo = VecDeque :: new ( ) ;
61
- let mut corner = FastMap :: new ( ) ;
62
- let mut middle = FastMap :: new ( ) ;
78
+ pub fn part2 ( grid : & Grid < u8 > ) -> usize {
63
79
let mut result = 0 ;
80
+ let mut todo = VecDeque :: new ( ) ;
81
+ let mut seen = grid. same_size_with ( false ) ;
82
+ let mut added = grid. same_size_with ( -1 ) ;
83
+ let mut region = Vec :: new ( ) ;
64
84
65
85
for y in 0 ..grid. height {
66
86
for x in 0 ..grid. width {
@@ -70,26 +90,15 @@ pub fn part2(grid: &Grid<u8>) -> u32 {
70
90
}
71
91
72
92
let kind = grid[ point] ;
73
- let mut size = 0 ;
93
+ let id = y * grid . width + x ;
74
94
let mut sides = 0 ;
75
95
76
96
todo. push_back ( point) ;
77
97
seen[ point] = true ;
78
98
79
99
while let Some ( point) = todo. pop_front ( ) {
80
- size += 1 ;
81
- let x = 2 * point. x ;
82
- let y = 2 * point. y ;
83
-
84
- * corner. entry ( Point :: new ( x, y) ) . or_insert ( 0 ) += 1 ;
85
- * corner. entry ( Point :: new ( x + 2 , y) ) . or_insert ( 0 ) += 1 ;
86
- * corner. entry ( Point :: new ( x, y + 2 ) ) . or_insert ( 0 ) += 1 ;
87
- * corner. entry ( Point :: new ( x + 2 , y + 2 ) ) . or_insert ( 0 ) += 1 ;
88
-
89
- * middle. entry ( Point :: new ( x + 1 , y) ) . or_insert ( 0 ) += 1 ;
90
- * middle. entry ( Point :: new ( x, y + 1 ) ) . or_insert ( 0 ) += 1 ;
91
- * middle. entry ( Point :: new ( x + 2 , y + 1 ) ) . or_insert ( 0 ) += 1 ;
92
- * middle. entry ( Point :: new ( x + 1 , y + 2 ) ) . or_insert ( 0 ) += 1 ;
100
+ added[ point] = id;
101
+ region. push ( point) ;
93
102
94
103
for next in ORTHOGONAL . map ( |o| point + o) {
95
104
if grid. contains ( next) && grid[ next] == kind && !seen[ next] {
@@ -99,20 +108,26 @@ pub fn part2(grid: &Grid<u8>) -> u32 {
99
108
}
100
109
}
101
110
102
- for ( & point, _ ) in corner . iter ( ) . filter ( | ( _ , & v ) | v < 4 ) {
103
- let freq = CLOCKWISE . map ( |c| * middle . get ( & ( point + c ) ) . unwrap_or ( & 2 ) ) ;
104
- let count = freq . windows ( 2 ) . filter ( |w| w [ 0 ] < 2 && w [ 1 ] < 2 ) . count ( ) ;
111
+ for & point in & region {
112
+ let [ up_left , up , up_right , left , right , down_left , down , down_right ] =
113
+ DIAGONAL . map ( |d| added . contains ( point + d ) && added [ point + d ] == id ) ;
105
114
106
- if count == 1 {
115
+ if !( up || left) || ( up && left && !up_left) {
116
+ sides += 1 ;
117
+ }
118
+ if !( up || right) || ( up && right && !up_right) {
119
+ sides += 1 ;
120
+ }
121
+ if !( down || left) || ( down && left && !down_left) {
122
+ sides += 1 ;
123
+ }
124
+ if !( down || right) || ( down && right && !down_right) {
107
125
sides += 1 ;
108
- } else if count == 4 {
109
- sides += 2 ;
110
126
}
111
127
}
112
128
113
- corner. clear ( ) ;
114
- middle. clear ( ) ;
115
- result += size * sides;
129
+ result += region. len ( ) * sides;
130
+ region. clear ( ) ;
116
131
}
117
132
}
118
133
0 commit comments