--- title: "Advent of Code 2023: Day Three" date: 2023-12-03T12:23:59 slug: advent-of-code-2023-day-three tags: [advent-of-code-2023] --- As before, this post contains spoilers. You can follow all of the Advent of Code posts using the [Advent of Code tag](https://lewisdale.dev/post/tag/advent-of-code-2023/), and the code is available on [Git](https://git.lewisdale.dev/lewis/advent-of-code-2023). 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_. ```javascript 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": ```javascript 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 😅