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