diff --git a/days/14.go b/days/14.go new file mode 100644 index 0000000..8b82b17 --- /dev/null +++ b/days/14.go @@ -0,0 +1,113 @@ +package days + +import ( + "fmt" + "math" + "strconv" + "strings" + + u "parnic.com/aoc2019/utilities" +) + +type reaction struct { + inputs map[string]int64 + output u.Pair[string, int] +} + +type Day14 struct { + reactions []reaction +} + +func (d *Day14) Parse() { + 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]int64) + for _, input := range inputs { + pair := strings.Split(input, " ") + d.reactions[i].inputs[pair[1]], _ = strconv.ParseInt(pair[0], 10, 64) + } + } +} + +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) getOreRequiredForFuel(qty int64) int64 { + oreRequired := int64(0) + needs := map[string]int64{ + "FUEL": qty, + } + excess := make(map[string]int64) + + getFromExcess := func(qty int64, chemical string) int64 { + available := u.Min(excess[chemical], qty) + excess[chemical] -= available + return available + } + + for len(needs) > 0 { + keys := u.MapKeys(needs) + producing := keys[0] + qtyRequired := needs[producing] + delete(needs, producing) + + fromExcess := getFromExcess(qtyRequired, producing) + if fromExcess == qtyRequired { + continue + } + qtyRequired -= fromExcess + + reaction := d.getReactionProducing(producing) + + qtyProduced := int64(reaction.output.Second) + reactionsNeeded := int64(math.Ceil(float64(qtyRequired) / float64(qtyProduced))) + + excess[producing] = (qtyProduced * reactionsNeeded) - qtyRequired + + for reagent, inputQty := range reaction.inputs { + qtyNeeded := inputQty * reactionsNeeded + if reagent == "ORE" { + oreRequired += qtyNeeded + } else { + needs[reagent] += qtyNeeded + } + } + } + + return oreRequired +} + +func (d *Day14) Part1() string { + neededOre := d.getOreRequiredForFuel(1) + return fmt.Sprintf("Minimum ore to produce 1 FUEL: %s%d%s", u.TextBold, neededOre, u.TextReset) +} + +func (d *Day14) Part2() string { + oreAvailable := int64(1000000000000) + estimate := oreAvailable / d.getOreRequiredForFuel(1) + lastSuccess := u.Bisect(estimate, estimate*2, 1, func(val int64) bool { + oreConsumed := d.getOreRequiredForFuel(val) + return oreConsumed < oreAvailable + }) + + return fmt.Sprintf("Maximum fuel we can make from 1 trillion ore: %s%d%s", u.TextBold, lastSuccess, u.TextReset) +} diff --git a/inputs/14p.txt b/inputs/14p.txt new file mode 100644 index 0000000..9395bfe --- /dev/null +++ b/inputs/14p.txt @@ -0,0 +1,56 @@ +6 WBVJ, 16 CDVNK => 2 PJBZT +135 ORE => 8 MWDXJ +27 NBRHT, 2 NSWK, 2 CMHMQ, 29 NFCB, 11 KNGJ, 12 MGCKC, 56 NHTKL, 7 WNFSV => 1 FUEL +1 SFJFX, 3 MXNK => 4 NLSBZ +2 PFKRW, 1 VXFRX, 22 QDJCL => 6 GBDG +7 TSTF, 4 ZLJN => 7 DMWS +5 KPCF, 1 DLMDJ, 1 FNWGH => 6 TSTF +8 DTWKS, 1 GBDG => 4 CGZQ +26 CNWZM, 4 KPCF => 3 DTWKS +1 JVLHM, 7 DTWKS, 7 PJBZT => 8 MRPHV +2 MWDXJ => 3 VHFPC +1 WXNW, 6 PFKRW => 7 ZVGVP +2 ZVGVP => 1 CMHMQ +8 JVLHM, 11 XRKN, 1 HCGKZ => 8 CHZLX +20 TSTF => 4 XDZMZ +3 CMHMQ, 7 ZVGVP, 10 XRKN => 9 FNWGH +12 HCGKZ, 4 NLSBZ, 15 RWRDP, 4 MRPHV, 31 KRDV, 6 PMXK, 2 NFVZ => 7 KNGJ +1 TXZCM => 9 BMPJ +2 ZFXQ => 3 NBRHT +13 JVLHM, 1 VHFPC => 3 PBJPZ +7 HCGKZ => 7 PMXK +2 RWRDP, 3 VSTQ, 12 PMXK => 7 MXNK +1 PJBZT, 3 QRSK => 1 KRDV +1 MGCKC, 6 CMHMQ => 6 PQTVS +1 TNHCS, 24 ZLJN => 4 RWRDP +5 MWDXJ, 1 WXNW => 9 QBCLF +1 ZFXQ, 1 DLMDJ => 4 DJXRM +1 ZFXQ => 2 CNWZM +1 KPCF => 6 ZXDVF +2 MRPHV => 1 GSTG +5 BMPJ, 2 ZLJN => 8 XQJZ +1 MWDXJ, 1 ZVGVP => 3 CDVNK +3 NFCB, 3 CMHMQ, 1 MWDXJ => 4 XRKN +1 WXNW, 1 TXZCM => 5 ZLJN +4 ZXDVF => 4 WBVJ +2 GBDG => 4 KPCF +4 CHZLX, 7 ZFXQ, 14 PQTVS => 9 VSTQ +3 TXZCM, 7 ZLJN, 7 ZXDVF => 9 JVLHM +1 DMWS, 3 TSTF => 5 HCGKZ +2 CGZQ => 4 NFVZ +2 PQTVS, 9 VMNJ => 9 TXZCM +3 KPCF => 4 DLMDJ +7 VMNJ, 24 XQJZ, 7 GSTG, 8 NLSBZ, 10 MGCKC, 2 SFJFX, 18 BMPJ => 1 NSWK +41 CNWZM, 5 DJXRM, 1 QRSK, 1 KPCF, 15 XDZMZ, 3 MRPHV, 1 NLSBZ, 9 KRDV => 2 WNFSV +10 PBJPZ, 29 BMPJ, 2 PMXK => 7 SFJFX +116 ORE => 4 WXNW +2 CNWZM => 2 TNHCS +10 QBCLF => 7 NFCB +1 QBCLF => 2 ZFXQ +15 ZLJN => 7 QRSK +183 ORE => 3 QDJCL +11 GBDG => 5 VMNJ +4 DMWS, 3 QRSK => 3 NHTKL +124 ORE => 6 VXFRX +1 MWDXJ => 6 MGCKC +108 ORE => 9 PFKRW \ No newline at end of file diff --git a/inputs/14s1.txt b/inputs/14s1.txt new file mode 100644 index 0000000..26a4d7e --- /dev/null +++ b/inputs/14s1.txt @@ -0,0 +1,6 @@ +10 ORE => 10 A +1 ORE => 1 B +7 A, 1 B => 1 C +7 A, 1 C => 1 D +7 A, 1 D => 1 E +7 A, 1 E => 1 FUEL \ No newline at end of file diff --git a/inputs/14s2.txt b/inputs/14s2.txt new file mode 100644 index 0000000..80a5907 --- /dev/null +++ b/inputs/14s2.txt @@ -0,0 +1,7 @@ +9 ORE => 2 A +8 ORE => 3 B +7 ORE => 5 C +3 A, 4 B => 1 AB +5 B, 7 C => 1 BC +4 C, 1 A => 1 CA +2 AB, 3 BC, 4 CA => 1 FUEL \ No newline at end of file diff --git a/inputs/14s3.txt b/inputs/14s3.txt new file mode 100644 index 0000000..fe06479 --- /dev/null +++ b/inputs/14s3.txt @@ -0,0 +1,9 @@ +157 ORE => 5 NZVS +165 ORE => 6 DCFZ +44 XJWVT, 5 KHKGT, 1 QDVJ, 29 NZVS, 9 GPVTF, 48 HKGWZ => 1 FUEL +12 HKGWZ, 1 GPVTF, 8 PSHF => 9 QDVJ +179 ORE => 7 PSHF +177 ORE => 5 HKGWZ +7 DCFZ, 7 PSHF => 2 XJWVT +165 ORE => 2 GPVTF +3 DCFZ, 7 NZVS, 5 HKGWZ, 10 PSHF => 8 KHKGT \ No newline at end of file diff --git a/inputs/14s4.txt b/inputs/14s4.txt new file mode 100644 index 0000000..7c1333b --- /dev/null +++ b/inputs/14s4.txt @@ -0,0 +1,12 @@ +2 VPVL, 7 FWMGM, 2 CXFTF, 11 MNCFX => 1 STKFG +17 NVRVD, 3 JNWZP => 8 VPVL +53 STKFG, 6 MNCFX, 46 VJHF, 81 HVMC, 68 CXFTF, 25 GNMV => 1 FUEL +22 VJHF, 37 MNCFX => 5 FWMGM +139 ORE => 4 NVRVD +144 ORE => 7 JNWZP +5 MNCFX, 7 RFSQX, 2 FWMGM, 2 VPVL, 19 CXFTF => 3 HVMC +5 VJHF, 7 MNCFX, 9 VPVL, 37 CXFTF => 6 GNMV +145 ORE => 6 MNCFX +1 NVRVD => 8 CXFTF +1 VJHF, 6 MNCFX => 4 RFSQX +176 ORE => 6 VJHF \ No newline at end of file diff --git a/inputs/14s5.txt b/inputs/14s5.txt new file mode 100644 index 0000000..753fa4e --- /dev/null +++ b/inputs/14s5.txt @@ -0,0 +1,17 @@ +171 ORE => 8 CNZTR +7 ZLQW, 3 BMBT, 9 XCVML, 26 XMNCP, 1 WPTQ, 2 MZWV, 1 RJRHP => 4 PLWSL +114 ORE => 4 BHXH +14 VRPVC => 6 BMBT +6 BHXH, 18 KTJDG, 12 WPTQ, 7 PLWSL, 31 FHTLT, 37 ZDVW => 1 FUEL +6 WPTQ, 2 BMBT, 8 ZLQW, 18 KTJDG, 1 XMNCP, 6 MZWV, 1 RJRHP => 6 FHTLT +15 XDBXC, 2 LTCX, 1 VRPVC => 6 ZLQW +13 WPTQ, 10 LTCX, 3 RJRHP, 14 XMNCP, 2 MZWV, 1 ZLQW => 1 ZDVW +5 BMBT => 4 WPTQ +189 ORE => 9 KTJDG +1 MZWV, 17 XDBXC, 3 XCVML => 2 XMNCP +12 VRPVC, 27 CNZTR => 2 XDBXC +15 KTJDG, 12 BHXH => 5 XCVML +3 BHXH, 2 VRPVC => 7 MZWV +121 ORE => 7 VRPVC +7 XCVML => 6 RJRHP +5 BHXH, 4 VRPVC => 5 LTCX \ No newline at end of file diff --git a/main.go b/main.go index 7bb8514..3e6caa5 100644 --- a/main.go +++ b/main.go @@ -44,6 +44,7 @@ var dayMap = []day{ &days.Day11{}, &days.Day12{}, &days.Day13{}, + &days.Day14{}, } func main() { diff --git a/utilities/bisect.go b/utilities/bisect.go new file mode 100644 index 0000000..d2038ad --- /dev/null +++ b/utilities/bisect.go @@ -0,0 +1,26 @@ +package utilities + +import ( + "math" +) + +// Bisect takes a known-good low and known-bad high value as the bounds +// to bisect, and a function to test each value for success or failure. +// If the function succeeds, the value is adjusted toward the maximum, +// and if the function fails, the value is adjusted toward the minimum. +// The final value is returned when the difference between the success +// and the failure is less than or equal to the acceptance threshold +// (usually 1, for integers). +func Bisect[T Number](low, high, threshold T, tryFunc func(val T) bool) T { + for T(math.Abs(float64(high-low))) > threshold { + currVal := low + ((high - low) / 2) + success := tryFunc(currVal) + if success { + low = currVal + } else { + high = currVal + } + } + + return low +} diff --git a/utilities/math.go b/utilities/math.go index de6c372..11e6cf0 100644 --- a/utilities/math.go +++ b/utilities/math.go @@ -1,5 +1,7 @@ package utilities +import "math" + func GCD[T Integer](a, b T) T { if b == 0 { return a @@ -25,3 +27,23 @@ func LCM[T Integer](nums ...T) uint64 { func lcm[T Integer](a, b T) uint64 { return uint64(a*b) / uint64(GCD(a, b)) } + +func Min[T Number](nums ...T) T { + numNums := len(nums) + if numNums == 2 { + return T(math.Min(float64(nums[0]), float64(nums[1]))) + } + + if numNums == 0 { + return 0 + } + + least := nums[0] + for i := 1; i < numNums; i++ { + if nums[i] < least { + least = nums[i] + } + } + + return least +}