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
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
SEEN = []

class Factory
  attr_reader :time, :robots, :resources

  def initialize(
    blueprint,
    time=0,
    robots={ ore: 1, clay: 0, obsidian: 0, geode: 0 },
    resources={ ore: 0, clay: 0, obsidian: 0, geode: 0 }
  )
    @blueprint = blueprint
    @time = time
    @robots = robots
    @resources = resources
  end

  def max_geodes
    if @time == 20
      pp self if @robots.values_at(:ore, :clay, :obsidian, :geode) == [1, 4, 2, 1]
      return @resources.fetch(:geode)
    end

    resources = @resources.merge(@robots) { _2 + _3 }
    branches = @blueprint.filter_map {|robot, cost|
      if cost.all? {|res, qty| @resources.fetch(res) >= qty }
        Factory.new(
          @blueprint,
          @time + 1,
          @robots.merge({robot => 1}) { _2 + _3 },
          resources.merge(cost) { _2 - _3 },
        )
      else
        nil
      end
    }

    if @blueprint.any? {|_, cost| cost.any? {|res, qty| @robots.fetch(res) > 0 && @resources.fetch(res) < qty }}
      pp self
      branches << Factory.new(@blueprint, @time + 1, @robots, resources)
    end

    branches = branches.reject {|factory|
      SEEN.any? {|other|
        %i[ ore clay obsidian geode ].all? {|res|
          factory.robots.fetch(res) <= other.robots.fetch(res) &&
          factory.resources.fetch(res) <= other.resources.fetch(res) &&
          factory.time >= other.time
        }
      }
    }
    SEEN.concat(branches)

    return @resources.fetch(:geode) if branches.empty?

    branches.map(&:max_geodes).max
  end

  def to_s
    "<time: #@time robots: #{@robots.values} resources: #{@resources.values}>"
  end
  alias_method :inspect, :to_s
end

blueprints = ARGF.read
  .split("\n\n").map { _1.lines(chomp: true).join }
  .map { _1.scan(/\d+/).map(&:to_i) }
  .map {
    _1.shift # id
    {
      ore: {ore: _1.shift },
      clay: {ore: _1.shift },
      obsidian: {ore: _1.shift, clay: _1.shift },
      geode: {ore: _1.shift, obsidian: _1.shift },
    }
  }

p Factory.new(blueprints[0]).max_geodes
p SEEN.count

__END__