Skip to content

Instantly share code, notes, and snippets.

@ZeikJT
Last active December 17, 2023 07:16
Show Gist options
  • Save ZeikJT/67b8ef3bc670cff88a6b48516e0d488b to your computer and use it in GitHub Desktop.
Save ZeikJT/67b8ef3bc670cff88a6b48516e0d488b to your computer and use it in GitHub Desktop.
Advent of Code 2023
function adventOfCode2023_Day01_Part1(str) {
return str.trim().split('\n').map((line) => {
const firstNum = /\d/.exec(line)[0]
const lastNum = /(\d)[a-zA-Z]*?$/.exec(line)[1]
return Number(firstNum + lastNum)
}).reduce((total, num) => total + num, 0)
}
function adventOfCode2023_Day01_Part2(str) {
const CONVERSION = new Map([
['zero', '0'], // Not needed by prompt, but feels wrong to exclude.
['one', '1'],
['two', '2'],
['three', '3'],
['four', '4'],
['five', '5'],
['six', '6'],
['seven', '7'],
['eight', '8'],
['nine', '9'],
])
const CONVERSION_NUM_NAMES = Array.from(CONVERSION.keys()).join('|')
const NUM_REGEXP = new RegExp(`(?=(${CONVERSION_NUM_NAMES}|\\d))`, 'g')
return str.trim().split('\n').map((line) => {
const matches = Array.from(line.matchAll(NUM_REGEXP), (matchData) => matchData[1])
let firstNum = matches[0]
let lastNum = matches[matches.length - 1]
if (CONVERSION.has(firstNum)) {
firstNum = CONVERSION.get(firstNum)
}
if (CONVERSION.has(lastNum)) {
lastNum = CONVERSION.get(lastNum)
}
return Number(firstNum + lastNum)
}).reduce((total, num) => total + num, 0)
}
const NUM_COLOR_REGEX = /(\d+)\s(red|blue|green)/g
function adventOfCode2023_Day02_Part1(str) {
const COUNTS = {
red: 12,
green: 13,
blue: 14,
}
return str.trim().split('\n').reduce((total, line) => {
const gameNum = Number(line.substring(5, line.indexOf(':')))
let isValid = true
for (const set of line.split(';')) {
for (const [, numStr, color] of set.matchAll(NUM_COLOR_REGEX)) {
if (COUNTS[color] < Number(numStr)) {
isValid = false
break
}
}
if (!isValid) {
break
}
}
return total + (isValid ? gameNum : 0)
}, 0)
}
function adventOfCode2023_Day02_Part2(str) {
return str.trim().split('\n').reduce((total, line) => {
const gameNum = Number(line.substring(5, line.indexOf(':')))
const minCounts = {
red: 0,
green: 0,
blue: 0,
}
for (const set of line.split(';')) {
for (const [, numStr, color] of set.matchAll(NUM_COLOR_REGEX)) {
const num = Number(numStr)
if (minCounts[color] < num) {
minCounts[color] = num
}
}
}
return total + (minCounts.red * minCounts.green * minCounts.blue)
}, 0)
}
const NUMBERS = new Set(new Array(10).fill(0).map((num, index) => String(index)))
// Include undefined to handle rows before and after the input. Could pad grid but eh.
const NON_SYMBOLS = new Set([...NUMBERS, '.', undefined])
function adventOfCode2023_Day03_Part1(str) {
const grid = str.trim().split('\n')
function checkAdjacentSymbols(y, startX, endX) {
startX--
endX++
if (!NON_SYMBOLS.has(grid[y][startX]) || !NON_SYMBOLS.has(grid[y][endX])) {
return true
}
for (let i = startX; i <= endX; i++) {
if (!NON_SYMBOLS.has(grid[y - 1]?.[i]) || !NON_SYMBOLS.has(grid[y + 1]?.[i])) {
return true
}
}
return false
}
return grid.reduce((total, line, y) => {
let start = -1
let end = -1
// Go one extra character to terminate numbers at the end
for (let x = 0; x <= line.length; x++) {
if (NUMBERS.has(line[x])) {
if (start === -1) {
start = x
end = x
} else {
end = x
}
} else if (start !== -1) {
// Detect symbols left and right first, most local data, easiest to check
if (checkAdjacentSymbols(y, start, end)) {
total += Number(line.substring(start, end + 1))
}
start = -1
end = -1
}
}
return total
}, 0)
}
function adventOfCode2023_Day03_Part2(str) {
const grid = str.trim().split('\n')
function getFullNum(y, x) {
let startX = x
let endX = x
if (!NUMBERS.has(grid[y][x])) {
throw new Error(`Number not found at y ${y} and x ${x}: ${grid[y][x]}`)
}
while (NUMBERS.has(grid[y][startX - 1])) {
startX--
}
while (NUMBERS.has(grid[y][endX + 1])) {
endX++
}
return Number(grid[y].substring(startX, endX + 1))
}
function getGearRatio(y, x) {
if (NON_SYMBOLS.has(grid[y][x])) {
throw new Error(`Non symbol found at y ${y} and x ${x}: ${grid[y][x]}`)
}
const same = []
if (NUMBERS.has(grid[y][x - 1])) {
same.push(x - 1)
}
if (NUMBERS.has(grid[y][x + 1])) {
same.push(x + 1)
}
const above = new Set
const below = new Set
for (let i = x - 1; i < x + 2; i++) {
if (NUMBERS.has(grid[y - 1]?.[i])) {
above.delete(i - 1)
above.add(i)
}
if (NUMBERS.has(grid[y + 1]?.[i])) {
below.delete(i - 1)
below.add(i)
}
}
const foundCount = same.length + above.size + below.size
if (foundCount !== 2) {
return null
}
const found = []
for (const sameX of same) {
found.push(getFullNum(y, sameX))
}
for (const aboveX of above) {
found.push(getFullNum(y - 1, aboveX))
}
for (const belowX of below) {
found.push(getFullNum(y + 1, belowX))
}
return found[0] * found[1]
}
return grid.reduce((total, line, y) => {
for (let x = 0; x < line.length; x++) {
if (!NON_SYMBOLS.has(line[x])) {
const gearRatio = getGearRatio(y, x)
if (gearRatio !== null) {
total += gearRatio
}
}
}
return total
}, 0)
}
function adventOfCode2023_Day04_Part1(str) {
return str.trim().split('\n').reduce((total, line) => {
const [winningNums, haveNums] = line.substring(line.indexOf(':') + 1).split(' | ').map((str) => str.trim().split(' ').filter((str) => str))
const winningNumSet = new Set(winningNums)
let winTotal = 0
for (const num of haveNums) {
if (winningNumSet.has(num)) {
if (winTotal === 0) {
winTotal = 1
} else {
winTotal *= 2
}
}
}
return total + winTotal
}, 0)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment