Skip to content

Instantly share code, notes, and snippets.

@ZeikJT
Last active December 13, 2024 08:23
Show Gist options
  • Save ZeikJT/ed02b7d569f3738dc8e1e63fbc6b6064 to your computer and use it in GitHub Desktop.
Save ZeikJT/ed02b7d569f3738dc8e1e63fbc6b6064 to your computer and use it in GitHub Desktop.
Advent of Code 2024
function adventOfCode2024_Day01_Part1(str) {
const numsLeft = []
const numsRight = []
str.trim().split('\n').forEach((line) => {
const [numLeft,numRight] = line.split(/\s+/)
numsLeft.push(Number(numLeft))
numsRight.push(Number(numRight))
})
const numSort = (a,b) => a < b ? -1 : 1
numsLeft.sort(numSort)
numsRight.sort(numSort)
return numsLeft.reduce((totalDiff,_,i) => totalDiff + Math.abs(numsLeft[i] - numsRight[i]), 0)
}
function adventOfCode2024_Day01_Part2(str) {
const numsLeft = []
const numsRight = []
str.trim().split('\n').forEach((line) => {
const [numLeft,numRight] = line.split(/\s+/)
numsLeft.push(Number(numLeft))
numsRight.push(Number(numRight))
})
const numSort = (a,b) => a < b ? -1 : 1
numsLeft.sort(numSort)
numsRight.sort(numSort)
return numsLeft.reduce((totalDiff,val) => totalDiff + (numsRight.filter((num) => val === num).length * val), 0)
}
function adventOfCode2024_Day02_Part1(str) {
const lines = str.trim().split('\n').map((line) => line.split(' ').map((str) => Number(str)))
return lines.reduce((totalSafe, nums) => {
let dir = 0
for (let i = 0; i < nums.length - 1; i++) {
const diff = nums[i] - nums[i + 1]
const curDir = Math.sign(diff)
const curDiff = Math.abs(diff)
if ((dir !== 0 && dir !== curDir) || (curDiff < 1 || curDiff > 3)) {
return totalSafe
}
dir = curDir
}
return 1 + totalSafe
}, 0)
}
function adventOfCode2024_Day02_Part2(str) {
const lines = str.trim().split('\n').map((line) => line.split(' ').map((str) => Number(str)))
function cloneAndRemoveIndex(arr, i) {
return arr.filter((_, idx) => idx !== i)
}
function checkNums(nums) {
let dir = 0
for (let i = 0; i < nums.length - 1; i++) {
const diff = nums[i] - nums[i + 1]
const curDir = Math.sign(diff)
const curDiff = Math.abs(diff)
if ((dir !== 0 && dir !== curDir) || (curDiff < 1 || curDiff > 3)) {
return i
}
dir = curDir
}
return -1
}
return lines.reduce((totalSafe, nums) => {
const result = checkNums(nums)
if (result !== -1) {
if ((result !== 1 || checkNums(cloneAndRemoveIndex(nums, 0)) !== -1) &&
checkNums(cloneAndRemoveIndex(nums, result)) !== -1 &&
checkNums(cloneAndRemoveIndex(nums, result + 1)) !== -1) {
return totalSafe
}
}
return 1 + totalSafe
}, 0)
}
function adventOfCode2024_Day03_Part1(str) {
let total = 0
for (const {groups: {n1, n2}} of str.trim().matchAll(/mul\((?<n1>\d+),(?<n2>\d+)\)/g)) {
total += Number(n1) * Number(n2)
}
return total
}
function adventOfCode2024_Day03_Part2(str) {
let total = 0
let enabled = true
for (const {groups: {n1, n2, enable}} of str.trim().matchAll(/mul\((?<n1>\d+),(?<n2>\d+)\)|(?<enable>do(?:n\'t)?)\(\)/g)) {
if (enable) {
enabled = enable === 'do'
} else if (enabled) {
total += Number(n1) * Number(n2)
}
}
return total
}
function adventOfCode2024_Day04_Part1(str) {
const find = 'XMAS'
const findR = (() => {
const letters = find.split('')
letters.reverse()
return letters.join('')
})()
const findWords = [find, findR]
let total = 0
const lines = str.trim().split('\n')
const lineLen = lines[0].length
const linesVert = lines[0].split('').map(() => [])
const linesSW = []
const linesSE = []
// Vert, SW and SE top
for (let i = 0; i < lineLen; i++) {
for (const line of lines) {
linesVert[i].push(line[i])
}
if (i >= find.length - 1) {
const newSELine = []
const newSWLine = []
for (let j = i, l = 0; j >= 0, l < lines.length; j--, l++) {
newSWLine.push(lines[l][j])
newSELine.push(lines[l][lineLen - j - 1])
}
linesSW.push(newSWLine.join(''))
linesSE.push(newSELine.join(''))
}
linesVert[i] = linesVert[i].join('')
}
// SW and SE sides
for (let j = 1; j <= lines.length - 4; j++) {
const newSELine = []
const newSWLine = []
for (let i = 0, l = j; i < lineLen, l < lines.length; i++, l++) {
newSELine.push(lines[l][i])
newSWLine.push(lines[l][lineLen - i - 1])
}
linesSW.push(newSWLine.join(''))
linesSE.push(newSELine.join(''))
}
for (const linesToCheck of [lines, linesVert, linesSW, linesSE]) {
for (const line of linesToCheck) {
total += [...line.matchAll(new RegExp(find, 'g')), ...line.matchAll(new RegExp(findR, 'g'))].length
}
}
return total
}
function adventOfCode2024_Day04_Part2(str) {
const find = 'MAS'
const l1 = find[0]
const l2 = find[1]
const l3 = find[2]
const possibilities = [
`${l1}${l1}${l3}${l3}`,
`${l1}${l3}${l3}${l1}`,
`${l3}${l3}${l1}${l1}`,
`${l3}${l1}${l1}${l3}`,
]
const lines = str.trim().split('\n')
const lineLen = lines[0].length
let total = 0
for (let l = 1; l < lines.length - 1; l++) {
for (let i = 1; i < lineLen - 1; i++) {
if (lines[l][i] === l2 && possibilities.includes(`${lines[l-1][i-1]}${lines[l-1][i+1]}${lines[l+1][i+1]}${lines[l+1][i-1]}`)) {
total += 1
}
}
}
return total
}
function adventOfCode2024_Day05_Part1(str) {
const sections = str.trim().split('\n\n')
const befores = sections[0].split('\n').map((line) => line.split('|').map((str) => Number(str)))
const lines = sections[1].split('\n').map((line) => line.split(',').map((str) => Number(str)))
const afterBeforeMap = new Map()
for (const [before,after] of befores) {
if (!afterBeforeMap.has(before)) {
afterBeforeMap.set(before, [after])
} else {
afterBeforeMap.set(before, [...afterBeforeMap.get(before), after])
}
}
let total = 0
outer: for (const line of lines) {
const seen = new Set()
for (const num of line) {
if (afterBeforeMap.get(num)?.some((after) => seen.has(after)) ?? false) {
continue outer
}
seen.add(num)
}
total += line[Math.floor(line.length / 2)]
}
return total
}
function adventOfCode2024_Day05_Part2(str) {
const sections = str.trim().split('\n\n')
const befores = sections[0].split('\n').map((line) => line.split('|').map((str) => Number(str)))
const lines = sections[1].split('\n').map((line) => line.split(',').map((str) => Number(str)))
const beforeAfterMap = new Map()
for (const [before, after] of befores) {
if (!beforeAfterMap.has(after)) {
beforeAfterMap.set(after, [before])
} else {
beforeAfterMap.set(after, [...beforeAfterMap.get(after), before])
}
}
let total = 0
for (const line of lines) {
let outOfOrder = false
const resortedLine = []
for (const num of line) {
const insertIndex = resortedLine.findIndex((seenNum) => beforeAfterMap.get(seenNum)?.some((before) => num === before))
if (insertIndex !== -1) {
resortedLine.splice(insertIndex, 0, num)
outOfOrder = true
} else {
resortedLine.push(num)
}
}
if (outOfOrder) {
total += resortedLine[Math.floor(resortedLine.length / 2)]
}
}
return total
}
function adventOfCode2024_Day06_Part1(str) {
const map = str.trim().split('\n')
const width = map[0].length
const height = map.length
const walked = new Set()
const pos = {x: -1, y: -1}
const dir = {x: 0, y: -1}
for (const [y, row] of map.entries()) {
const x = row.indexOf('^')
if (x !== -1) {
pos.x = x
pos.y = y
//map[y] = row.replace('^', '.')
break
}
}
while (pos.x >= 0 && pos.x < width && pos.y >= 0 && pos.y < height) {
/* Debug
map[pos.y] = map[pos.y].substring(0, pos.x) + 'X' + map[pos.y].substring(pos.x + 1)
document.body.children[0].textContent = map.join('\n')
//*/
walked.add(`${pos.y},${pos.x}`)
while (map[pos.y + dir.y]?.[pos.x + dir.x] === '#') {
const {x,y} = dir
dir.x = y * -1
dir.y = x
}
pos.x += dir.x
pos.y += dir.y
}
return walked.size
}
function adventOfCode2024_Day06_Part2(str) {
const defaultMap = str.trim().split('\n')
const width = defaultMap[0].length
const height = defaultMap.length
const startPos = {x: -1, y: -1}
const dirs = [
{x:1,y:0},
{x:-1,y:0},
{x:0,y:1},
{x:0,y:-1},
]
for (const [y, row] of defaultMap.entries()) {
const x = row.indexOf('^')
if (x !== -1) {
startPos.x = x
startPos.y = y
break
}
}
possibleObstacles = new Set()
function isInMap(pos) {
return pos.x >= 0 && pos.x < width && pos.y >= 0 && pos.y < height
}
function vec2ToStr(vec2) {
return `${vec2.x},${vec2.y}`
}
function walk(map = defaultMap, pos = startPos, dir = {x: 0, y: -1}, walked = new Set(), turns = new Map(dirs.map((vec2) => [vec2ToStr(vec2), new Set()])), addObstacles = true) {
while (isInMap(pos)) {
walked.add(vec2ToStr(pos))
while (map[pos.y + dir.y]?.[pos.x + dir.x] === '#') {
const turned = turns.get(vec2ToStr(dir))
if (turned.has(vec2ToStr(pos))) {
return true // looped
}
turned.add(vec2ToStr(pos))
const {x,y} = dir
dir.x = y * -1
dir.y = x
}
const dest = {x: pos.x + dir.x, y: pos.y + dir.y}
if (addObstacles && !walked.has(vec2ToStr(dest)) && isInMap(dest)) {
const speculativeMap = map.with(dest.y, map[dest.y].slice(0, dest.x) + '#' + map[dest.y].slice(dest.x + 1))
const speculativeTurns = new Map()
for (const [turn, positions] of turns.entries()) {
speculativeTurns.set(turn, new Set(positions))
}
if (walk(speculativeMap, Object.assign({}, pos), Object.assign({}, dir), new Set(walked), speculativeTurns, false)) {
possibleObstacles.add(vec2ToStr(dest))
}
}
pos.x += dir.x
pos.y += dir.y
}
return false // didn't loop
}
if (walk()) {
throw new Error(`main map walking shouldn't loop`)
}
return possibleObstacles
}
function adventOfCode2024_Day07_Part1(str) {
const data = str.trim().split('\n').map((line) => line.split(/:? /).map(Number))
let total = 0
for (const [testValue, ...nums] of data) {
const max = Math.pow(2, nums.length - 1)
for (let bits = 0; bits < max; bits++) {
let calc = nums[0]
const bitStr = bits.toString(2).padStart(nums.length - 1, '0')
for (let i = 1; i < nums.length; i++) {
if (bitStr[i - 1] === '1') {
calc *= nums[i]
} else {
calc += nums[i]
}
}
if (calc === testValue) {
total += testValue
break
}
}
}
return total
}
function adventOfCode2024_Day07_Part2(str) {
const data = str.trim().split('\n').map((line) => line.split(/:? /).map(Number))
const ops = ['+', '*', '||']
// Adapted from https://stackoverflow.com/a/75016688
function createCombinations(ops, size) {
const combinations = []
function generateCombinations(ops, combination) {
if (combination.length === size) {
combinations.push(combination)
} else {
for (let i = 0; i < ops.length; i++) {
const newCombination = [...combination, ops[i]]
generateCombinations(ops, newCombination)
}
}
}
generateCombinations(ops, [])
return combinations
}
let total = 0
for (const [testValue, ...nums] of data) {
const operations = createCombinations(ops, nums.length - 1)
for (const operation of operations) {
let calc = nums[0]
for (let i = 1; i < nums.length; i++) {
switch (operation[i - 1]) {
case '+':
calc += nums[i]
break
case '*':
calc *= nums[i]
break
case '||':
calc = Number(`${calc}${nums[i]}`)
break
}
}
if (calc === testValue) {
total += testValue
break
}
}
}
return total
}
function adventOfCode2024_Day08_Part1(str) {
const map = str.trim().split('\n')
const width = map[0].length
const height = map.length
const antennae = new Map()
const antinodes = new Set()
for (const [y, line] of map.entries()) {
for (const [x, char] of line.split('').entries()) {
const pos = [x,y]
if (char !== '.') {
if (!antennae.has(char)) {
antennae.set(char, [pos])
} else {
const a = antennae.get(char)
for (const prev of a) {
const dist = [pos[0] - prev[0], pos[1] - prev[1]]
const antis = [
[pos[0] + dist[0], pos[1] + dist[1]],
[prev[0] - dist[0], prev[1] - dist[1]],
]
for (const anti of antis) {
if (anti[0] >= 0 && anti[0] < width && anti[1] >= 0 && anti[1] < height) {
antinodes.add(`${anti[0]},${anti[1]}`)
}
}
}
a.push(pos)
}
}
}
}
return antinodes.size
}
function adventOfCode2024_Day08_Part2(str) {
const map = str.trim().split('\n')
const width = map[0].length
const height = map.length
const antennae = new Map()
const antinodes = new Set()
for (const [y, line] of map.entries()) {
for (const [x, char] of line.split('').entries()) {
const pos = [x,y]
if (char !== '.') {
if (!antennae.has(char)) {
antennae.set(char, [pos])
} else {
const a = antennae.get(char)
for (const prev of a) {
const dist = [pos[0] - prev[0], pos[1] - prev[1]]
const antis = [
[pos.slice(), dist],
[prev.slice(), [dist[0] * -1, dist[1] * -1]]
]
for (const [anti, dist] of antis) {
while (anti[0] >= 0 && anti[0] < width && anti[1] >= 0 && anti[1] < height) {
antinodes.add(`${anti[0]},${anti[1]}`)
anti[0] += dist[0]
anti[1] += dist[1]
}
}
}
a.push(pos)
}
}
}
}
return antinodes.size
}
function adventOfCode2024_Day09_Part1(str) {
let id = 0
let file = true
const layout = []
for (const char of str.trim()) {
const num = Number(char)
layout.push(...new Array(num).fill(file ? id++ : '.'))
file = !file
}
let j = layout.length - 1
let checksum = 0
for (let i = 0; i < layout.length; i++) {
if (layout[i] === '.') {
while (layout[j] === '.') {
j--
}
if (i < j) {
layout[i] = layout[j]
layout[j] = '.'
}
}
if (layout[i] !== '.') {
checksum += i * layout[i]
}
if (i >= j) {
break
}
}
return checksum
}
function adventOfCode2024_Day09_Part2(str) {
let id = 0
let file = true
const layout = []
for (const char of str.trim()) {
const num = Number(char)
layout.push(file ? [{id: id++, num}] : num)
file = !file
}
let data = layout.length - 1 & 1 == 1 ? layout.length - 2 : layout.length - 1
let empty = 1
for (; data > empty; data -= 2) {
for (let e = empty; e < data; e += 2) {
const size = layout[data][0].num
if (layout[e] >= size) {
layout[e] -= size
layout[data - 1] += size
layout[e - 1].push(layout[data].shift())
while (layout[empty] === 0) {
empty += 2
}
break
}
}
}
let checksum = 0
let index = 0
for (let i = 0; i < layout.length; i++) {
if (i & 1 === 1) {
index += layout[i]
} else {
for (const {id,num} of layout[i]) {
for (let n = 0; n < num; n++) {
checksum += (index + n) * id
}
index += num
}
}
}
return checksum
}
function adventOfCode2024_Day10_Part1(str) {
const map = str.trim().split('\n').map((line) => line.split('').map(Number))
const height = map.length
const width = map[0].length
let score = 0
const neighborLocations = [
[-1, 0],
[ 0, -1],
[ 0, 1],
[ 1, 0],
]
function followNeighborPath(py, px, num, nines = new Set) {
for (const [oy, ox] of neighborLocations) {
const y = py + oy
const x = px + ox
if (map[y]?.[x] === num) {
if (num === 9) {
nines.add(`${y},${x}`)
} else {
followNeighborPath(y, x, num + 1, nines)
}
}
}
return nines
}
let nines = 0
for (let y = 0; y < height; y++) {
for (let x = 0; x < width; x++) {
if (map[y][x] === 0) {
nines += followNeighborPath(y, x, 1).size
}
}
}
return nines
}
function adventOfCode2024_Day10_Part2(str) {
const map = str.trim().split('\n').map((line) => line.split('').map(Number))
const height = map.length
const width = map[0].length
let score = 0
const neighborLocations = [
[-1, 0],
[ 0, -1],
[ 0, 1],
[ 1, 0],
]
function followNeighborPath(py, px, num) {
let nines = 0
for (const [oy, ox] of neighborLocations) {
const y = py + oy
const x = px + ox
if (map[y]?.[x] === num) {
if (num === 9) {
nines++
} else {
nines += followNeighborPath(y, x, num + 1)
}
}
}
return nines
}
let nines = 0
for (let y = 0; y < height; y++) {
for (let x = 0; x < width; x++) {
if (map[y][x] === 0) {
nines += followNeighborPath(y, x, 1)
}
}
}
return nines
}
function adventOfCode2024_Day11_Part1(str) {
let nums = str.trim().split(' ').map(Number)
for (let i = 0; i < 25; i++) {
let newNums = []
for (const num of nums) {
if (num === 0) {
newNums.push(1)
} else if ((String(num).length & 1) === 0) {
const strNum = String(num)
const half = Math.floor(strNum.length / 2)
newNums.push(Number(strNum.slice(0, half)), Number(strNum.slice(half)))
} else {
newNums.push(num * 2024)
}
}
nums = newNums
}
return nums.length
}
function adventOfCode2024_Day11_Part2(str) {
let nums = str.trim().split(' ').map(Number)
const memo = new Map()
let numCount = 0
function calc(num, iters) {
if (iters === 0) {
return 1
}
const id = `${num};${iters}`
if (memo.has(id)) {
return memo.get(id)
}
let res
if (num === 0) {
res = calc(1, iters - 1)
} else if ((String(num).length & 1) === 0) {
const strNum = String(num)
const half = Math.floor(strNum.length / 2)
res = calc(Number(strNum.slice(0, half)), iters - 1) + calc(Number(strNum.slice(half)), iters - 1)
} else {
res = calc(num * 2024, iters - 1)
}
memo.set(id, res)
return res
}
for (const num of nums) {
numCount += calc(num, 75)
}
return numCount
}
function adventOfCode2024_Day13_Part1(str) {
const data = str.trim().split('\n\n').map((section) => section.split('\n').map((line) => {
const {x,y} = /X[+=](?<x>\d+), Y[+=](?<y>\d+)/.exec(line).groups
return {x: Number(x), y: Number(y)}
}))
let tokens = 0
for (const [a,b,total] of data) {
const bmax = Math.floor(total.x / b.x)
let lowestTokens = 0
for (let bi = bmax; bi >= 0; bi--) {
if (((total.x - (bi * b.x)) % a.x) === 0) {
const afill = (total.x - (bi * b.x)) / a.x
if (((bi * b.y) + (afill * a.y)) === total.y) {
const cost = bi + (afill * 3)
if (cost < lowestTokens || !lowestTokens) {
lowestTokens = cost
}
}
}
}
tokens += lowestTokens
}
return tokens
}
// Didn't read the example code close enough to realize loops don't have a be a single 4 anchor shape, could be two 3 anchor shapes or more.
function adventOfCode2024_Day06_Part2(str) {
function indexOfOr(str, find, start, orDefault) {
const res = str.indexOf(find, start)
return res === -1 ? orDefault : res
}
const map = str.trim().split('\n')
const width = map[0].length
const height = map.length
const vmap = (() => {
const arr = new Array(width).fill(0).map(() => [''])
for (let c = 0; c < width; c++) {
for (let r = 0; r < height; r++) {
arr[c] += map[r][c]
}
}
return arr
})()
const min = 4
const possibleLoops = []
for (let r = 0; r <= height - min; r++) {
if (r === 6) debugger
for (let c = 0; c <= width - min; c++) {
const maxWidthT = indexOfOr(map[r + 1], '#', c + 1, width - 1)
const maxHeightT = indexOfOr(vmap[c + 1], '#', r + 1, height - 1)
for (let mr = r + min - 1; mr <= maxHeightT; mr++) {
const maxWidthB = indexOfOr(map[mr - 1], '#', c + 1, width - 1)
for (let mc = c + min - 1; mc <= maxWidthT && mc <= maxWidthB; mc++) {
const maxHeightB = indexOfOr(vmap[mc -1], '#', r + 1, height - 1)
if (maxHeightB < mr) continue // obstacle
let cornerCount = 0
const types = []
if (map[r][c + 1] === '#') {types.push('TL');cornerCount++} // top left
if (map[mr - 1][c] === '#') {types.push('BL');cornerCount++} // bottom left
if (map[r + 1][mc] === '#') {types.push('TR');cornerCount++} // top right
if (map[mr][mc - 1] === '#') {types.push('BR');cornerCount++} // bottom right
if (cornerCount === 4) {
throw new Error('pre-existing loops, not written to handle')
}
if (cornerCount === 3) {
possibleLoops.push({c, r, mc, mr})
console.log({c, r, mc, mr}, types)
console.log(map.slice(r, mr + 1).map((str) => str.slice(c, mc + 1)).join('\n'))
}
}
}
}
}
return possibleLoops
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment