[2022][ruby][15.x]
change yvpvxvzpssslnpmwpvtwppuzzqrxrqym
commit 9d2be65175008b9212c72ef244295655e7646e95
author Alpha Chen <alpha@kejadlen.dev>
date
parent xnttkksl
diff --git a/2022/ruby/day_15.rb b/2022/ruby/day_15.rb
new file mode 100644
index 0000000..23089ea
--- /dev/null
+++ b/2022/ruby/day_15.rb
@@ -0,0 +1,87 @@
+require "set"
+
+sensors_and_beacons = ARGF.read
+  .scan(/Sensor at x=(-?\d+), y=(-?\d+): closest beacon is at x=(-?\d+), y=(-?\d+)/)
+  .map { _1.map(&:to_i) }
+  .flatten
+  .each_slice(2)
+
+# part one
+row = 2_000_000
+# row = 10
+
+no_beacons = []
+sensors_and_beacons.each_slice(2) do |sensor, beacon|
+  dist = sensor.zip(beacon).sum { (_1 - _2).abs }
+
+  dy = (row - sensor[1]).abs
+  next if dy > dist
+  dx = dist - dy
+
+  x_min, x_max = [sensor[0]-dx, sensor[0]+dx].sort
+
+  no_beacons << (x_min..x_max)
+end
+
+# remove covered ranges
+no_beacons = no_beacons.reject {|x| (no_beacons - [x]).any? { _1.cover?(x) }}
+
+# merge ranges
+no_beacons = no_beacons.inject([no_beacons.shift]) {|no_beacons, range|
+  next no_beacons if no_beacons.any? { _1.cover?(range) }
+
+  if overlap = no_beacons.find { _1.cover?(range.begin) }
+    range = (overlap.end + 1..range.end)
+  end
+
+  if overlap = no_beacons.find { _1.cover?(range.end) }
+    range = (range.begin..overlap.begin-1)
+  end
+
+  no_beacons << range
+}
+
+p no_beacons.sum(&:size) - sensors_and_beacons.to_a.select { _2 == row }.uniq.size
+
+# part two
+find_sab = ->(x, y) {
+  sensors_and_beacons.each_slice(2).find {|sensor, beacon|
+    dist = sensor.zip(beacon).sum { (_1 - _2).abs }
+    dist >= sensor.zip([x, y]).sum { (_1 - _2).abs }
+  }
+}
+
+max = 4_000_000
+# max = 20
+x = 0
+y = 0
+loop do
+  p y if (y % 10_000).zero?
+
+  sensor, _ = find_sab.(x, y)
+  dx = sensor[0] - x
+  if dx >= max / 2
+    dy = sensor[1] - y
+    y += dy.positive? ? 2 * dy + 1 : 1
+  end
+
+  loop do
+    sensor, beacon = find_sab.(x, y)
+    if sensor.nil?
+      p 4_000_000 * x + y
+      exit
+    end
+
+    dist = sensor.zip(beacon).sum { (_1 - _2).abs }
+    dy = (sensor[1] - y).abs
+    dx = (dist - dy).abs
+    x = sensor[0] + dx + 1
+
+    if x > max
+      x = 0
+      break
+    end
+  end
+  y += 1
+  break if y > max
+end