@@ -2,21 +2,22 @@ use anyhow::Result;
22use aoc_rust_common:: Solution ;
33use regex:: Regex ;
44use std:: cmp:: { max, min} ;
5- use std:: collections:: HashSet ;
65
76pub struct Day17 ;
87
9- #[ derive( Clone , Copy , Debug , Eq , PartialEq , Hash ) ]
10- struct Point {
11- x : i32 ,
12- y : i32 ,
8+ #[ derive( Clone , Copy , Debug , Eq , PartialEq ) ]
9+ enum Tile {
10+ Sand ,
11+ Clay ,
12+ Flowing ,
13+ Still ,
1314}
1415
15- fn parse ( input : & str ) -> ( HashSet < Point > , i32 , i32 ) {
16- let mut clay = HashSet :: new ( ) ;
16+ fn parse ( input : & str ) -> ( Vec < Vec < Tile > > , i32 , i32 , i32 , i32 ) {
17+ let mut clay_points = Vec :: new ( ) ;
1718 let re = Regex :: new ( r"(x|y)=(\d+), (x|y)=(\d+)..(\d+)" ) . unwrap ( ) ;
18- let mut min_y = i32:: MAX ;
19- let mut max_y = i32:: MIN ;
19+ let ( mut min_x , mut max_x ) = ( i32:: MAX , i32 :: MIN ) ;
20+ let ( mut min_y , mut max_y) = ( i32:: MAX , i32 :: MIN ) ;
2021
2122 for line in input. lines ( ) {
2223 let caps = re. captures ( line) . unwrap ( ) ;
@@ -25,114 +26,126 @@ fn parse(input: &str) -> (HashSet<Point>, i32, i32) {
2526 let b2: i32 = caps[ 5 ] . parse ( ) . unwrap ( ) ;
2627
2728 if & caps[ 1 ] == "x" {
29+ let x = a;
30+ min_x = min ( min_x, x) ;
31+ max_x = max ( max_x, x) ;
2832 min_y = min ( min_y, b1) ;
2933 max_y = max ( max_y, b2) ;
3034 for y in b1..=b2 {
31- clay . insert ( Point { x : a , y } ) ;
35+ clay_points . push ( ( x , y) ) ;
3236 }
3337 } else {
34- min_y = min ( min_y, a) ;
35- max_y = max ( max_y, a) ;
38+ let y = a;
39+ min_y = min ( min_y, y) ;
40+ max_y = max ( max_y, y) ;
41+ min_x = min ( min_x, b1) ;
42+ max_x = max ( max_x, b2) ;
3643 for x in b1..=b2 {
37- clay . insert ( Point { x, y : a } ) ;
44+ clay_points . push ( ( x, y) ) ;
3845 }
3946 }
4047 }
41- ( clay, min_y, max_y)
48+
49+ // Add padding to x to allow water to flow around clay
50+ min_x -= 1 ;
51+ max_x += 1 ;
52+
53+ let width = ( max_x - min_x + 1 ) as usize ;
54+ let height = ( max_y + 1 ) as usize ;
55+ let mut grid = vec ! [ vec![ Tile :: Sand ; width] ; height] ;
56+
57+ for ( x, y) in clay_points {
58+ grid[ y as usize ] [ ( x - min_x) as usize ] = Tile :: Clay ;
59+ }
60+
61+ ( grid, min_x, max_x, min_y, max_y)
4262}
4363
4464fn solve ( input : & str ) -> ( usize , usize ) {
45- let ( clay, min_y, max_y) = parse ( input) ;
46- let mut flowing_water = HashSet :: new ( ) ;
47- let mut still_water = HashSet :: new ( ) ;
48- let mut stack = vec ! [ Point { x: 500 , y: 0 } ] ;
65+ let ( mut grid, min_x, _, min_y, max_y) = parse ( input) ;
66+ let mut stack = vec ! [ ( 500i32 , 0i32 ) ] ;
4967
50- while let Some ( point) = stack. pop ( ) {
51- if point. y > max_y {
52- continue ;
53- }
68+ while let Some ( ( sx, sy) ) = stack. pop ( ) {
69+ let mut y = sy;
70+ let x = sx;
5471
55- let mut current = point ;
56- while current . y <= max_y && !clay . contains ( & current ) {
57- flowing_water . insert ( current ) ;
58- current . y += 1 ;
72+ // Flow down
73+ while y <= max_y && grid [ y as usize ] [ ( x - min_x ) as usize ] == Tile :: Sand {
74+ grid [ y as usize ] [ ( x - min_x ) as usize ] = Tile :: Flowing ;
75+ y += 1 ;
5976 }
6077
61- if current . y > max_y {
78+ if y > max_y || grid [ y as usize ] [ ( x - min_x ) as usize ] == Tile :: Flowing {
6279 continue ;
6380 }
6481
65- current. y -= 1 ; // Step back up to solid ground
66-
67- loop {
68- let mut left = current;
69- let mut right = current;
70- let mut wall_left = false ;
71- let mut wall_right = false ;
72-
73- while !clay. contains ( & Point {
74- x : left. x - 1 ,
75- y : left. y ,
76- } ) {
77- left. x -= 1 ;
78- let down = Point {
79- x : left. x ,
80- y : left. y + 1 ,
81- } ;
82- if !clay. contains ( & down) && !still_water. contains ( & down) {
83- stack. push ( left) ;
82+ // We hit clay or still water, move back up and fill levels
83+ y -= 1 ;
84+ while y >= sy {
85+ let mut left_x = x;
86+ let mut left_bound = false ;
87+ while grid[ y as usize ] [ ( left_x - min_x) as usize ] != Tile :: Clay {
88+ let below = grid[ ( y + 1 ) as usize ] [ ( left_x - min_x) as usize ] ;
89+ if below == Tile :: Sand || below == Tile :: Flowing {
8490 break ;
8591 }
92+ left_x -= 1 ;
8693 }
87- if clay. contains ( & Point {
88- x : left. x - 1 ,
89- y : left. y ,
90- } ) {
91- wall_left = true ;
94+ if grid[ y as usize ] [ ( left_x - min_x) as usize ] == Tile :: Clay {
95+ left_bound = true ;
96+ left_x += 1 ;
9297 }
9398
94- while !clay. contains ( & Point {
95- x : right. x + 1 ,
96- y : right. y ,
97- } ) {
98- right. x += 1 ;
99- let down = Point {
100- x : right. x ,
101- y : right. y + 1 ,
102- } ;
103- if !clay. contains ( & down) && !still_water. contains ( & down) {
104- stack. push ( right) ;
99+ let mut right_x = x;
100+ let mut right_bound = false ;
101+ while grid[ y as usize ] [ ( right_x - min_x) as usize ] != Tile :: Clay {
102+ let below = grid[ ( y + 1 ) as usize ] [ ( right_x - min_x) as usize ] ;
103+ if below == Tile :: Sand || below == Tile :: Flowing {
105104 break ;
106105 }
106+ right_x += 1 ;
107107 }
108- if clay. contains ( & Point {
109- x : right. x + 1 ,
110- y : right. y ,
111- } ) {
112- wall_right = true ;
108+ if grid[ y as usize ] [ ( right_x - min_x) as usize ] == Tile :: Clay {
109+ right_bound = true ;
110+ right_x -= 1 ;
113111 }
114112
115- for x in left. x ..=right. x {
116- if wall_left && wall_right {
117- still_water. insert ( Point { x, y : current. y } ) ;
118- } else {
119- flowing_water. insert ( Point { x, y : current. y } ) ;
113+ if left_bound && right_bound {
114+ for fill_x in left_x..=right_x {
115+ grid[ y as usize ] [ ( fill_x - min_x) as usize ] = Tile :: Still ;
116+ }
117+ } else {
118+ for fill_x in left_x..=right_x {
119+ grid[ y as usize ] [ ( fill_x - min_x) as usize ] = Tile :: Flowing ;
120120 }
121+ if !left_bound {
122+ stack. push ( ( left_x, y) ) ;
123+ }
124+ if !right_bound {
125+ stack. push ( ( right_x, y) ) ;
126+ }
127+ break ;
121128 }
129+ y -= 1 ;
130+ }
131+ }
122132
123- if !( wall_left && wall_right) {
124- break ;
133+ let mut total_water = 0 ;
134+ let mut still_water = 0 ;
135+ for y in min_y as usize ..=max_y as usize {
136+ for x in 0 ..grid[ y] . len ( ) {
137+ match grid[ y] [ x] {
138+ Tile :: Flowing => total_water += 1 ,
139+ Tile :: Still => {
140+ total_water += 1 ;
141+ still_water += 1 ;
142+ }
143+ _ => { }
125144 }
126- current. y -= 1 ;
127145 }
128146 }
129147
130- let total_water = flowing_water
131- . union ( & still_water)
132- . filter ( |p| p. y >= min_y && p. y <= max_y)
133- . count ( ) ;
134- let still_water_count = still_water. len ( ) ;
135- ( total_water, still_water_count)
148+ ( total_water, still_water)
136149}
137150
138151impl Solution for Day17 {
@@ -154,4 +167,4 @@ impl Solution for Day17 {
154167 }
155168}
156169
157- aoc_rust_common:: aoc_test!( Day17 ) ;
170+ aoc_rust_common:: aoc_test!( Day17 , 162 , 144 , slow ) ;
0 commit comments