mirror of
https://github.com/Luzifer/aoc2019.git
synced 2024-12-22 05:51:16 +00:00
Add solution for Day 14
Signed-off-by: Knut Ahlers <knut@ahlers.me>
This commit is contained in:
parent
1c34ac5186
commit
21d19eb3f1
3 changed files with 279 additions and 0 deletions
158
day14.go
Normal file
158
day14.go
Normal file
|
@ -0,0 +1,158 @@
|
||||||
|
package aoc2019
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"io"
|
||||||
|
"log"
|
||||||
|
"math"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
type day14NanoFactory struct {
|
||||||
|
reactions map[string]day14Reaction
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d day14NanoFactory) calculateOreForFuel(n int64) int64 {
|
||||||
|
var (
|
||||||
|
availableMats = map[string]int64{}
|
||||||
|
oreUsed int64
|
||||||
|
)
|
||||||
|
|
||||||
|
var produce func(mat string, amount int64)
|
||||||
|
produce = func(mat string, amount int64) {
|
||||||
|
var (
|
||||||
|
reaction = d.reactions[mat]
|
||||||
|
reactN = int64(math.Ceil(float64(amount) / float64(reaction.yield)))
|
||||||
|
)
|
||||||
|
|
||||||
|
for reqMat, reqAm := range reaction.materials {
|
||||||
|
// Multiply for requested amount
|
||||||
|
reqAm *= reactN
|
||||||
|
|
||||||
|
if reqMat == "ORE" {
|
||||||
|
oreUsed += reqAm
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if availableMats[reqMat] < reqAm {
|
||||||
|
produce(reqMat, reqAm-availableMats[reqMat])
|
||||||
|
}
|
||||||
|
|
||||||
|
availableMats[reqMat] -= reqAm
|
||||||
|
}
|
||||||
|
|
||||||
|
availableMats[mat] += reactN * reaction.yield
|
||||||
|
}
|
||||||
|
|
||||||
|
produce("FUEL", n)
|
||||||
|
|
||||||
|
return oreUsed
|
||||||
|
}
|
||||||
|
|
||||||
|
type day14Reaction struct {
|
||||||
|
materials map[string]int64
|
||||||
|
yield int64
|
||||||
|
|
||||||
|
factory *day14NanoFactory
|
||||||
|
}
|
||||||
|
|
||||||
|
func day14ParseReactionChain(r io.Reader) (*day14NanoFactory, error) {
|
||||||
|
var factory = &day14NanoFactory{reactions: make(map[string]day14Reaction)}
|
||||||
|
|
||||||
|
parse := func(in string) (int64, string, error) {
|
||||||
|
var parts = strings.Split(in, " ")
|
||||||
|
count, err := strconv.ParseInt(parts[0], 10, 64)
|
||||||
|
return count, parts[1], errors.Wrap(err, "Unable to parse count")
|
||||||
|
}
|
||||||
|
|
||||||
|
scanner := bufio.NewScanner(r)
|
||||||
|
for scanner.Scan() {
|
||||||
|
var (
|
||||||
|
sides = strings.Split(scanner.Text(), " => ")
|
||||||
|
inputs = strings.Split(sides[0], ", ")
|
||||||
|
)
|
||||||
|
|
||||||
|
yield, outMaterial, err := parse(sides[1])
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, "Unable to parse output side")
|
||||||
|
}
|
||||||
|
|
||||||
|
rea := day14Reaction{factory: factory, yield: yield, materials: make(map[string]int64)}
|
||||||
|
|
||||||
|
for _, in := range inputs {
|
||||||
|
required, inMaterial, err := parse(in)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, "Unable to parse input material")
|
||||||
|
}
|
||||||
|
rea.materials[inMaterial] = required
|
||||||
|
}
|
||||||
|
|
||||||
|
factory.reactions[outMaterial] = rea
|
||||||
|
}
|
||||||
|
|
||||||
|
return factory, errors.Wrap(scanner.Err(), "Unable to scan input")
|
||||||
|
}
|
||||||
|
|
||||||
|
func solveDay14Part1(inFile string) (int64, error) {
|
||||||
|
f, err := os.Open(inFile)
|
||||||
|
if err != nil {
|
||||||
|
return 0, errors.Wrap(err, "Unable to open input file")
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
factory, err := day14ParseReactionChain(f)
|
||||||
|
if err != nil {
|
||||||
|
return 0, errors.Wrap(err, "Unable to parse reaction chain")
|
||||||
|
}
|
||||||
|
|
||||||
|
return factory.calculateOreForFuel(1), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func solveDay14Part2(inFile string) (int64, error) {
|
||||||
|
f, err := os.Open(inFile)
|
||||||
|
if err != nil {
|
||||||
|
return 0, errors.Wrap(err, "Unable to open input file")
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
factory, err := day14ParseReactionChain(f)
|
||||||
|
if err != nil {
|
||||||
|
return 0, errors.Wrap(err, "Unable to parse reaction chain")
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
limit int64 = 1000000000000
|
||||||
|
orePerFuel = factory.calculateOreForFuel(1)
|
||||||
|
minBound = limit / orePerFuel
|
||||||
|
maxBound = minBound * 2
|
||||||
|
)
|
||||||
|
|
||||||
|
for {
|
||||||
|
var (
|
||||||
|
test = (minBound + maxBound) / 2
|
||||||
|
oreUsed = factory.calculateOreForFuel(test)
|
||||||
|
)
|
||||||
|
|
||||||
|
log.Printf("limit=%d used=%d min=%d max=%d test=%d", limit, oreUsed, minBound, maxBound, test)
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case minBound+1 == maxBound && oreUsed < limit:
|
||||||
|
return test, nil
|
||||||
|
|
||||||
|
case oreUsed > limit:
|
||||||
|
maxBound = test
|
||||||
|
|
||||||
|
case oreUsed < limit:
|
||||||
|
minBound = test
|
||||||
|
|
||||||
|
case oreUsed == limit:
|
||||||
|
// Doubt this will happen but well
|
||||||
|
return test, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
57
day14_input.txt
Normal file
57
day14_input.txt
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
164 ORE => 2 TLJL
|
||||||
|
2 VKMKW => 4 DTCB
|
||||||
|
2 VKMKW, 16 ZKMXZ, 2 TSVN => 3 TSVQX
|
||||||
|
2 NFJKN, 2 LMVCD, 5 DSQLK => 1 RNRPB
|
||||||
|
3 NFJKN, 3 TSVQX, 6 VKMKW => 7 FBFQZ
|
||||||
|
7 ZKMXZ, 1 PVQLR => 4 MBWVZ
|
||||||
|
3 SHMGH => 4 ZKMXZ
|
||||||
|
2 MSZWL => 4 QSDC
|
||||||
|
3 DGFK => 9 TSVN
|
||||||
|
21 DTCB, 1 DSQLK => 8 DGDGS
|
||||||
|
1 DGFK, 1 SXNZP, 1 GCHL => 9 JZWH
|
||||||
|
1 DSQLK, 4 WFDK, 1 BVSL, 1 TZND, 15 HVPMK, 1 NSKX => 3 DSFDZ
|
||||||
|
1 ZDVCH, 2 PVQLR, 7 VLNX, 4 JTZM, 1 MVLHV, 1 RDBR, 11 MBWVZ => 7 ZTXQ
|
||||||
|
9 JZWH, 4 BVSL, 2 NFJKN, 26 LMVCD, 3 MKFDR, 2 TGMNG, 1 NTMRX, 12 DGDGS => 4 PBRZF
|
||||||
|
25 RNRPB => 6 MKFDR
|
||||||
|
27 ZKMXZ, 4 NFJKN, 1 DTCB => 5 RDBR
|
||||||
|
2 ZXTQ, 13 KHRFD => 7 JQJGR
|
||||||
|
3 WFDVM, 18 QSLKV => 5 NSBN
|
||||||
|
2 ZXTQ, 6 NTMRX => 4 WFDK
|
||||||
|
1 VKMKW, 14 TSVQX, 10 ZKMXZ => 6 NFJKN
|
||||||
|
1 NVDL, 1 ZKMXZ, 9 NSKX => 5 ZDVCH
|
||||||
|
7 QSDC, 1 BVSL => 4 GCHL
|
||||||
|
1 QSLKV, 13 XRBKF => 5 NTMRX
|
||||||
|
11 GDPLN => 8 KHRFD
|
||||||
|
15 VCJSD => 7 LSLP
|
||||||
|
4 PCHC, 1 SXNZP, 1 JQJGR => 9 KPBPL
|
||||||
|
18 TGMNG => 4 HVPMK
|
||||||
|
1 XRBKF, 26 LVLV => 6 WFDVM
|
||||||
|
9 VCJSD, 14 SXNZP => 4 TGMNG
|
||||||
|
22 WFDK, 20 FBFQZ => 6 LHJBH
|
||||||
|
195 ORE => 7 SHMGH
|
||||||
|
2 VCJSD, 1 XRBKF => 8 QSLKV
|
||||||
|
8 ZTXNJ, 4 TLJL => 2 MSZWL
|
||||||
|
2 LMVCD, 9 PVQLR => 4 NSKX
|
||||||
|
2 TLJL, 1 GJDPC, 8 ZXTQ => 8 PCHC
|
||||||
|
6 NSBN, 4 JVJV => 9 ZCDZ
|
||||||
|
155 ORE => 1 GDPLN
|
||||||
|
1 GDPLN => 4 VKMKW
|
||||||
|
1 KPBPL => 8 LVLV
|
||||||
|
30 NSBN, 20 MVLHV => 1 JVJV
|
||||||
|
1 LVLV => 1 DGFK
|
||||||
|
7 TSVQX => 6 LMVCD
|
||||||
|
7 TLJL, 16 MSZWL, 5 KHRFD => 2 ZXTQ
|
||||||
|
55 MBWVZ, 61 KHRFD, 16 DSFDZ, 40 LHJBH, 6 ZTXQ, 28 JZWH, 1 PBRZF => 1 FUEL
|
||||||
|
5 JQJGR, 20 VCJSD => 5 MVLHV
|
||||||
|
1 SHMGH, 1 ZTXNJ => 4 GJDPC
|
||||||
|
3 XRBKF, 9 QSLKV, 2 WFDK => 5 JTZM
|
||||||
|
5 GJDPC => 6 VCJSD
|
||||||
|
1 GJDPC, 7 XRBKF => 4 PVQLR
|
||||||
|
11 BVSL => 6 SXNZP
|
||||||
|
104 ORE => 3 ZTXNJ
|
||||||
|
3 JZWH, 9 HVPMK, 2 GCHL => 6 VLNX
|
||||||
|
1 LSLP => 6 XRBKF
|
||||||
|
1 TLJL => 5 BVSL
|
||||||
|
5 HVPMK => 9 DSQLK
|
||||||
|
6 FBFQZ, 22 PVQLR, 4 ZCDZ => 1 NVDL
|
||||||
|
3 JZWH => 1 TZND
|
64
day14_test.go
Normal file
64
day14_test.go
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
package aoc2019
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestDay14OreForFuel(t *testing.T) {
|
||||||
|
in := strings.NewReader("10 ORE => 10 A\n1 ORE => 1 B\n7 A, 1 B => 1 C\n7 A, 1 C => 1 D\n7 A, 1 D => 1 E\n7 A, 1 E => 1 FUEL")
|
||||||
|
factory, err := day14ParseReactionChain(in)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Parsing reaction chain caused an error: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if ore := factory.calculateOreForFuel(1); ore != 31 {
|
||||||
|
t.Errorf("Unexpected amount of ore: exp=31 got=%d", ore)
|
||||||
|
}
|
||||||
|
|
||||||
|
// More complex example
|
||||||
|
in = strings.NewReader(`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`)
|
||||||
|
|
||||||
|
factory, err = day14ParseReactionChain(in)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Parsing complex reaction chain caused an error: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if ore := factory.calculateOreForFuel(1); ore != 2210736 {
|
||||||
|
t.Errorf("Unexpected amount of ore: exp=2210736 got=%d", ore)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCalculateDay14_Part1(t *testing.T) {
|
||||||
|
count, err := solveDay14Part1("day14_input.txt")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Day 14 solver failed: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Logf("Solution Day 14 Part 1: %d", count)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCalculateDay14_Part2(t *testing.T) {
|
||||||
|
res, err := solveDay14Part2("day14_input.txt")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Day 14 solver failed: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Logf("Solution Day 14 Part 2: %d", res)
|
||||||
|
}
|
Loading…
Reference in a new issue