From 04fd7dbb8669a9cc3d389b2725f5eb27d52dbfc8 Mon Sep 17 00:00:00 2001 From: Kyle Gough Date: Mon, 4 Nov 2024 22:13:59 +0000 Subject: [PATCH] 2019 Day 14 Puzzle --- solutions/2019/14/day14.helper.ts | 99 +++++++++++++++++++++++++++++++ solutions/2019/14/day14.test.ts | 39 ++++++++++++ solutions/2019/14/example.txt | 9 +++ solutions/2019/14/example2.txt | 12 ++++ solutions/2019/14/example3.txt | 17 ++++++ solutions/2019/14/input.txt | 61 +++++++++++++++++++ solutions/2019/14/p1.ts | 11 ++++ solutions/2019/14/p2.ts | 23 +++++++ 8 files changed, 271 insertions(+) create mode 100644 solutions/2019/14/day14.helper.ts create mode 100644 solutions/2019/14/day14.test.ts create mode 100644 solutions/2019/14/example.txt create mode 100644 solutions/2019/14/example2.txt create mode 100644 solutions/2019/14/example3.txt create mode 100644 solutions/2019/14/input.txt create mode 100644 solutions/2019/14/p1.ts create mode 100644 solutions/2019/14/p2.ts diff --git a/solutions/2019/14/day14.helper.ts b/solutions/2019/14/day14.helper.ts new file mode 100644 index 0000000..8f49896 --- /dev/null +++ b/solutions/2019/14/day14.helper.ts @@ -0,0 +1,99 @@ +interface Requirement { + product: string; + quantity: number; +} + +interface Reaction { + quantity: number; + requirements: Requirement[]; +} + +const parseReaction = (input: string): Record => { + const [requirementList, product] = input.split(' => '); + const [quantity, productName] = product.split(' '); + const requirements = requirementList.split(', ').map((r) => ({ + product: r.split(' ')[1], + quantity: parseInt(r.split(' ')[0]), + })); + + return { + [productName]: { + quantity: parseInt(quantity), + requirements, + }, + }; +}; + +export const parseReactions = (input: string): Record => { + const reactions = input.split('\n').map(parseReaction); + return reactions.reduce((prev, curr) => Object.assign(curr, prev), {}); +}; + +export const getOreCount = ( + reactions: Record, + fuelRequired = 1 +): number => { + const stockpile: Record = Object.keys(reactions).reduce( + (prev, curr) => ({ ...prev, [curr]: 0 }), + {} + ); + + const requirements = reactions['FUEL'].requirements.map((r) => ({ + ...r, + quantity: r.quantity * fuelRequired, + })); + + let oreCount = 0; + + while (requirements.length) { + const requirement = requirements.pop() as Requirement; + const product = requirement.product; + const stock = stockpile[product]; + let quantity = requirement.quantity; + + // Take fully from stockpile. + if (quantity <= (stockpile[product] ?? 0)) { + stockpile[product] -= quantity; + continue; + } + + // Take partially from stockpile. + quantity -= stock; + delete stockpile[product]; + + // Calculate number of reactions required. + const productProduced = reactions[product].quantity; + const reactionCount = Math.ceil(quantity / productProduced); + + // Leftovers. + const leftover = productProduced * reactionCount - quantity; + stockpile[product] = (stockpile[product] ?? 0) + leftover; + + for (const reactionProduct of reactions[product].requirements) { + if (reactionProduct.product === 'ORE') { + oreCount += reactionCount * reactionProduct.quantity; + } else { + let requirementUpdated = false; + + // Update requirement if exists. + for (const current of requirements) { + if (current.product === reactionProduct.product) { + current.quantity += reactionCount * reactionProduct.quantity; + requirementUpdated = true; + break; + } + } + + // Create new requirement. + if (!requirementUpdated) { + requirements.push({ + ...reactionProduct, + quantity: reactionProduct.quantity * reactionCount, + }); + } + } + } + } + + return oreCount; +}; diff --git a/solutions/2019/14/day14.test.ts b/solutions/2019/14/day14.test.ts new file mode 100644 index 0000000..d2cb2c3 --- /dev/null +++ b/solutions/2019/14/day14.test.ts @@ -0,0 +1,39 @@ +import { day14p1 } from './p1'; +import { day14p2 } from './p2'; +import { getPuzzle } from '@utilities/getPuzzle'; + +const { example, example2, example3, input } = getPuzzle(__dirname); + +describe('Day 14 Puzzle', () => { + test('Part 1 Example', () => { + expect(day14p1(example)).toBe(13312); + }); + + test('Part 1 Example 2', () => { + expect(day14p1(example2)).toBe(180697); + }); + + test('Part 1 Example 3', () => { + expect(day14p1(example3)).toBe(2_210_736); + }); + + test('Part 1 Input', () => { + expect(day14p1(input)).toBe(397771); + }); + + test('Part 2 Example', () => { + expect(day14p2(example)).toBe(82_892_753); + }); + + test('Part 2 Example 2', () => { + expect(day14p2(example2)).toBe(5_586_022); + }); + + test('Part 2 Example 3', () => { + expect(day14p2(example3)).toBe(460664); + }); + + test('Part 2 Input', () => { + expect(day14p2(input)).toBe(3_126_714); + }); +}); diff --git a/solutions/2019/14/example.txt b/solutions/2019/14/example.txt new file mode 100644 index 0000000..fe06479 --- /dev/null +++ b/solutions/2019/14/example.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/solutions/2019/14/example2.txt b/solutions/2019/14/example2.txt new file mode 100644 index 0000000..7c1333b --- /dev/null +++ b/solutions/2019/14/example2.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/solutions/2019/14/example3.txt b/solutions/2019/14/example3.txt new file mode 100644 index 0000000..753fa4e --- /dev/null +++ b/solutions/2019/14/example3.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/solutions/2019/14/input.txt b/solutions/2019/14/input.txt new file mode 100644 index 0000000..438d38f --- /dev/null +++ b/solutions/2019/14/input.txt @@ -0,0 +1,61 @@ +8 LHFV => 3 PMVMQ +2 ZXNM, 1 PSVLS, 4 GRDNT, 26 GLZH, 3 VHJX, 16 BGPF, 1 LHVTN => 4 BTQL +10 NKHSG, 20 FCPC, 11 GRDNT => 5 HDJB +6 WPZN, 1 LHFV => 7 BGPF +1 WDXT, 1 PLCNZ => 3 QHFKR +12 LCHZ => 1 TPXCK +11 LSNG => 4 XFGH +195 ORE => 4 GRNC +8 XFGQ => 1 GRDNT +1 FBRG => 5 LCHZ +7 XZBJ, 8 RSZF, 9 SVDX => 9 LWDP +20 WDXT => 5 RQFRT +1 LXQWG, 1 GLZH => 6 SDLJ +4 XFGH => 1 GCZLZ +1 WPZN => 1 FBRG +19 XZBJ => 5 WXGV +1 GDXC => 6 WDXT +1 WXGV, 1 NKHSG, 2 LWDP => 5 FCNPB +4 LWDP, 5 BGPF => 9 KLRB +1 GMRN => 4 GLZH +1 RQFRT => 5 SVDX +2 HWKG => 7 LHFV +2 LCHZ, 13 JTJT, 10 TPXCK => 3 RSZF +29 MZTVH => 6 TSGR +9 NRFLK, 1 SVDX => 5 NKHSG +123 ORE => 9 GDXC +1 PZPBV, 21 PMVMQ, 1 GCZLZ => 8 SKZGB +3 GRNC, 5 GDXC => 8 QZVM +6 VTDQ, 13 TCQW, 3 FCNPB, 48 PSVLS, 3 RLNF, 73 BTQL, 5 MHRVG, 26 BGPF, 26 HDJB, 5 XFGQ, 6 HTFL => 1 FUEL +5 QZVM, 2 JTJT => 1 PXKHG +3 LSNG, 1 PMVMQ => 8 VTDQ +31 XFGH => 1 FCPC +9 PSVLS => 8 FWGTF +1 GRNC => 3 WPZN +16 JBXDX, 4 GRNC => 6 HWKG +1 SKZGB, 5 RSZF => 4 XZBJ +134 ORE => 9 CTDRZ +1 SVDX, 2 TPXCK => 7 JTJT +6 RQFRT, 4 KBCW => 3 BGNLR +12 KLRB, 12 LHFV => 4 HTFL +2 GMRN => 6 XFGQ +16 WNSW, 12 SKZGB => 8 LXQWG +2 NRFLK, 2 CTDRZ => 9 JBXDX +1 PZPBV => 8 RLNF +2 JTJT, 5 GCZLZ => 3 WNSW +5 WXGV, 2 LCHZ => 2 SCDS +1 QHFKR => 3 GMRN +10 JTJT, 2 HRCG => 8 KBCW +7 HWKG => 4 PSVLS +7 WNSW, 1 PXKHG, 3 BGNLR => 9 MZTVH +15 TPXCK, 11 LHFV => 5 HRCG +1 LSNG, 1 HWKG => 3 PZPBV +7 BGPF => 9 PLCNZ +1 ZGWT => 6 ZXNM +26 NKHSG, 1 LHFV, 2 JTJT, 26 WXGV, 6 SDLJ, 1 KLRB, 1 TSGR => 8 TCQW +154 ORE => 4 NRFLK +1 GMRN => 3 VHJX +5 QZVM, 3 GDXC => 7 LSNG +5 WNSW => 5 ZGWT +6 QHFKR, 8 PZPBV, 10 FBRG, 13 FWGTF, 1 LHVTN, 4 SCDS, 8 VHJX, 7 TSGR => 6 MHRVG +12 GLZH => 5 LHVTN \ No newline at end of file diff --git a/solutions/2019/14/p1.ts b/solutions/2019/14/p1.ts new file mode 100644 index 0000000..9d0cb5f --- /dev/null +++ b/solutions/2019/14/p1.ts @@ -0,0 +1,11 @@ +import { getPuzzle } from '@utilities/getPuzzle'; +import { run } from '@utilities/run'; +import { getOreCount, parseReactions } from './day14.helper'; + +export const day14p1 = (input: string) => { + const reactions = parseReactions(input); + return getOreCount(reactions); +}; + +const input = getPuzzle(__dirname).input; +run(() => day14p1(input)); // 397771 diff --git a/solutions/2019/14/p2.ts b/solutions/2019/14/p2.ts new file mode 100644 index 0000000..f33024f --- /dev/null +++ b/solutions/2019/14/p2.ts @@ -0,0 +1,23 @@ +import { getPuzzle } from '@utilities/getPuzzle'; +import { run } from '@utilities/run'; +import { getOreCount, parseReactions } from './day14.helper'; + +export const day14p2 = (input: string) => { + const reactions = parseReactions(input); + const singleFuelCost = getOreCount(reactions); + const oreCount = 1_000_000_000_000; + const lowerBound = Math.ceil(oreCount / singleFuelCost); + + let fuelCount = lowerBound; + let remaining = oreCount; + + while (remaining > singleFuelCost) { + remaining = oreCount - getOreCount(reactions, fuelCount); + fuelCount += Math.floor(remaining / singleFuelCost); + } + + return fuelCount; +}; + +const input = getPuzzle(__dirname).input; +run(() => day14p2(input)); // 3126714