11//! # 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+ //! ```
223use crate :: util:: grid:: * ;
3- use crate :: util:: hash:: * ;
424use crate :: util:: point:: * ;
525use std:: collections:: VecDeque ;
626
7- const CLOCKWISE : [ Point ; 5 ] = [ UP , RIGHT , DOWN , LEFT , UP ] ;
8-
927pub fn parse ( input : & str ) -> Grid < u8 > {
1028 Grid :: parse ( input)
1129}
1230
1331pub fn part1 ( grid : & Grid < u8 > ) -> i32 {
32+ let mut result = 0 ;
1433 let mut todo = VecDeque :: new ( ) ;
1534 let mut seen = grid. same_size_with ( false ) ;
1635 let mut added = grid. same_size_with ( false ) ;
17- let mut result = 0 ;
1836
1937 for y in 0 ..grid. height {
2038 for x in 0 ..grid. width {
@@ -23,44 +41,46 @@ pub fn part1(grid: &Grid<u8>) -> i32 {
2341 continue ;
2442 }
2543
44+ // Flood fill each region.
2645 let kind = grid[ point] ;
2746 let mut area = 0 ;
28- let mut perm = 0 ;
47+ let mut perimeter = 0 ;
2948
3049 todo. push_back ( point) ;
3150 seen[ point] = true ;
3251
3352 while let Some ( point) = todo. pop_front ( ) {
34- area += 1 ;
35- perm += 4 ;
3653 added[ point] = true ;
54+ area += 1 ;
55+ perimeter += 4 ;
3756
3857 for next in ORTHOGONAL . map ( |o| point + o) {
3958 if grid. contains ( next) && grid[ next] == kind {
4059 if !seen[ next] {
4160 seen[ next] = true ;
4261 todo. push_back ( next) ;
4362 }
63+ // Remove both sides from neighbouring plots.
4464 if added[ next] {
45- perm -= 2 ;
65+ perimeter -= 2 ;
4666 }
4767 }
4868 }
4969 }
5070
51- result += area * perm ;
71+ result += area * perimeter ;
5272 }
5373 }
5474
5575 result
5676}
5777
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 {
6379 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 ( ) ;
6484
6585 for y in 0 ..grid. height {
6686 for x in 0 ..grid. width {
@@ -70,26 +90,15 @@ pub fn part2(grid: &Grid<u8>) -> u32 {
7090 }
7191
7292 let kind = grid[ point] ;
73- let mut size = 0 ;
93+ let id = y * grid . width + x ;
7494 let mut sides = 0 ;
7595
7696 todo. push_back ( point) ;
7797 seen[ point] = true ;
7898
7999 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) ;
93102
94103 for next in ORTHOGONAL . map ( |o| point + o) {
95104 if grid. contains ( next) && grid[ next] == kind && !seen[ next] {
@@ -99,20 +108,26 @@ pub fn part2(grid: &Grid<u8>) -> u32 {
99108 }
100109 }
101110
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 ) ;
105114
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) {
107125 sides += 1 ;
108- } else if count == 4 {
109- sides += 2 ;
110126 }
111127 }
112128
113- corner. clear ( ) ;
114- middle. clear ( ) ;
115- result += size * sides;
129+ result += region. len ( ) * sides;
130+ region. clear ( ) ;
116131 }
117132 }
118133
0 commit comments