[2021][ruby][16.x]
change nzkpqrplzkkkyvluvwysqzynnwrvzvzw
commit c3927efa65c53ec6dbe77bf554da86460bafb048
author Alpha Chen <alpha@kejadlen.dev>
date
parent txupmnkl
diff --git a/2021/ruby/day_16.rb b/2021/ruby/day_16.rb
new file mode 100644
index 0000000..b15b10e
--- /dev/null
+++ b/2021/ruby/day_16.rb
@@ -0,0 +1,68 @@
+require "strscan"
+
+def parse(ss)
+  version = ss.scan(/.{3}/).to_i(2)
+  type_id = ss.scan(/.{3}/).to_i(2)
+
+  content = case type_id
+            when 4 # literal value
+              value = ""
+              while group = ss.scan(/1/)
+                value << ss.scan(/.{4}/)
+              end
+              ss.scan(/0/)
+              value << ss.scan(/.{4}/)
+              value.to_i(2)
+            else # operator
+              length_type_id = ss.scan(/./)
+              case length_type_id
+              when ?0
+                sub_packet_length = ss.scan(/.{15}/).to_i(2)
+                sub_packet = ss.scan(/.{#{sub_packet_length}}/)
+
+                sss = StringScanner.new(sub_packet)
+                sub_packets = []
+                until sss.eos?
+                  sub_packets << parse(sss)
+                end
+                sub_packets
+              when ?1
+                sub_packet_num = ss.scan(/.{11}/).to_i(2)
+                sub_packet_num.times.map { parse(ss) }
+              else
+                fail
+              end
+            end
+
+  { version: version, type_id: type_id, content: content }
+end
+
+message = ARGF.read.chomp.chars.map { _1.to_i(16).to_s(2).rjust(4, ?0) }.join
+ss = StringScanner.new(message)
+packet = parse(ss)
+
+def each(packet, &block)
+  return enum_for(__method__, packet) unless block_given?
+
+  block.(packet)
+  if (content = packet.fetch(:content)).is_a?(Array)
+    content.each do |sub_packet|
+      each(sub_packet, &block)
+    end
+  end
+end
+
+# p each(packet).sum { _1.fetch(:version) }
+
+value = {
+  0 => ->(c) { c.map { value.fetch(_1.fetch(:type_id)).(_1.fetch(:content)) }.sum },
+  1 => ->(c) { c.map { value.fetch(_1.fetch(:type_id)).(_1.fetch(:content)) }.inject(:*) },
+  2 => ->(c) { c.map { value.fetch(_1.fetch(:type_id)).(_1.fetch(:content)) }.min },
+  3 => ->(c) { c.map { value.fetch(_1.fetch(:type_id)).(_1.fetch(:content)) }.max },
+  4 => ->(c) { c },
+  5 => ->(c) { c.map { value.fetch(_1.fetch(:type_id)).(_1.fetch(:content)) }.inject(:>) ? 1 : 0 },
+  6 => ->(c) { c.map { value.fetch(_1.fetch(:type_id)).(_1.fetch(:content)) }.inject(:<) ? 1 : 0 },
+  7 => ->(c) { c.map { value.fetch(_1.fetch(:type_id)).(_1.fetch(:content)) }.inject(:==) ? 1 : 0 },
+}
+
+p value.fetch(packet.fetch(:type_id)).(packet.fetch(:content))