@@ -4,8 +4,141 @@ def part_one(input, w = 70, h = 70, run_corruptions = 1024)
44 sim . run!
55 end
66
7- def part_two ( input )
8- 0
7+ def part_two ( input , w = 70 , h = 70 , run_corruptions = 1024 )
8+ sim = DayTwo . new ( input , w , h , run_corruptions )
9+ sim . sim!
10+ end
11+
12+ class DayTwo
13+ #Shamelessly stolen from day 16
14+ DIRECTIONS = [
15+ [ 0 , -1 ] , # North
16+ [ 1 , 0 ] , # East
17+ [ 0 , 1 ] , # South
18+ [ -1 , 0 ] # West
19+ ]
20+
21+ TURN_COST = 1
22+ MOVE_COST = 1
23+
24+ def initialize ( input , w = 70 , h = 70 , run_corruptions = 1024 )
25+ @rows = h + 1
26+ @cols = w + 1
27+ @run_corruptions = run_corruptions
28+ @grid = Array . new ( @rows ) { Array . new ( @cols , '.' ) }
29+
30+ @corruptions = input . split ( "\n " ) . map { |l | l . split ( ',' ) . map ( &:to_i ) }
31+ end
32+
33+ def rain_corruption! ( idx )
34+ @corruptions [ 0 ..idx ] . each do |corruption |
35+ set_cell ( corruption , '#' )
36+ end
37+ end
38+
39+ def set_cell ( xy , ch )
40+ x , y = xy
41+ @grid [ y ] [ x ] = ch
42+ end
43+
44+ def to_s
45+ @grid . map { |r | r . join ( '' ) }
46+ end
47+
48+ def sim!
49+ r = ( 0 ...@corruptions . size )
50+
51+ result = r . bsearch do |corruption_index |
52+ puts "BSearching #{ corruption_index } - #{ @corruptions [ corruption_index ] . join ( ',' ) } "
53+ @grid = Array . new ( @rows ) { Array . new ( @cols , '.' ) }
54+ rain_corruption! ( corruption_index )
55+ run! == Float ::INFINITY
56+ end
57+
58+ @corruptions [ result ] . join ( "," )
59+ end
60+
61+ def run!
62+ # Run, Dijkstra, Run!
63+ start_xy = [ 0 , 0 ]
64+ end_xy = [ @cols - 1 , @rows - 1 ]
65+
66+ direction = 1 # East
67+
68+ # 3D array to keep track of distances from each cell
69+ distances = Array . new ( @cols ) { Array . new ( @rows ) { Array . new ( 4 , Float ::INFINITY ) } }
70+
71+ # Start cell
72+ distances [ start_xy [ 0 ] ] [ start_xy [ 1 ] ] [ direction ] = 0
73+
74+ # Priority queue for [cost, x, y, direction]
75+ p_queue = [ ]
76+ p_queue << [ 0 , start_xy [ 0 ] , start_xy [ 1 ] , direction ]
77+
78+ while !p_queue . empty?
79+ # Sort priority queueu by cost
80+ p_queue . sort_by! { |a | a [ 0 ] }
81+
82+ # Get next items from queue
83+ cost , x , y , direction = p_queue . shift
84+
85+ # Reached end
86+ return cost if x == end_xy [ 0 ] && y == end_xy [ 1 ]
87+
88+ # Skip if we already have a lower cost to this cell
89+ next if distances [ x ] [ y ] [ direction ] < cost
90+
91+ # Try moving forward
92+ new_x = x + DIRECTIONS [ direction ] [ 0 ]
93+ new_y = y + DIRECTIONS [ direction ] [ 1 ]
94+
95+ if can_move? ( new_x , new_y )
96+ new_cost = cost + MOVE_COST
97+ if new_cost < distances [ new_x ] [ new_y ] [ direction ]
98+ distances [ new_x ] [ new_y ] [ direction ] = new_cost
99+ p_queue << [ new_cost , new_x , new_y , direction ]
100+ end
101+ end
102+
103+ # Try turning
104+ [
105+ direction ,
106+ ( direction + 1 ) % 4 ,
107+ ( direction + 2 ) % 4 ,
108+ ( direction + 3 ) % 4
109+ ] . each do |new_dir |
110+ new_dir = ( direction - 1 ) % 4
111+ new_cost = cost # + TURN_COST
112+
113+ if new_cost < distances [ x ] [ y ] [ new_dir ]
114+ distances [ x ] [ y ] [ new_dir ] = new_cost
115+ p_queue << [ new_cost , x , y , new_dir ]
116+ end
117+ end
118+
119+ end
120+
121+ # Can't move forward...
122+ return Float ::INFINITY
123+ end
124+
125+ def can_move? ( x , y )
126+ return false if x < 0 || x >= @cols
127+ return false if y < 0 || y >= @rows
128+ return false if @grid [ y ] [ x ] == '#'
129+ true
130+ end
131+
132+
133+ def find_cell ( ch )
134+ @grid . each_with_index do |row , y |
135+ x = row . index ( ch )
136+ if x
137+ return [ x , y ]
138+ end
139+ end
140+ nil
141+ end
9142 end
10143
11144 class DayOne
@@ -34,7 +167,6 @@ def rain_corruption!
34167 x , y = corruption
35168 @grid [ y ] [ x ] = '#'
36169 end
37- puts to_s
38170 end
39171
40172 def to_s
0 commit comments