Skip to content

Commit 9c69a4b

Browse files
committed
Day 17 part 2 Ruby solution
1 parent fa8baee commit 9c69a4b

File tree

1 file changed

+254
-0
lines changed

1 file changed

+254
-0
lines changed

17-2.rb

+254
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,254 @@
1+
#!/usr/bin/env ruby
2+
3+
require 'numo/narray'
4+
5+
class Rock
6+
@@shapes = [
7+
[[1, 1, 1, 1]],
8+
[[0, 1, 0],
9+
[1, 1, 1],
10+
[0, 1, 0]],
11+
[[0, 0, 1],
12+
[0, 0, 1],
13+
[1, 1, 1]],
14+
[[1],
15+
[1],
16+
[1],
17+
[1]],
18+
[[1, 1],
19+
[1, 1]]
20+
]
21+
22+
def self.shapes
23+
@@shapes
24+
end
25+
26+
def [](row, col)
27+
@rock[row][col]
28+
end
29+
30+
def width
31+
@rock[0].size
32+
end
33+
34+
def height
35+
@rock.size
36+
end
37+
38+
attr_accessor :x, :y
39+
40+
def initialize(shape)
41+
@rock = @@shapes[shape]
42+
@x = 2
43+
@y = 0
44+
end
45+
end
46+
47+
class Cave
48+
def initialize(jets)
49+
@num_rocks = 0
50+
@height = 25
51+
@map = Numo::UInt8.zeros(@height, 7)
52+
@next_shape = -1
53+
@jets = jets.chars.map { |j| j == '<' ? -1 : +1 }
54+
@jet_offset = -1
55+
@rock = nil
56+
@highest_rock = @height
57+
end
58+
59+
attr_reader :num_rocks, :highest_rock
60+
61+
def find_repetition
62+
base = @jets.size
63+
1.upto(@jets.size * Rock.shapes.size) do |window_size|
64+
maybe_repeats = true
65+
window_size.times do |row|
66+
7.times do |col|
67+
if @map[@height - base - window_size * 0 - row, col] !=
68+
@map[@height - base - window_size * 1 - row, col] or
69+
@map[@height - base - window_size * 1 - row, col] !=
70+
@map[@height - base - window_size * 2 - row, col]
71+
maybe_repeats = false
72+
break
73+
end
74+
end
75+
break unless maybe_repeats
76+
end
77+
return [base, window_size] if maybe_repeats
78+
end
79+
end
80+
81+
def rock_height
82+
@height - @highest_rock
83+
end
84+
85+
def step!
86+
unless @rock
87+
@next_shape = (@next_shape + 1) % Rock.shapes.size
88+
@rock = Rock.new(@next_shape)
89+
@rock.y = @highest_rock - 3 - @rock.height
90+
expand! if @rock.y.negative?
91+
end
92+
push!
93+
@rock = nil unless fall!
94+
end
95+
96+
def push!
97+
@jet_offset = (@jet_offset + 1) % @jets.size
98+
dir = @jets[@jet_offset]
99+
can_move = true
100+
if @rock.x + dir < 0 or
101+
@rock.width + @rock.x + dir > 7
102+
can_move = false
103+
else
104+
@rock.height.times do |row|
105+
@rock.width.times do |col|
106+
next unless @rock[row, col] == 1
107+
108+
if @map[row + @rock.y, col + @rock.x + dir] != 0
109+
can_move = false
110+
break
111+
end
112+
end
113+
end
114+
end
115+
@rock.x += dir if can_move
116+
end
117+
118+
def fall!
119+
can_fall = true
120+
if @rock.height + @rock.y >= @height
121+
can_fall = false
122+
else
123+
@rock.height.times do |row|
124+
@rock.width.times do |col|
125+
next unless @rock[row, col] == 1
126+
127+
if @map[row + @rock.y + 1, col + @rock.x] != 0
128+
can_fall = false
129+
break
130+
end
131+
end
132+
end
133+
end
134+
if can_fall
135+
@rock.y += 1
136+
else
137+
@rock.height.times do |row|
138+
@rock.width.times do |col|
139+
@map[row + @rock.y, col + @rock.x] = @rock[row, col]
140+
end
141+
end
142+
@highest_rock = [@rock.y, @highest_rock].min
143+
@num_rocks += 1
144+
end
145+
can_fall
146+
end
147+
148+
def expand!
149+
add = 100
150+
z = Numo::UInt8.zeros(add, 7)
151+
@map = z.append(@map, axis:0)
152+
@highest_rock += add
153+
@rock.y += add if @rock
154+
@height += add
155+
end
156+
157+
def to_s
158+
s = "<#{self.class}:\n"
159+
s += "Next shape: #{@next_shape}, next push: #{@jets[@jet_offset]}\n"
160+
s += "Highest rock: #{@highest_rock}\n"
161+
@height.times do |row|
162+
s += row.to_s.rjust(Math::log10(@height).ceil, '0') + ' '
163+
7.times do |col|
164+
tile = 0
165+
if @rock and
166+
row.between?(@rock.y, @rock.y + @rock.height - 1) and
167+
col.between?(@rock.x, @rock.x + @rock.width - 1) and
168+
@rock[row - @rock.y, col - @rock.x] == 1
169+
tile = 2
170+
end
171+
tile += @map[row, col]
172+
s += case tile
173+
when 0
174+
'.'
175+
when 1
176+
'#'
177+
when 2
178+
'@'
179+
else
180+
'!'
181+
end
182+
end
183+
s += "\n"
184+
end
185+
if @rock
186+
s += "Rock: x: #{@rock.x}, y: #{@rock.y}\n"
187+
@rock.height.times do |row|
188+
@rock.width.times do |col|
189+
s += @rock[row, col].zero? ? '.' : '@'
190+
end
191+
s += "\n"
192+
end
193+
s += '>'
194+
end
195+
s
196+
end
197+
198+
alias inspect to_s
199+
end
200+
201+
input = File.read('17.input').strip
202+
203+
# Caves will be filled as:
204+
#
205+
# Remaining
206+
# Period
207+
# Period
208+
# ...
209+
# Period
210+
# Period
211+
# Base
212+
213+
# "cave" will be base + 3 periods, to find the repetition
214+
cave = Cave.new(input)
215+
216+
until cave.rock_height >= input.size + input.size * Rock.shapes.size * 3 do
217+
cave.step!
218+
end
219+
220+
base, period_height = cave.find_repetition
221+
222+
# "cave2" will be base + 1 period, to count the number of rocks in one
223+
# period
224+
cave2 = Cave.new(input)
225+
226+
until cave2.rock_height >= base do
227+
cave2.step!
228+
end
229+
230+
base_rocks = cave2.num_rocks
231+
232+
until cave2.rock_height >= base + period_height do
233+
cave2.step!
234+
end
235+
236+
period_rocks = cave2.num_rocks - base_rocks
237+
238+
final_rock = 1_000_000_000_000
239+
periods = (final_rock - base_rocks) / period_rocks
240+
remaining_rocks = final_rock - base_rocks - periods * period_rocks
241+
242+
remaining_rocks.times do
243+
cave2.step!
244+
end
245+
246+
# "cave3" will be base + remaining, to count the rocks outside of any
247+
# periods
248+
cave3 = Cave.new(input)
249+
250+
until cave3.num_rocks == base_rocks + remaining_rocks do
251+
cave3.step!
252+
end
253+
254+
print periods * period_height + cave3.rock_height, "\n"

0 commit comments

Comments
 (0)