diff --git a/days/14.go b/days/14.go index 36f98dc..8739554 100644 --- a/days/14.go +++ b/days/14.go @@ -2,6 +2,7 @@ package days import ( "fmt" + "math" "strconv" "strings" @@ -21,7 +22,7 @@ type Day14 struct { func (d *Day14) Parse() { d.leftovers = make(map[string]int) - lines := u.GetStringLines("14s2") + lines := u.GetStringLines("14p") d.reactions = make([]reaction, len(lines)) for i, line := range lines { sides := strings.Split(line, " => ") @@ -53,97 +54,74 @@ func (d Day14) Num() int { return 14 } -func (d Day14) getOreRequiredFor(chem string, amt int) (int, int) { - requiredOre := 0 - for _, reaction := range d.reactions { - if reaction.output.First == chem { - // if oreAmt, exists := reaction.inputs["ORE"]; exists && len(reaction.inputs) == 1 { - // if d.leftovers[chem] >= amt { - // d.leftovers[chem] -= amt - // return 0, amt - // } +func (d *Day14) oreRequiredStock(qty int64) int64 { + oreRequired := int64(0) + needs := map[string]int64{ + "FUEL": qty, + } + excess := make(map[string]int64) - // produced := reaction.output.Second - // for produced < amt { - // requiredOre += oreAmt - // produced += reaction.output.Second - // } - // requiredOre += oreAmt - // if produced > amt { - // d.leftovers[chem] += produced - amt - // } - // return requiredOre, produced - // } else { - for inChem, inAmt := range reaction.inputs { - produced := 0 - if d.leftovers[inChem] >= inAmt { - d.leftovers[inChem] -= inAmt - produced = inAmt - } else { - for produced < inAmt { - madeOre, madeChem := d.getOreRequiredFor(inChem, inAmt) - produced += madeChem - requiredOre += madeOre - } - if produced > inAmt { - d.leftovers[inChem] += produced - inAmt - } - } + 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 requiredOre, 0 + return oreRequired } func (d *Day14) Part1() string { - fuelReaction := d.getReactionProducing("FUEL") - if fuelReaction == nil { - panic("") - } - - neededMaterial := map[string]int{ - "FUEL": 1, - } - var recurse func(neededMaterial map[string]int) map[string]int - recurse = func(neededMaterial map[string]int) map[string]int { - neededInputs := make(map[string]int) - for chem, amt := range neededMaterial { - reaction := d.getReactionProducing(chem) - if reaction == nil { - continue - } - produced := reaction.output.Second - reactionsNeeded := 1 - for produced < amt { - produced += reaction.output.Second - reactionsNeeded++ - } - for inChem, inAmt := range reaction.inputs { - neededInputs[inChem] += inAmt * reactionsNeeded - } - } - if len(neededInputs) > 0 { - recursed := recurse(neededInputs) - for k, v := range recursed { - neededInputs[k] += v - } - } - return neededInputs - } - recursed := recurse(neededMaterial) - fmt.Println(len(recursed)) - - ore := 0 - for inChem, inAmt := range fuelReaction.inputs { - requiredOre, _ := d.getOreRequiredFor(inChem, inAmt) - ore += requiredOre - } - - return fmt.Sprintf("%s%d%s", u.TextBold, ore, u.TextReset) + neededOre := d.oreRequiredStock(1) + return fmt.Sprintf("%s%d%s", u.TextBold, neededOre, u.TextReset) } func (d *Day14) Part2() string { - return "" + 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) } diff --git a/utilities/map.go b/utilities/map.go new file mode 100644 index 0000000..58e970f --- /dev/null +++ b/utilities/map.go @@ -0,0 +1,17 @@ +package utilities + +func MapKeys[T comparable, U any](m map[T]U) []T { + r := make([]T, 0, len(m)) + for k := range m { + r = append(r, k) + } + return r +} + +func MapValues[T comparable, U any](m map[T]U) []U { + r := make([]U, 0, len(m)) + for _, v := range m { + r = append(r, v) + } + return r +}