-
Notifications
You must be signed in to change notification settings - Fork 1
/
03_spiral_matrix.rb
48 lines (43 loc) · 1.18 KB
/
03_spiral_matrix.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
input = Integer(!ARGV.empty? && ARGV.first.match?(/^\d+$/) ? ARGV.first : ARGF.read)
ORIGIN = Complex(0, 0)
def spiral_matrix(corners_only: false)
pos = ORIGIN
direction = 1
length = 1
n = 1
Enumerator.new { |e|
e << [1, pos]
loop {
if corners_only
n += length
pos += direction * length
e << [n, pos]
else
length.times { |i|
n += 1
pos += direction
e << [n, pos]
}
end
direction *= Complex::I
length += 1 if direction.imag == 0
}
}
end
# The corners of the side of the square that contain the input,
# and the input's distance to each:
dists_and_corners = spiral_matrix(corners_only: true).each_cons(2).find { |(_, _), (n, _)|
n >= input
}.map { |n, d| [(input - n).abs, d] }
# The closer corner:
dist, corner = dists_and_corners.min_by(&:first)
# One coordinate the same as the corner, the other decreased by the distance.
puts corner.real.abs + corner.imag.abs - dist
values = {ORIGIN => 1}
puts spiral_matrix.lazy.map { |_, pos|
values[pos] = [-1, 0, 1].sum { |dy|
[-1, 0, 1].sum { |dx|
values[pos + dx + Complex::I * dy] || 0
}
}
}.find { |n| n > input }