Advent of Code 2023 Day 3 Got it! Geez that was hard. This was my winning algo without any cleanup after the first successful submission:
import fs from 'fs'
// fs.readFile("testinput.txt", 'utf-8', (err, data) => {
fs.readFile("puzzleinput.txt", 'utf-8', (err, data) => {
const lines = data.split('\n')
const lineLength = lines.length
const compass = [-lineLength - 1, -lineLength, -lineLength + 1, -1, +1, lineLength -1, lineLength, lineLength+1]
let result = 0
let gearRatios = 0
data = data.replace(/\s+/g, '')
const parts = Array.from(data.matchAll(/\d+/g))
const symbols = Array.from(data.matchAll(/[^0-9.]/g))
const digitIndexes: Map<number, string> = new Map() // map of indexes corresponding to the value of an entire part number with the convention `${index-of-first-char}-${value}`
const partSet: Map<string, number> = new Map() // a set of every unique part number, which can be split apart to get a value
const gearMap: Map<number, Set<string>> = new Map() // a map of every gear to the number of all parts it is adjacent to
function createPartId(partIndex: number, partValue: string){
return `${partIndex}-${partValue}`
}
// Iterate through every part match
// For each digit it has, record it in the digitIndexes map with it's unique partId
for (let i = 0; i < parts.length; i++){
const part = parts[i]
for (let offset = 0; offset <part[0].length; offset++){
const newIndex = part.index + offset
const partId = createPartId(part.index, part[0])
digitIndexes.set(newIndex, partId)
}
}
// Iterate through every symbol match
for (let s = 0; s < symbols.length; s++){
const symbol= symbols[s]
// For every symbol match, iterate through all 8 potential neighboring squares
for (const direction of compass){
const searchIndex = symbol.index + direction
if (searchIndex < 0 && searchIndex >= data.length) continue // search index is out of bounds
if (digitIndexes.has(searchIndex)){
// We have a match! We have captured a relationship between a symbol and the digit of a part
const partId = digitIndexes.get(searchIndex)!
partSet.set(partId, parseInt(partId.split("-")[1]))
if (symbol[0] === "*"){
gearMap.set(symbol.index,
(gearMap.get(symbol.index) ?? new Set()).add(partId)
)
}
}
}
}
console.log({partSet})
console.log("Parts number answer 1 is", [...partSet].reduce((acc, current) => {
const value = current[1]
return acc + value
}, 0))
console.log({gearMap})
for (let [_, value] of gearMap){
if (value.size === 2){
let ratio = 1
for (const partId of value){
ratio *= partSet.get(partId) ?? 1
}
gearRatios += ratio
}
}
console.log(gearRatios)
})
/// Gotchas
// I had the indexes I was iterating through and then I had these regex indexes, and the combination really messed me upLet me clean it up a bit:
Things I learned
1) A for...of loop can be used to iterate through a Set, but not for...in
const someSet = new Set(1, 2, 3)
for (const val of someSet) console.log(val)
// 1
// 2
// 3
for (const in of someSet) console.log(val)
// undefined <- No error though 🤔2) for...of also works for maps
const someMap = new Map(["Shrek", 4], ["Shrek2", 2.4], ["Shrek3", 1])
for (const val of someMap) console.log(val)
// ["Shrek", 4]
// ["Shrek2", 2.4]
// ["Shrek3", 1]3) Variable naming really matters
The second biggest issue I ran into here was that I had two index values that pertained to the index a regex match found in the larger string, and I also had two index values that pertained to which match I was on as I looped through those matches.
This was really confusing and hard to catch because they either accidentally shadowed each other by both being named partIndex OR their names contained no meaningfully different information to me, like partIndex and partMatchIndex.
Maybe what I learned was that index isn’t a very variable name? I’m still not totally sure what names I should use in some of this algo.
4) Data structures containing other data structures are powerful but dangerous The hardest part about this particular challenge seems to be that it forces you to either
- Nest data structures within datastrctures or
- Reference one datastructure from another
And to be totally honest, I had a lot of trouble figuring out how to design these relationships so they did what I wanted. I did a lot of flailing around and changing data structures that aren’t present here.
If I’m honest, I also made some mistakes reading the question, which had devastating consequences to my data structure design. But I think that the question itself had some major hints as to how to design the data structures and their relationships with one another.