Advent of Code 2022 - Day 3
The elf in charge of packing for the journey failed to correctly follow instructions, now it's up to us to help locate & prioritize items.
Input
The input for this problem takes the form of a series of newline-separated strings, each containing a set of characters.
vJrwpWtwJgWrhcsFMMfFFhFp
jqHRNqRjqzjGDLGLrsFMfFZSrLrFZsSL
PmmdzqPrVvPwwTWBwg
wMqvLMZHhHMvwLHjbvcjnnSBnvTQFn
ttgJtRGJQctTZtZT
CrZsJsPPZsGzwwsLwLmpwMDw
As our processing involves us processing each line, the input parsing for this problem can just use the normal input loading snippet I use as the basis for most problems.
Solving the problem - part one
For part one of today's problem, we need to split each rucksack's contents into the two compartments, and determine the priorities of the items in common between each rucksack.
First, I define a simple utility function to determine the priority of an item, as I'll need to make use of this later. To determine the priority, I construct a string containing the items in order of priority and then take the index of the item in the string and add one (as the indices will be zero-indexed, but priorities start at one).
Next, I define a utility function for getting the common items between two compartments. I do this by converting both to sets and determining the intersection of the two, then converting that resulting set back into an array.
With these utility functions, I can start to piece together a solution. First, I take the input and break each rucksack into it's two compartments, by splitting each line in half.
I then take those compartments and pass them to the getCommonItems
utility function I defined to get the common items.
Next, I take each common item in each rucksack, get its priority, and then sum those priorities.
Finally, I take those resulting priorities and sum all of them to get the resulting overall priority.
Solving the problem - part two
In part two, the change to the problem is the sets we are performing operations on: rather than taking each line of the input, splitting it in half, and working on the two resulting sets, in part two we need to take groups of three lines from the input and work on those three sets instead.
To do this, I largely just copy/paste the same code and tweak the set usage & set generation - I use Array.chunkBySize
to split the input into groups of 3 lines, and perform a fold of set intersections over the resulting sets in order to get the resulting set.
Cleaning up
For cleaning up this solution, there's really just one main thing that needs fixing: code duplication. As both problems deal with the same thing and just deal with different ways of generating the sets, I can reduce the code duplication by moving both to a single function which takes in a function that splits the input into the necessary groups of strings.
Then, with this function in place, I can convert the splitting logic from part one and two into functions that can be plugged into this, and then replace the generation of the answers for part one and two with invoking this function, giving a final simplified solution.
let partAGroups (input: string[]) : string[][] =
input
|> Array.map (fun line -> [| line[.. line.Length / 2 - 1]; line[line.Length / 2 ..] |])
let partBGroups (input: string []) : string [] [] = input |> Array.chunkBySize 3
let partA = inputData |> calculatePriority partAGroups
let partB = inputData |> calculatePriority partBGroups