LD
Change your colour scheme

Advent of Code 2023: Day Three

Published:

As before, this post contains spoilers. You can follow all of the Advent of Code posts using the Advent of Code tag, and the code is available on Git.

Thanks Day Three, I hate it.

Part One

For part one, we had to take a schematic of an engine, with symbols for components, and numbers for “part numbers”, and then find all of the part numbers adjacent to a component, and sum them together.

I really struggled to get the right data structure for this while still being able to look it up properly. In the end, I matched every integer, stuck it in a 2D array, at each index that int would occupy (e.g. ‘245’ starting at position 3, line 3 would be in indexes [3][3], [3][4], and [3][5].

Then I found each symbol, checked the adjacent spots for numbers, removed the duplicates, and summed them. The code is horrible.

export class Engine {
constructor(private readonly parts: number[][], private readonly schematic: string[][]) {
}

public static create(input: string): Engine {
const lines = input.split('\n').filter(Boolean);
const partRegex = /\d+/g;
const symbolRegex = /[^a-zA-Z\d\.]/g;

const parts : number[][] = new Array(lines.length).fill(0).map(() => new Array(lines[0].length));
const symbols: string[][] = new Array(lines.length).fill(0).map(() => new Array(lines[0].length));

lines.forEach((line, lineNumber) => {
matchAllAndThen(line, partRegex, (match, index) => {
const parsedNumber = parseInt(match, 10);
for (let i = index; i < index + match.length; i++) {
parts[lineNumber][i] = parsedNumber;
}
});
});

lines.forEach((line, lineNumber) => {
matchAllAndThen(line, symbolRegex, (match, index) => symbols[lineNumber][index] = match);
});

return new Engine(parts, symbols);
}

public sumPartNumbers(): number {
const partsList = this.schematic.flatMap((row, rowIndex) =>
row.map((symbol, index) => {
if (!symbol) return symbol;

const partIndex = [
[rowIndex - 1, index - 1],
[rowIndex - 1, index],
[rowIndex - 1, index + 1],
[rowIndex, index - 1],
[rowIndex, index + 1],
[rowIndex + 1, index - 1],
[rowIndex + 1, index],
[rowIndex + 1, index + 1]
];
return Array.from(new Set(partIndex.filter(([rowNum, col]) => rowNum >= 0 && rowNum <= this.schematic.length && index >= 0 && index <= row.length)
.map(([rowNum, column]) => {
return this.parts[rowNum][column]
}).filter(Boolean)))
.reduce((total, val) => total + val,0);
})
) as number[];
return partsList.reduce((total, partNumber) => total + partNumber, 0);
}

But whatever, it works.

Part Two

This was easier - now we just need to find * symbols with exactly two adjacent numbers. I added a flag to my create function that only matched * symbols, and then filtered the list of part numbers to ones where length === 2. Finally, I reduced the list down and summed the “ratios”:

public gearRatioSums(): number {
return this.schematic.flatMap((row, rowIndex) =>
row
.map((symbol, index) => {
const partIndex = [
[rowIndex - 1, index - 1],
[rowIndex - 1, index],
[rowIndex - 1, index + 1],
[rowIndex, index - 1],
[rowIndex, index + 1],
[rowIndex + 1, index - 1],
[rowIndex + 1, index],
[rowIndex + 1, index + 1]
];
return Array.from(new Set(partIndex.filter(([rowNum, col]) => rowNum >= 0 && rowNum <= this.schematic.length && index >= 0 && index <= row.length)
.map(([rowNum, column]) => {
return this.parts[rowNum][column]
}).filter(Boolean)))
})
).filter(list => list.length === 2)
.reduce((total, [a, b]) => {
return total + (a * b)
}, 0);
}

Today took me way longer than I’d have expected. Feels like this year is surprisingly hard 😅

Tags:

About the author

My face

I'm Lewis Dale, a software engineer and web developer based in the UK. I write about writing software, silly projects, and cycling. A lot of cycling. Too much, maybe.

Responses