1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
require "strscan"

ss = StringScanner.new(ARGF.read)

rules = {}
mine = []
nearby = []

while ss.scan(/([\w\s]+): (\d+)-(\d+) or (\d+)-(\d+)\n/)
  c = ss.captures
  field = c.shift
  rules[field] = c.map(&:to_i).each_slice(2).map {|a,b| a..b }
end
ss.scan(/\n/)

ss.scan(/your ticket:\n/)
mine = ss.scan(/(?~\n)\n/).scan(/\d+/).map(&:to_i)

ss.scan(/\nnearby tickets:\n/)
nearby = ss.rest.split("\n").map {|line| line.split(?,).map(&:to_i) }

# part one
# p nearby.flat_map {|fields|
#   fields.reject {|field|
#     rules.values.any? {|values| values.any? {|range| range.cover?(field) }}
#   }
# }.sum

nearby.select! {|fields|
  fields.all? {|field|
    rules.values.any? {|values| values.any? {|range| range.cover?(field) }}
  }
}

valid = (0...mine.size).map {|i|
  rules.select {|name, ranges|
    nearby.map {|n| n[i] }.all? {|n| ranges.any? {|range| range.cover?(n) }}
  }
}.map.with_index {|v,i| [i, v.keys] }.to_h

order = {}
until valid.values.all?(&:empty?)
  k, f = valid.find {|_,v| v.size == 1 }
  order[k] = f[0]
  valid.transform_values! {|v| v - f }
end

p order
  .select {|_,v| v.start_with?("departure") }
  .map(&:first)
  .map {|i| mine[i] }
  .inject(&:*)