-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathday15.rb
149 lines (123 loc) · 4.69 KB
/
day15.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
# frozen_string_literal: true
# https://adventofcode.com/2021/day/15
class Chitons
attr_accessor :risks, :i_length, :j_length
def initialize(input_data_filename, expand: false)
@risks = File.readlines(input_data_filename).map(&:chomp).map(&:chars).map { |r| r.map(&:to_i) }
@risks = expand_risks if expand
@i_length = @risks.length
@j_length = @risks[0].length
end
def print_risks
@risks.each { |r| puts r.map(&:to_i).join }
end
def expand_risks
expanded = @risks.clone
(0...5).each do |i|
(0...5).each do |j|
next if i.zero? && j.zero?
@risks.length.times { expanded << [] } if j.zero?
incremented = increment(previous_block(i, j, expanded))
append_incremented(expanded, incremented)
end
end
expanded
end
def append_incremented(expanded, incremented)
(0...incremented.length).each do |ii|
expanded[(i * @risks.length) + ii] = expanded[(i * @risks.length) + ii] + incremented[ii]
end
end
def previous_block(i_idx, j_idx, expanded)
return previous_block_up(i_idx, expanded) if j_idx.zero?
previous_block_left(i_idx, j_idx, expanded)
end
def previous_block_up(i_idx, expanded)
sub_matrix(expanded, (i_idx - 1) * @risks.length, @risks.length, 0, @risks[0].length)
end
def previous_block_left(i_idx, j_idx, expanded)
sub_matrix(expanded, i_idx * @risks.length, @risks.length, (j_idx - 1) * @risks[0].length, @risks[0].length)
end
def sub_matrix(matrix, i_idx, i_length, j_idx, j_length)
matrix[i_idx, i_length].map { |r| r[j_idx, j_length] }
end
def increment(risk_map)
incremented = []
(0...risk_map.length).each do |i|
(0...risk_map[0].length).each do |j|
incremented << [] if j.zero?
incremented[i] << (risk_map[i][j] % 9) + 1
end
end
incremented
end
# Part 1
# Good explanation: https://www.geeksforgeeks.org/dijkstras-shortest-path-algorithm-greedy-algo-7/
def dijkstra
points = init_data_for_dijkstra
until all_done?(points)
start = min_risk(points)
start[:calculated] = true
revise_risk_for_adjacents(start, points)
end
points[@i_length - 1][@j_length - 1][:risk_from_start].to_i
end
def revise_risk_for_adjacents(start, points)
adjacents(start, points).reject { |p| p[:calculated] }.each do |p|
revised_risk_from_start = start[:risk_from_start] + p[:risk].to_f
p[:risk_from_start] = revised_risk_from_start if revised_risk_from_start < p[:risk_from_start]
end
end
def init_data_for_dijkstra
points = []
(0...@i_length).each do |i|
(0...@j_length).each do |j|
points << [] if j.zero?
points[i] << { position: [i, j], risk: @risks[i][j], risk_from_start: Float::INFINITY, calculated: false }
end
end
points[0][0][:risk_from_start] = 0.0
points
end
def all_done?(points)
points.flatten(1).reduce(true) { |done, p| done && p[:calculated] }
end
def min_risk(points)
points.flatten(1).reject { |p| p[:calculated] }.min { |a, b| a[:risk_from_start] <=> b[:risk_from_start] }
end
def adjacents(point, points)
adjacent_points = []
adjacent_up(adjacent_points, point, points)
adjacent_right(adjacent_points, point, points)
adjacent_down(adjacent_points, point, points)
adjacent_left(adjacent_points, point, points)
adjacent_points
end
def adjacent_up(adjacent_points, point, points)
adjacent_points << points[point[:position][0] - 1][point[:position][1]] unless point[:position][0].zero?
end
def adjacent_right(adjacent_points, point, points)
adjacent_points << points[point[:position][0]][point[:position][1] + 1] unless point[:position][1] + 1 == @j_length
end
def adjacent_down(adjacent_points, point, points)
adjacent_points << points[point[:position][0] + 1][point[:position][1]] unless point[:position][0] + 1 == @i_length
end
def adjacent_left(adjacent_points, point, points)
adjacent_points << points[point[:position][0]][point[:position][1] - 1] unless point[:position][1].zero?
end
end
def path(filename, expand)
data = Chitons.new(filename, expand: expand)
msg = expand ? ' expanded' : ''
puts "The lowest risk path has #{data.dijkstra} risk (#{filename}#{msg})"
end
[false, true].each { |e| ['day15-input-test.txt', 'day15-input-01.txt'].each { |f| path(f, e) } }
# data = Chitons.new('../inputs/2021/day15-input-test.txt', expand: true)
# data.print_risks
# risks = File.readlines('../inputs/2021/day15-expanded-test.txt').map(&:chomp).map(&:chars).map { |r| r.map(&:to_i) }
# (0...risks.length).each do |i|
# (0...risks[0].length).each do |j|
# puts "(#{i}, #{j}) == (#{data.risks[i][j]}, #{risks[i][j]})"
# return if data.risks[i][j] != risks[i][j]
# end
# end