Skip to content

Commit 67d3fa7

Browse files
committed
Day 19 Ruby solutions
1 parent fd6a863 commit 67d3fa7

File tree

2 files changed

+462
-0
lines changed

2 files changed

+462
-0
lines changed

19-1.rb

Lines changed: 225 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,225 @@
1+
#!/usr/bin/env ruby
2+
3+
require 'matrix'
4+
5+
class Scanner
6+
attr_accessor :beacons
7+
attr_accessor :distances
8+
attr_accessor :id
9+
10+
def initialize(id)
11+
@id = id
12+
@beacons = []
13+
@distances = []
14+
end
15+
16+
def add_beacon(x, y, z)
17+
@beacons.append Matrix[[x], [y], [z]]
18+
end
19+
20+
def calculate_distances!
21+
@beacons.permutation(2) do |b1, b2|
22+
@distances.append (b1[0, 0] - b2[0, 0]).abs**2 +
23+
(b1[1, 0] - b2[1, 0]).abs**2 +
24+
(b1[2, 0] - b2[2, 0]).abs**2
25+
end
26+
@distances.sort!
27+
end
28+
29+
def match_distances(other)
30+
matching = 0
31+
@distances.each { |d| matching += 1 if other.distances.bsearch { |e| d <=> e } }
32+
matching
33+
end
34+
35+
def match_distances?(other)
36+
match_distances(other) >= 66 # 66 == 12 choose 2
37+
end
38+
39+
def all_rotations
40+
# https://stackoverflow.com/a/51836928
41+
as = [[[1, 0, 0],
42+
[0, 1, 0],
43+
[0, 0, 1]],
44+
45+
[[0, 1, 0],
46+
[0, 0, 1],
47+
[1, 0, 0]],
48+
49+
[[0, 0, 1],
50+
[1, 0, 0],
51+
[0, 1, 0]]].map { |m| Matrix[*m] }
52+
53+
bs = [[[1, 0, 0],
54+
[0, 1, 0],
55+
[0, 0, 1]],
56+
57+
[[-1, 0, 0],
58+
[ 0,-1, 0],
59+
[ 0, 0, 1]],
60+
61+
[[-1, 0, 0],
62+
[ 0, 1, 0],
63+
[ 0, 0,-1]],
64+
65+
[[ 1, 0, 0],
66+
[ 0,-1, 0],
67+
[ 0, 0,-1]]].map { |m| Matrix[*m] }
68+
69+
cs = [[[1, 0, 0],
70+
[0, 1, 0],
71+
[0, 0, 1]],
72+
73+
[[ 0, 0,-1],
74+
[ 0,-1, 0],
75+
[-1, 0, 0]]].map { |m| Matrix[*m] }
76+
77+
rotations = []
78+
as.each do |a|
79+
bs.each do |b|
80+
cs.each do |c|
81+
rotations.append a * b * c
82+
end
83+
end
84+
end
85+
rotations
86+
end
87+
88+
def match_coords(other)
89+
all_rotations.each do |rotation|
90+
rotated = other.beacons.map { |coords| rotation * coords }
91+
92+
rotated.each do |r|
93+
@beacons.each do |b|
94+
translation = r - b
95+
translated = 0
96+
to_translate = @beacons.dup
97+
until to_translate.empty?
98+
coords = to_translate.shift
99+
if rotated.any? (coords + translation)
100+
translated += 1
101+
end
102+
end
103+
return [true, rotation, translation] if translated >= 12
104+
end
105+
end
106+
end
107+
[false, nil, nil]
108+
end
109+
110+
def transform!(rotation, translation)
111+
@beacons = transformed_coords(rotation, translation)
112+
end
113+
114+
def transformed_coords(rotation, translation)
115+
@beacons.map do |b|
116+
rotation * b - translation
117+
end
118+
end
119+
120+
def to_s
121+
s = "<#{self.class}: #{@id}\n"
122+
@beacons.each do |b|
123+
s += "(#{b[0, 0]}, #{b[1, 0]}, #{b[2, 0]})\n"
124+
end
125+
s += "\n#{@distances}\n>"
126+
s
127+
end
128+
129+
def inspect
130+
to_s
131+
end
132+
end
133+
134+
class Map
135+
attr_accessor :map
136+
137+
def initialize
138+
@map = {}
139+
end
140+
141+
def mark(beacon)
142+
self[beacon[0, 0],
143+
beacon[1, 0],
144+
beacon[2, 0]] = 1
145+
end
146+
147+
def [](x, y, z)
148+
@map[[x, y, z]]
149+
end
150+
151+
def []=(x, y, z, val)
152+
@map[[x, y, z]] = val
153+
end
154+
155+
def score
156+
@map.values.size
157+
end
158+
159+
def to_s
160+
s = "<#{self.class}:\n"
161+
@map.each_key do |m|
162+
s += "(#{m[0]}, #{m[1]}, #{m[2]})\n"
163+
end
164+
s += "\nScore: #{score}\n>"
165+
s
166+
end
167+
168+
def inspect
169+
to_s
170+
end
171+
end
172+
173+
header_format = /^--- scanner (?<id>[[:digit:]]+) ---$/
174+
beacon_format = /^(?<x>[[:digit:]\-]+),(?<y>[[:digit:]\-]+),(?<z>[[:digit:]\-]+)$/
175+
176+
input = File.read('19.input').lines.map(&:strip)
177+
178+
scanners = []
179+
180+
until input.empty?
181+
h = header_format.match input.shift
182+
id = h['id'].to_i
183+
s = Scanner.new(id)
184+
185+
loop do
186+
l = input.shift
187+
break if not l or l.empty?
188+
189+
b = beacon_format.match l
190+
s.add_beacon(b['x'].to_i, b['y'].to_i, b['z'].to_i)
191+
end
192+
s.calculate_distances!
193+
scanners.append s
194+
end
195+
196+
map = Map.new
197+
scanners[0].beacons.each { |b| map.mark b }
198+
199+
checked = []
200+
locked = [scanners[0]]
201+
to_check = scanners[1..]
202+
203+
until to_check.empty?
204+
found = []
205+
locked.each do |l|
206+
to_check.each do |t|
207+
next if l.id == t.id
208+
next if checked.any? [l.id, t.id] or checked.any? [t.id, l.id]
209+
210+
checked.append [l.id, t.id]
211+
if l.match_distances? t
212+
res, rotation, translation = l.match_coords t
213+
if res
214+
found.append t
215+
t.transform!(rotation, translation)
216+
t.beacons.each { |c| map.mark c }
217+
end
218+
end
219+
end
220+
end
221+
locked += found
222+
to_check -= found
223+
end
224+
225+
print map.score, "\n"

0 commit comments

Comments
 (0)