mirror of
https://github.com/Luzifer/aoc2019.git
synced 2024-12-21 21:41: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