[2023][ruby][16.2] shameless green
change mzpuukwtypmrzorlxnomovwtktvmyvtr
commit 4a24fb8f24b7b7da1c3abccd157b188a50a8186b
author Alpha Chen <alpha@kejadlen.dev>
date
parent ulxxntpn
diff --git a/2023/ruby/day_16.rb b/2023/ruby/day_16.rb
index e5bb867..1d7cb7b 100644
--- a/2023/ruby/day_16.rb
+++ b/2023/ruby/day_16.rb
@@ -3,45 +3,59 @@ input = ARGF.readlines(chomp: true)
     row.chars.map.with_index {|elem, x| [[y,x], elem] }
   }.to_h
 
-current = [[[0,0], [0,1]]]
-seen = Set.new
-
-until current.empty?
-  coord, delta = current.shift
-  seen << [coord, delta]
-
-  case [elem = input[coord], delta]
-  in [nil, _]
-    # the beam has escaped the contraption
-  in [?., _] | [?|, [_,0]] | [?-, [0,_]] # keep going
-    current << [coord.zip(delta).map { _1 + _2 }, delta]
-  in [?|, [0,_]] # split up and down
-    current << [coord.zip([-1,0]).map { _1 + _2 }, [-1,0]]
-    current << [coord.zip([1,0]).map { _1 + _2 }, [1,0]]
-  in [?-, [_,0]] # split left and right
-    current << [coord.zip([0,-1]).map { _1 + _2 }, [0,-1]]
-    current << [coord.zip([0,1]).map { _1 + _2 }, [0,1]]
-  in [?/, [0,1]] # going right, goes up
-    current << [coord.zip([-1,0]).map { _1 + _2 }, [-1,0]]
-  in [?/, [0,-1]] # going left, goes down
-    current << [coord.zip([1,0]).map { _1 + _2 }, [1,0]]
-  in [?/, [1,0]] # going down, goes left
-    current << [coord.zip([0,-1]).map { _1 + _2 }, [0,-1]]
-  in [?/, [-1,0]] # going up, goes right
-    current << [coord.zip([0,1]).map { _1 + _2 }, [0,1]]
-  in [?\\, [0,1]] # going right, goes down
-    current << [coord.zip([1,0]).map { _1 + _2 }, [1,0]]
-  in [?\\, [0,-1]] # going left, goes up
-    current << [coord.zip([-1,0]).map { _1 + _2 }, [-1,0]]
-  in [?\\, [1,0]] # going down, goes right
-    current << [coord.zip([0,1]).map { _1 + _2 }, [0,1]]
-  in [?\\, [-1,0]] # going up, goes left
-    current << [coord.zip([0,-1]).map { _1 + _2 }, [0,-1]]
-  else
-    fail "unexpected element: #{elem.inspect}"
+count_energized = ->(start, dir) {
+  current = [[start, dir]]
+  seen = Set.new
+
+  until current.empty?
+    coord, delta = current.shift
+    seen << [coord, delta]
+
+    case [elem = input[coord], delta]
+    in [nil, _]
+      # the beam has escaped the contraption
+    in [?., _] | [?|, [_,0]] | [?-, [0,_]] # keep going
+      current << [coord.zip(delta).map { _1 + _2 }, delta]
+    in [?|, [0,_]] # split up and down
+      current << [coord.zip([-1,0]).map { _1 + _2 }, [-1,0]]
+      current << [coord.zip([1,0]).map { _1 + _2 }, [1,0]]
+    in [?-, [_,0]] # split left and right
+      current << [coord.zip([0,-1]).map { _1 + _2 }, [0,-1]]
+      current << [coord.zip([0,1]).map { _1 + _2 }, [0,1]]
+    in [?/, [0,1]] # going right, goes up
+      current << [coord.zip([-1,0]).map { _1 + _2 }, [-1,0]]
+    in [?/, [0,-1]] # going left, goes down
+      current << [coord.zip([1,0]).map { _1 + _2 }, [1,0]]
+    in [?/, [1,0]] # going down, goes left
+      current << [coord.zip([0,-1]).map { _1 + _2 }, [0,-1]]
+    in [?/, [-1,0]] # going up, goes right
+      current << [coord.zip([0,1]).map { _1 + _2 }, [0,1]]
+    in [?\\, [0,1]] # going right, goes down
+      current << [coord.zip([1,0]).map { _1 + _2 }, [1,0]]
+    in [?\\, [0,-1]] # going left, goes up
+      current << [coord.zip([-1,0]).map { _1 + _2 }, [-1,0]]
+    in [?\\, [1,0]] # going down, goes right
+      current << [coord.zip([0,1]).map { _1 + _2 }, [0,1]]
+    in [?\\, [-1,0]] # going up, goes left
+      current << [coord.zip([0,-1]).map { _1 + _2 }, [0,-1]]
+    else
+      fail "unexpected element: #{elem.inspect}"
+    end
+
+    current = current.reject { seen.include?(_1) }
   end
 
-  current = current.reject { seen.include?(_1) }
-end
+  seen.map(&:first).select { input.has_key?(_1) }.uniq.size
+}
+
+max_y = input.keys.map(&:first).max
+max_x = input.keys.map(&:last).max
+
+candidates = [
+  (0..max_x).map { [[0,_1], [1,0]] },      # down
+  (0..max_x).map { [[max_y,_1], [-1,0]] }, # up
+  (0..max_y).map { [[_1,0], [0,1]] },      # right
+  (0..max_y).map { [[_1,max_x], [0,-1]] }, # left
+].inject(:+)
 
-p seen.map(&:first).select { input.has_key?(_1) }.uniq.size
+p candidates.map { count_energized[*_1] }.max