Skip to content

Commit

Permalink
2019 Day 14 Puzzle
Browse files Browse the repository at this point in the history
  • Loading branch information
KyleGough committed Nov 4, 2024
1 parent f171821 commit 04fd7db
Show file tree
Hide file tree
Showing 8 changed files with 271 additions and 0 deletions.
99 changes: 99 additions & 0 deletions solutions/2019/14/day14.helper.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
interface Requirement {
product: string;
quantity: number;
}

interface Reaction {
quantity: number;
requirements: Requirement[];
}

const parseReaction = (input: string): Record<string, Reaction> => {
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<string, Reaction> => {
const reactions = input.split('\n').map(parseReaction);
return reactions.reduce((prev, curr) => Object.assign(curr, prev), {});
};

export const getOreCount = (
reactions: Record<string, Reaction>,
fuelRequired = 1
): number => {
const stockpile: Record<string, number> = 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;
};
39 changes: 39 additions & 0 deletions solutions/2019/14/day14.test.ts
Original file line number Diff line number Diff line change
@@ -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);
});
});
9 changes: 9 additions & 0 deletions solutions/2019/14/example.txt
Original file line number Diff line number Diff line change
@@ -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
12 changes: 12 additions & 0 deletions solutions/2019/14/example2.txt
Original file line number Diff line number Diff line change
@@ -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
17 changes: 17 additions & 0 deletions solutions/2019/14/example3.txt
Original file line number Diff line number Diff line change
@@ -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
61 changes: 61 additions & 0 deletions solutions/2019/14/input.txt
Original file line number Diff line number Diff line change
@@ -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
11 changes: 11 additions & 0 deletions solutions/2019/14/p1.ts
Original file line number Diff line number Diff line change
@@ -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
23 changes: 23 additions & 0 deletions solutions/2019/14/p2.ts
Original file line number Diff line number Diff line change
@@ -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

0 comments on commit 04fd7db

Please sign in to comment.