Skip to content

Instantly share code, notes, and snippets.

@ekscrypto
Last active December 14, 2019 16:30
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ekscrypto/59410e8f4ef0c5043a9c81c3790f788a to your computer and use it in GitHub Desktop.
Save ekscrypto/59410e8f4ef0c5043a9c81c3790f788a to your computer and use it in GitHub Desktop.
//
// OreCalculator.swift
// AdventOfCode2019-Day14
//
// Created by Dave Poirier on 2019-12-14.
// Copyright © 2019 Soft.io. All rights reserved.
//
import Foundation
class OreCalculator {
typealias Material = String
typealias Quantity = Int
struct Item {
let qty: Quantity
let material: Material
init(_ string: String) {
let cleanedUp = string.trimmingCharacters(in: .whitespacesAndNewlines)
let parts = cleanedUp.components(separatedBy: " ")
self.qty = Int(parts.first ?? "") ?? 0
self.material = parts.last ?? ""
}
}
struct Reaction {
let output: Item
let components: [Item]
init(_ string: String) {
let inputAndOutput = string.components(separatedBy: " => ")
let input = inputAndOutput[0]
self.output = Item(inputAndOutput[1])
self.components = input.components(separatedBy: ", ").map({Item($0) })
}
}
let grimoire: [Material: Reaction]
var inventory = [Material: Quantity]()
init(using definitions: String)
{
let reactions: [Reaction] = definitions
.components(separatedBy: "\n")
.map({ Reaction($0)} )
var index = [Material: Reaction]()
reactions.forEach { (reaction) in
index[reaction.output.material] = reaction
}
self.grimoire = index
resetInventory()
}
func resetInventory() {
grimoire.forEach({ (_, reaction) in
inventory[reaction.output.material] = 0
reaction.components.forEach({ (item) in
inventory[item.material] = 0
})
})
}
func neededOre(for quantity: Quantity, of material: Material) -> Int {
if material == "ORE" { return quantity }
let reaction = grimoire[material]!
let stockLeft = inventory[material]! - quantity
if stockLeft >= 0 {
inventory[material] = stockLeft
return 0
}
let needed = -stockLeft
let qtyPerReaction = reaction.output.qty
let neededReactions = (needed + (qtyPerReaction - 1)) / qtyPerReaction
let stockAfterProduction = (neededReactions * qtyPerReaction) - needed
inventory[material] = stockAfterProduction
return reaction.components.reduce(0, { (ore, component) in
let unitsNeeded = neededReactions * component.qty
return ore + neededOre(for: unitsNeeded, of: component.material )
})
}
func maximumProduceable(material: Material, with oreInStock: Int, maxComplexity: Int) -> Int {
var maxProduced = 0
var incrementValue = (2<<maxComplexity)
var unitsOfFuelToAttempt = incrementValue
repeat {
resetInventory()
let oreRequired = neededOre(for: unitsOfFuelToAttempt, of: material)
if oreRequired < oreInStock {
maxProduced = unitsOfFuelToAttempt
} else {
unitsOfFuelToAttempt -= incrementValue
}
incrementValue = incrementValue>>1
unitsOfFuelToAttempt += incrementValue
} while incrementValue != 0
return maxProduced
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment