2019: Day 14 Part 2
This commit is contained in:
parent
f58d42e04a
commit
e5c99b6217
1 changed files with 130 additions and 0 deletions
130
2019/day14/part2.rb
Normal file
130
2019/day14/part2.rb
Normal file
|
|
@ -0,0 +1,130 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
Chemical = Struct.new(:amount, :name) do
|
||||
def pretty_print(pp)
|
||||
pp.text to_s
|
||||
end
|
||||
|
||||
def to_s
|
||||
"#{amount} #{name}"
|
||||
end
|
||||
end
|
||||
|
||||
Formula = Struct.new(:product, :reactants) do
|
||||
def produces?(name)
|
||||
product.name == name
|
||||
end
|
||||
|
||||
def pretty_print(pp)
|
||||
pp.text to_s
|
||||
end
|
||||
|
||||
def to_s
|
||||
"#{product} <= #{reactants.join(', ')}"
|
||||
end
|
||||
end
|
||||
|
||||
def parse_chemical(text)
|
||||
parts = text.split(' ')
|
||||
amount = parts[0].to_i
|
||||
name = parts[1].strip
|
||||
Chemical.new(amount, name)
|
||||
end
|
||||
|
||||
def parse(input)
|
||||
input.lines.map do |line|
|
||||
left, right = line.split('=>')
|
||||
|
||||
product = parse_chemical(right)
|
||||
reactants = left.split(',').map { |text| parse_chemical(text) }
|
||||
Formula.new(product, reactants)
|
||||
end
|
||||
end
|
||||
|
||||
def ore_needed(formulas, fuel)
|
||||
have = Hash.new { |hash, key| hash[key] = 0 }
|
||||
need = Hash.new { |hash, key| hash[key] = 0 }
|
||||
|
||||
have = Hash.new { |hash, key| hash[key] = 0 }
|
||||
need = Hash.new { |hash, key| hash[key] = 0 }
|
||||
need['FUEL'] = fuel
|
||||
ore = 0
|
||||
|
||||
until need.empty?
|
||||
product, amount = need.shift
|
||||
next if amount.zero?
|
||||
|
||||
formula = formulas.find { |f| f.produces? product }
|
||||
|
||||
needed = (amount - have[product])
|
||||
|
||||
runs = (needed.fdiv formula.product.amount).ceil
|
||||
|
||||
formula.reactants.each do |r|
|
||||
wanted = runs * r.amount
|
||||
already_have = have[r.name]
|
||||
can_use = [already_have, wanted].min
|
||||
more = wanted - can_use
|
||||
|
||||
have[r.name] -= can_use
|
||||
if r.name == 'ORE'
|
||||
ore += more
|
||||
else
|
||||
need[r.name] += more
|
||||
end
|
||||
end
|
||||
|
||||
yielded = runs * formula.product.amount
|
||||
extra = yielded - needed
|
||||
have[product] += extra
|
||||
end
|
||||
|
||||
ore
|
||||
end
|
||||
|
||||
class OreCalculator
|
||||
def initialize(formulas)
|
||||
@formulas = formulas
|
||||
@memo = {}
|
||||
end
|
||||
|
||||
def calc(fuel)
|
||||
return @memo[fuel] if @memo.key?(fuel)
|
||||
|
||||
ore = ore_needed(@formulas, fuel)
|
||||
@memo[fuel] = ore
|
||||
ore
|
||||
end
|
||||
end
|
||||
|
||||
def binary_search(f, lo, hi, target)
|
||||
until (f.call(hi) - f.call(lo)).zero? || hi - lo < 2
|
||||
mid = (hi + lo) / 2
|
||||
case f.call(mid) <=> target
|
||||
when -1
|
||||
lo = mid
|
||||
when 0
|
||||
break
|
||||
when +1
|
||||
hi = mid
|
||||
end
|
||||
end
|
||||
|
||||
lo
|
||||
end
|
||||
|
||||
input = File.read(File.join(__dir__, 'input'))
|
||||
formulas = parse(input)
|
||||
|
||||
available_ore = 1_000_000_000_000
|
||||
|
||||
upper_threshold = 0
|
||||
upper_fuel = 1
|
||||
until upper_threshold > available_ore
|
||||
upper_threshold = ore_needed(formulas, upper_fuel)
|
||||
upper_fuel *= 2
|
||||
end
|
||||
|
||||
oc = OreCalculator.new(formulas)
|
||||
fuel_made = binary_search(oc.method(:calc), 0, upper_fuel, available_ore)
|
||||
puts fuel_made
|
||||
Loading…
Reference in a new issue