128 lines
2.9 KiB
Go
128 lines
2.9 KiB
Go
package days
|
|
|
|
import (
|
|
"fmt"
|
|
"math"
|
|
"strconv"
|
|
"strings"
|
|
|
|
u "parnic.com/aoc2019/utilities"
|
|
)
|
|
|
|
type reaction struct {
|
|
inputs map[string]int
|
|
output u.Pair[string, int]
|
|
}
|
|
|
|
type Day14 struct {
|
|
reactions []reaction
|
|
leftovers map[string]int
|
|
}
|
|
|
|
func (d *Day14) Parse() {
|
|
d.leftovers = make(map[string]int)
|
|
|
|
lines := u.GetStringLines("14p")
|
|
d.reactions = make([]reaction, len(lines))
|
|
for i, line := range lines {
|
|
sides := strings.Split(line, " => ")
|
|
inputs := strings.Split(sides[0], ", ")
|
|
output := sides[1]
|
|
|
|
outPair := strings.Split(output, " ")
|
|
outAmt, _ := strconv.Atoi(outPair[0])
|
|
d.reactions[i].output = u.Pair[string, int]{First: outPair[1], Second: outAmt}
|
|
d.reactions[i].inputs = make(map[string]int)
|
|
for _, input := range inputs {
|
|
pair := strings.Split(input, " ")
|
|
d.reactions[i].inputs[pair[1]], _ = strconv.Atoi(pair[0])
|
|
}
|
|
}
|
|
}
|
|
|
|
func (d Day14) getReactionProducing(chem string) *reaction {
|
|
for _, reaction := range d.reactions {
|
|
if reaction.output.First == chem {
|
|
return &reaction
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (d Day14) Num() int {
|
|
return 14
|
|
}
|
|
|
|
func (d *Day14) oreRequiredStock(qty int64) int64 {
|
|
oreRequired := int64(0)
|
|
needs := map[string]int64{
|
|
"FUEL": qty,
|
|
}
|
|
excess := make(map[string]int64)
|
|
|
|
getFromExcess := func(qty int64, chemical string) int64 {
|
|
inStock := excess[chemical]
|
|
qty -= inStock
|
|
excess[chemical] = int64(math.Min(math.Abs(float64(qty)), 0))
|
|
return inStock - excess[chemical]
|
|
}
|
|
|
|
for len(needs) > 0 {
|
|
keys := u.MapKeys(needs)
|
|
chemical := keys[len(keys)-1]
|
|
qtyRequired := needs[chemical]
|
|
delete(needs, chemical)
|
|
|
|
fromExcess := getFromExcess(qtyRequired, chemical)
|
|
qtyRequired -= fromExcess
|
|
|
|
reaction := d.getReactionProducing(chemical)
|
|
qtyProduced := int64(reaction.output.Second)
|
|
ingredients := reaction.inputs
|
|
|
|
n := int64(math.Ceil(float64(qtyRequired) / float64(qtyProduced)))
|
|
|
|
excess[chemical] = (qtyProduced * n) - qtyRequired
|
|
for ingredient, qtyIngredient := range ingredients {
|
|
if ingredient == "ORE" {
|
|
oreRequired += int64(int64(qtyIngredient) * n)
|
|
} else {
|
|
needs[ingredient] += int64(qtyIngredient) * n
|
|
}
|
|
}
|
|
}
|
|
|
|
return oreRequired
|
|
}
|
|
|
|
func (d *Day14) Part1() string {
|
|
neededOre := d.oreRequiredStock(1)
|
|
return fmt.Sprintf("%s%d%s", u.TextBold, neededOre, u.TextReset)
|
|
}
|
|
|
|
func (d *Day14) Part2() string {
|
|
oreAvailable := int64(1000000000000)
|
|
estimate := oreAvailable / d.oreRequiredStock(1)
|
|
|
|
high := estimate * 2
|
|
low := estimate
|
|
|
|
lastSuccess := low
|
|
lastFailure := high
|
|
fuelProduced := low
|
|
|
|
for math.Abs(float64(lastFailure)-float64(lastSuccess)) > 1 {
|
|
oreConsumed := d.oreRequiredStock(fuelProduced)
|
|
if oreConsumed < oreAvailable {
|
|
lastSuccess = fuelProduced
|
|
fuelProduced += (lastFailure - lastSuccess) / 2
|
|
} else {
|
|
lastFailure = fuelProduced
|
|
fuelProduced -= (lastFailure - lastSuccess) / 2
|
|
}
|
|
}
|
|
|
|
return fmt.Sprintf("%s%d%s", u.TextBold, lastSuccess, u.TextReset)
|
|
}
|