From 882e1675b39603efb70fb27983e7ad5ce76e6ceb Mon Sep 17 00:00:00 2001 From: Thijs Busser Date: Mon, 25 Nov 2024 15:47:30 +0100 Subject: [PATCH] Add solution for 2015, day 19 --- 2015/19/README.md | 58 ++++++++++++++++++++++++++++++++ 2015/19/helpers/get-mutations.ts | 18 ++++++++++ 2015/19/helpers/parse-input.ts | 31 +++++++++++++++++ 2015/19/part-1.ts | 31 +++++++++++++++++ 2015/19/part-2.ts | 43 +++++++++++++++++++++++ 5 files changed, 181 insertions(+) create mode 100644 2015/19/README.md create mode 100644 2015/19/helpers/get-mutations.ts create mode 100644 2015/19/helpers/parse-input.ts create mode 100644 2015/19/part-1.ts create mode 100644 2015/19/part-2.ts diff --git a/2015/19/README.md b/2015/19/README.md new file mode 100644 index 0000000..992fd90 --- /dev/null +++ b/2015/19/README.md @@ -0,0 +1,58 @@ +# Day 19: Medicine for Rudolph +Rudolph the Red-Nosed Reindeer is sick! His nose isn't shining very brightly, and he needs medicine. + +Red-Nosed Reindeer biology isn't similar to regular reindeer biology; Rudolph is going to need custom-made medicine. Unfortunately, Red-Nosed Reindeer chemistry isn't similar to regular reindeer chemistry, either. + +The North Pole is equipped with a Red-Nosed Reindeer nuclear fusion/fission plant, capable of constructing any Red-Nosed Reindeer molecule you need. It works by starting with some input molecule and then doing a series of **replacements**, one per step, until it has the right molecule. + +However, the machine has to be calibrated before it can be used. Calibration involves determining the number of molecules that can be generated in one step from a given starting point. + +For example, imagine a simpler machine that supports only the following replacements: + +``` +H => HO +H => OH +O => HH +``` + +Given the replacements above and starting with `HOH`, the following molecules could be generated: + +``` +HOOH (via H => HO on the first H). +HOHO (via H => HO on the second H). +OHOH (via H => OH on the first H). +HOOH (via H => OH on the second H). +HHHH (via O => HH). +``` + +So, in the example above, there are `4` **distinct molecules** (not five, because `HOOH` appears twice) after one replacement from `HOH`. Santa's favorite molecule, `HOHOHO`, can become `7` distinct molecules (over nine replacements: six from `H`, and three from `O`). + +The machine replaces without regard for the surrounding characters. For example, given the string `H2O`, the transition `H => OO` would result in `OO2O`. + +Your puzzle input describes all of the possible replacements and, at the bottom, the medicine molecule for which you need to calibrate the machine. **How many distinct molecules can be created** after all the different ways you can do one replacement on the medicine molecule? + +## Part Two +Now that the machine is calibrated, you're ready to begin molecule fabrication. + +Molecule fabrication always begins with just a single electron, `e`, and applying replacements one at a time, just like the ones during calibration. + +For example, suppose you have the following replacements: +``` +e => H +e => O +H => HO +H => OH +O => HH +``` + +If you'd like to make `HOH`, you start with `e`, and then make the following replacements: + +``` +e => O to get O +O => HH to get HH +H => OH (on the second H) to get HOH +``` + +So, you could make `HOH` after **`3` steps**. Santa's favorite molecule, `HOHOHO`, can be made in **`6` steps**. + +How long will it take to make the medicine? Given the available **replacements** and the **medicine molecule** in your puzzle input, what is the **fewest number of steps** to go from `e` to the medicine molecule? \ No newline at end of file diff --git a/2015/19/helpers/get-mutations.ts b/2015/19/helpers/get-mutations.ts new file mode 100644 index 0000000..181ff5c --- /dev/null +++ b/2015/19/helpers/get-mutations.ts @@ -0,0 +1,18 @@ +export function* getMutations(molecule: string, sequence: string, replacement: string): Generator { + // Create a regex to find the sequence in the molecule. + const regex = new RegExp(sequence, 'g'); + + let match: RegExpExecArray | null; + + // Process all the matches in the molecule for the current replacement. + while ((match = regex.exec(molecule))) { + // Get the part before the match and after the match. + const moleculeStart = molecule.substring(0, match.index); + const moleculeEnd = molecule.substring(match.index + sequence.length); + + // Yield the mutation of the molecule with the replacement done. + yield `${moleculeStart}${replacement}${moleculeEnd}`; + } + + return; +} diff --git a/2015/19/helpers/parse-input.ts b/2015/19/helpers/parse-input.ts new file mode 100644 index 0000000..19eb231 --- /dev/null +++ b/2015/19/helpers/parse-input.ts @@ -0,0 +1,31 @@ +export type MachineInput = { + medicineMolecule: string; + replacements: Replacement[]; +}; + +export type Replacement = { + sequence: string; + replacement: string; +}; + +/* ========================================================================== */ + +export function parseInput(input: string): MachineInput { + const lines = input.split('\n'); + + // The last line is the medicine molecule, we need this. + const medicineMolecule = lines.pop(); + // Before the medicine molecule is an empty string, this can be discarded. + lines.pop(); + + const replacements = lines.map(line => { + const [sequence, replacement] = line.split(' => '); + + return { sequence, replacement }; + }); + + return { + medicineMolecule, + replacements + }; +}; diff --git a/2015/19/part-1.ts b/2015/19/part-1.ts new file mode 100644 index 0000000..4015f81 --- /dev/null +++ b/2015/19/part-1.ts @@ -0,0 +1,31 @@ +import { MachineInput, parseInput } from './helpers/parse-input.js'; +import { getMutations } from './helpers/get-mutations.js'; + +/* ========================================================================== */ + +function countNumberOfMutations(machineInput: MachineInput): number { + const mutations = new Set(); + + machineInput.replacements.forEach(({ replacement, sequence }) => { + for (const mutation of getMutations(machineInput.medicineMolecule, sequence, replacement)) { + mutations.add(mutation); + } + }); + + return mutations.size; +} + +/* ========================================================================== */ + +async function solver(input: string): Promise { + const machineInput = parseInput(input); + + return countNumberOfMutations(machineInput); +} + +/* ========================================================================== */ + +export default { + prompt: 'Number of distinct molecules', + solver +} satisfies Solution; diff --git a/2015/19/part-2.ts b/2015/19/part-2.ts new file mode 100644 index 0000000..4658fc0 --- /dev/null +++ b/2015/19/part-2.ts @@ -0,0 +1,43 @@ +import { parseInput } from './helpers/parse-input.js'; + +/* ========================================================================== */ + +/** + * My first solution was to take the letter 'e' as start and basically keep + * applying the replacements like in part 1 till the medicine molecule has been + * created. I was afraid it would create too many paths to follow and this soon + * turned out to be true. + * + * My second solution was to work backwards, start from the medicine molecule + * and reverse the replacements till it yields 'e' as a result. This seemed like + * a valid strategy except it would never get to 'e'. At some point it would + * reach a molecule which no longer contained parts which could be replaced. + * + * The current solution is not my own, it is based on a solution / explanation + * found on Reddit. It works like magic. + * + * @see {@link https://www.reddit.com/r/adventofcode/comments/3xflz8/comment/cy4h7ji/} + */ +async function solver(input: string): Promise { + const { medicineMolecule } = parseInput(input); + + // A symbol starts with a capital letter. + const symbolCount = medicineMolecule.match(/[A-Z]/g).length; + + // For each Rn is there is matching occurrence of Ar. When the number of + // times Rn appears in the molecule is know, we also know the number of + // times Ar should be present in the molecule. + const rnCount = medicineMolecule.match(/Rn/g).length; + + // Count then number of times the letter Y is present in the molecule. + const yCount = medicineMolecule.match(/Y/g).length; + + return (symbolCount - (rnCount * 2) - (2 * yCount)) - 1; +} + +/* ========================================================================== */ + +export default { + prompt: 'Minimum steps required to create medicine molecule', + solver +} satisfies Solution;