# Advent of Code 2022: Day 3

A TypeScript solution for the Advent of Code 2022, Day 3 puzzle: playing with sets and intersections.

Today’s puzzle deals heavily with sets and intersections. We’re asked to find a character that appears somewhere in one or more strings, then combine results by mapping the repeated characters with a simple scoring system.

JavaScript gives you a Set object to help with this, but lacks an `intersection` function, so we’ll have to write our own.

The puzzle input looks something like this.

```vJrwpWtwJgWrhcsFMMfFFhFp
jqHRNqRjqzjGDLGLrsFMfFZSrLrFZsSL
PmmdzqPrVvPwwTWBwg
wMqvLMZHhHMvwLHjbvcjnnSBnvTQFn
ttgJtRGJQctTZtZT
CrZsJsPPZsGzwwsLwLmpwMDw
```

First we split the data into individual rucksacks (one per line).

```const rucksacks: string[] = puzzleInput.split('\n');
```

Now define an `intersection` function to find the overlapping characters between two or more strings.

```function intersection(...xs: string[]): string[] {
// Create sets of characters for each string.
// Note: dedupes all characters within each string.
const sets = xs
.map(x => new Set<string>(x.split('')));

// Then reduce over the sets, using filter()
// to accumulate the overlapping values
return sets.slice(1).reduce(
(overlap, set) => overlap.filter(x => set.has(x)),
Array.from(sets[0].values())
);
}
```

For our final puzzle answers, we also need a lookup table of characters (`a-z` and `A-Z`) that maps them to a priority number. We can build it succinctly with clever usage of `map()` and its index parameter.

```const alphabet = 'abcdefghijklmnopqrstuvwxyz';
const priorityMap = new Map([
// lowercase letters 1 through 26
...alphabet
.split('')
.map((char, i) => [char, i+1] as const),
// uppercase letters 27 through 52
...alphabet
.toUpperCase()
.split('')
.map((char, i) => [char, i+27] as const)
]);
```

The stage is now set 🙃 so we can finally get to the actual data crunching.

For part 1, split each rucksack into left and right equal-sized compartments, then use `intersection` to figure out the mistakes where the same characters are in both compartments. Then we can calculate the total priority of the mistakes by using the `priorityMap` lookup table and `reduce` for summation.

```const part1 = rucksacks
.flatMap(rucksack => {
const middle = rucksack.length / 2;
const left = rucksack.slice(0, middle);
const right = rucksack.slice(middle);
return intersection(left, right)
})
.map(mistake => priorityMap.get(mistake))
.reduce((sum, p) => sum + p, 0);
```

For part 2, we instead need to find the `intersection` between groups of 3 rucksacks, which is the badge for each team of 3 elves. We can use the same `priorityMap` from part 1 to sum up the priorities of the badges for the answer.

```const part2 = rucksacks
// Group into bunches of 3 rucksacks by reducing,
// creating a new group whenever index mod 3 is 0
.reduce(
(acc, curr, i) => {
if (i % 3 === 0) { acc.push([]); }
const prev = acc.length - 1;
acc[prev].push(curr);
return acc;
},
[] as string[][]
)
// Find the badge (i.e. intersection) of each triplet,
// then look up the priorities and total them up
.flatMap(triplet => intersection(...triplet))
.reduce((sum, p) => sum + p, 0);
```

## Final Solution

```const rucksacks: string[] = puzzleInput.split('\n');

function intersection(...xs: string[]): string[] {
// Create sets of characters for each string.
// Note: dedupes all characters within each string.
const sets = xs
.map(x => new Set<string>(x.split('')));

// Then reduce over the sets, using filter()
// to accumulate the overlapping values
return sets.slice(1).reduce(
(overlap, set) => overlap.filter(x => set.has(x)),
Array.from(sets[0].values())
);
}

const alphabet = 'abcdefghijklmnopqrstuvwxyz';
const priorityMap = new Map([
// lowercase letters 1 through 26
...alphabet
.split('')
.map((char, i) => [char, i+1] as const),
// uppercase letters 27 through 52
...alphabet
.toUpperCase()
.split('')
.map((char, i) => [char, i+27] as const)
]);

const part1 = rucksacks
.flatMap(rucksack => {
const middle = rucksack.length / 2;
const left = rucksack.slice(0, middle);
const right = rucksack.slice(middle);
return intersection(left, right)
})
.map(mistake => priorityMap.get(mistake))
.reduce((sum, p) => sum + p, 0);

const part2 = rucksacks
// Group into bunches of 3 rucksacks by reducing,
// creating a new group whenever index mod 3 is 0
.reduce(
(acc, curr, i) => {
if (i % 3 === 0) { acc.push([]); }
const prev = acc.length - 1;
acc[prev].push(curr);
return acc;
},
[] as string[][]
)
// Find the badge (i.e. intersection) of each triplet,
// then look up the priorities and total them up
.flatMap(triplet => intersection(...triplet))
```Part 1: 7908