Last active
February 5, 2023 19:11
-
-
Save ZeikJT/4cf0de1d18539e3ee310d9d0b577c7aa to your computer and use it in GitHub Desktop.
Advent of Code 2022
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
function getElfCalories(str) { | |
return str.trim().split('\n').reduce((arr, val) => { | |
if (val === '') { | |
arr.push(0) | |
} else { | |
const maxIndex = arr.length - 1 | |
arr[maxIndex] = arr[maxIndex] + Number(val) | |
} | |
return arr | |
}, [0]) | |
} | |
function adventOfCode2022_Day01_Part1(str) { | |
return Math.max(...getElfCalories(str)) | |
} | |
function adventOfCode2022_Day01_Part2(str) { | |
return getElfCalories(str).sort((a,b) => a > b ? 1 : a < b ? -1 : 0).slice(-3).reduce((total, val) =>(total + val, 0) | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const ROCK_VALUE = 1 | |
const PAPER_VALUE = 2 | |
const SCISSORS_VALUE = 3 | |
const LOSE_VALUE = 0 | |
const TIE_VALUE = 3 | |
const WIN_VALUE = 6 | |
function getRPSValues(str) { | |
return str.trim().split('\n').map((line) => line.split(' ')) | |
} | |
function adventOfCode2022_Day02_Part1(str) { | |
const outcomeValueMap = { | |
A: { | |
X: TIE_VALUE, | |
Y: WIN_VALUE, | |
Z: LOSE_VALUE, | |
}, | |
B: { | |
X: LOSE_VALUE, | |
Y: TIE_VALUE, | |
Z: WIN_VALUE, | |
}, | |
C: { | |
X: WIN_VALUE, | |
Y: LOSE_VALUE, | |
Z: TIE_VALUE, | |
}, | |
} | |
const value = { | |
X: ROCK_VALUE, | |
Y: PAPER_VALUE, | |
Z: SCISSORS_VALUE, | |
} | |
return getRPSValues(str).reduce((total, [opponent, self]) => { | |
return total + value[self] + outcomeValueMap[opponent][self] | |
}, 0) | |
} | |
function adventOfCode2022_Day02_Part2(str) { | |
const choiceValueMap = { | |
A: { | |
X: SCISSORS_VALUE, | |
Y: ROCK_VALUE, | |
Z: PAPER_VALUE, | |
}, | |
B: { | |
X: ROCK_VALUE, | |
Y: PAPER_VALUE, | |
Z: SCISSORS_VALUE, | |
}, | |
C: { | |
X: PAPER_VALUE, | |
Y: SCISSORS_VALUE, | |
Z: ROCK_VALUE, | |
}, | |
} | |
const outcomeValue = { | |
X: LOSE_VALUE, | |
Y: TIE_VALUE, | |
Z: WIN_VALUE, | |
} | |
return getRPSValues(str).reduce((total, [opponent, self]) => { | |
return total + choiceValueMap[opponent][self] + outcomeValue[self] | |
}, 0) | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
function getLines(str) { | |
return str.trim().split('\n') | |
} | |
function getLetterValue(letter) { | |
const charCode = letter.charCodeAt(0) | |
if (charCode >= 97) { | |
// a-z | |
return charCode - 96 | |
} | |
// A-Z | |
return charCode - 38 | |
} | |
function adventOfCode2022_Day03_Part1(str) { | |
return getLines(str).map((line) => { | |
const len = line.length | |
return [line.slice(0, len / 2), line.slice(len / 2)] | |
}).reduce((total, [left, right]) => { | |
for (const letter of left) { | |
if (right.includes(letter)) { | |
return total + getLetterValue(letter) | |
} | |
} | |
// Shouldn't happen. | |
return total | |
}, 0) | |
} | |
function adventOfCode2022_Day03_Part2(str) { | |
return getLines(str).reduce((arr, line) => { | |
let lines = arr[arr.length - 1] | |
if (lines.length === 3) { | |
lines = [line] | |
arr.push(lines) | |
} else { | |
lines.push(line) | |
} | |
return arr | |
}, [[]]).reduce((total, [line1, line2, line3]) => { | |
for (const letter of line1) { | |
if (line2.includes(letter) && line3.includes(letter)) { | |
return total + getLetterValue(letter) | |
} | |
} | |
// Shouldn't happen. | |
return total | |
}, 0) | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
function getLines(str) { | |
return str.trim().split('\n').map((line) => line.split(',').map((range) => range.split('-').map(Number))) | |
} | |
function adventOfCode2022_Day04_Part1(str) { | |
return getLines(str).reduce((total, [[start1, end1], [start2, end2]]) => { | |
const startMin = Math.min(start1, start2) | |
const endMax = Math.max(end1, end2) | |
if ((startMin === start1 && endMax === end1) || (startMin === start2 && endMax === end2)) { | |
return total + 1 | |
} | |
return total | |
}, 0) | |
} | |
function adventOfCode2022_Day04_Part2(str) { | |
return getLines(str).reduce((total, [[start1, end1], [start2, end2]]) => { | |
if ((start1 >= start2 && start1 <= end2) || (end1 >= start2 && end1 <= end2) || (start2 >= start1 && start2 <= end1) || (end2 >= start1 && end2 <= end1)) { | |
return total + 1 | |
} | |
return total | |
}, 0) | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
function getData(str) { | |
const lines = str.split('\n') | |
const columns = (lines[0].length + 1) / 4 | |
const data = new Array(columns).fill(0).map(() => []) | |
const separator = lines.findIndex((line) => line.startsWith(' 1 ')) | |
for (let i = 0; i < separator; i++) { | |
for (let j = 0; j < columns; j++) { | |
const char = lines[i][(j * 4) + 1] | |
if (char !== ' ') { | |
data[j].unshift(char) | |
} | |
} | |
} | |
return {lines, columns, data, separator} | |
} | |
function getMoveData(line) { | |
return /move (\d+) from (\d+) to (\d+)/.exec(line).slice(1).map((n) => Number.parseInt(n)) | |
} | |
function adventOfCode2022_Day05_Part1(str) { | |
const {lines, columns, data, separator} = getData(str) | |
for (let i = separator + 2; i < lines.length; i++) { | |
if (!lines[i].trim()) {continue} | |
const [count, from, to] = getMoveData(lines[i]) | |
for (let j = 0; j < count && data[from - 1].length; j++) { | |
data[to - 1].push(data[from - 1].pop()) | |
} | |
} | |
return data.map((column) => column.pop()).join('') | |
} | |
function adventOfCode2022_Day05_Part2(str) { | |
const {lines, columns, data, separator} = getData(str) | |
for (let i = separator + 2; i < lines.length; i++) { | |
if (!lines[i].trim()) {continue} | |
const [count, from, to] = getMoveData(lines[i]) | |
data[to - 1].push(...data[from - 1].splice(count * -1, count)) | |
} | |
return data.map((column) => column.pop()).join('') | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
function findUniqueStringPosition(str, length) { | |
const seenSet = new Set | |
for (let i = length - 1; i < str.length; i++) { | |
seenSet.clear() | |
for (let j = i - (length - 1); j <= i; j++) { | |
seenSet.add(str[j]) | |
} | |
if (seenSet.size === length) { | |
return i + 1 | |
} | |
} | |
} | |
function adventOfCode2022_Day06_Part1(str) { | |
return findUniqueStringPosition(str, 4) | |
} | |
function adventOfCode2022_Day06_Part2(str) { | |
return findUniqueStringPosition(str, 14) | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class File { | |
constructor(name, parentFolder = null, size = 0) { | |
this.name = name | |
this.parentFolder = parentFolder | |
this.size = size | |
} | |
} | |
class Folder extends File { | |
constructor(...args) { | |
super(...args) | |
this.children = new Map() | |
} | |
createSubfolder(name, parentFolder) { | |
this.children.set(name, new Folder(name, parentFolder)) | |
} | |
createFile(name, size) { | |
this.children.set(name, new File(name, this, size)) | |
this.addFileSize(size) | |
} | |
addFileSize(size) { | |
this.size += size | |
this.parentFolder?.addFileSize(size) | |
} | |
} | |
function buildFileSystem(str, includeFiles = false) { | |
let lsState = false | |
const root = new Folder('root') | |
let currentFolder = root | |
for (const line of str.trim().split('\n')) { | |
if (line.startsWith('$ cd ')) { | |
lsState = false | |
if (line === '$ cd /') { | |
currentFolder = root | |
} else if (line === '$ cd ..') { | |
currentFolder = currentFolder.parentFolder | |
} else { | |
currentFolder = currentFolder.children.get(line.substring(5)) | |
if (!currentFolder) { | |
throw new Error('Subfolder not found') | |
} | |
} | |
} else if (line.startsWith('$ ls')) { | |
lsState = true | |
} else if (lsState) { | |
if (line.startsWith('dir ')) { | |
currentFolder.createSubfolder(line.substring(4), currentFolder) | |
} else { | |
const match = /^(?<size>\d+)\s(?<name>[\w\.]+)$/.exec(line) | |
if (!match) { | |
throw new Error('Unexpected line, expected file format') | |
} | |
const fileSize = Number.parseInt(match.groups.size) | |
if (includeFiles) { | |
currentFolder.createFile(match.groups.name, fileSize) | |
} else { | |
currentFolder.addFileSize(fileSize) | |
} | |
} | |
} else { | |
throw new Error('Unknown line and state combo') | |
} | |
} | |
return root | |
} | |
function adventOfCode2022_Day07_Part1(str) { | |
const fileSystem = buildFileSystem(str) | |
function getTotalSubfolderSizeSmallerThan(root, maxSize) { | |
return Array.from(root.children.values()) | |
.reduce((totalSize, folder) => { | |
if (folder.size <= maxSize) { | |
totalSize += folder.size | |
} | |
return totalSize + getTotalSubfolderSizeSmallerThan(folder, maxSize) | |
}, 0) | |
} | |
return getTotalSubfolderSizeSmallerThan(fileSystem, 100000) | |
} | |
function adventOfCode2022_Day07_Part2(str) { | |
const fileSystem = buildFileSystem(str) | |
const minimumSizeNeeded = 30000000 - (70000000 - fileSystem.size) | |
function getMinumumFolderSize(root, currentMinimum = root.size) { | |
for (const folder of root.children.values()) { | |
if (folder.size > minimumSizeNeeded) { | |
if (folder.size < currentMinimum) { | |
currentMinimum = folder.size | |
} | |
currentMinimum = getMinumumFolderSize(folder, currentMinimum) | |
} | |
} | |
return currentMinimum | |
} | |
return getMinumumFolderSize(fileSystem, fileSystem.size) | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
function adventOfCode2022_Day08_Part1(str) { | |
const map = str.trim().split('\n') | |
const length = map[0].length | |
const height = map.length | |
let visibleCount = (length + height - 2) * 2 | |
function isVisible(x, y) { | |
const value = map[y][x] | |
let visible = true | |
for (let xl = 0; xl < x; xl++) { | |
if (map[y][xl] >= value) { | |
visible = false | |
break | |
} | |
} | |
if (!visible) { | |
visible = true | |
for (let xr = x + 1; xr < length; xr++) { | |
if (map[y][xr] >= value) { | |
visible = false | |
break | |
} | |
} | |
} | |
if (!visible) { | |
visible = true | |
for (let yt = 0; yt < y; yt++) { | |
if (map[yt][x] >= value) { | |
visible = false | |
break | |
} | |
} | |
} | |
if (!visible) { | |
visible = true | |
for (let yb = y + 1; yb < height; yb++) { | |
if (map[yb][x] >= value) { | |
visible = false | |
break | |
} | |
} | |
} | |
return visible | |
} | |
for (let y = 1; y < height - 1; y++) { | |
for (let x = 1; x < length - 1; x++) { | |
isVisible(x, y) && visibleCount++ | |
} | |
} | |
return visibleCount | |
} | |
function adventOfCode2022_Day08_Part2(str) { | |
const map = str.trim().split('\n') | |
const length = map[0].length | |
const height = map.length | |
let highestScore = 0 | |
function getScore(x, y) { | |
const value = map[y][x] | |
let score = 1 | |
for (let xl = x - 1; xl >= 0; xl--) { | |
if (xl === 0 || map[y][xl] >= value) { | |
score *= x - xl | |
break | |
} | |
} | |
for (let xr = x + 1; xr < length; xr++) { | |
if (xr === length - 1 || map[y][xr] >= value) { | |
score *= xr - x | |
break | |
} | |
} | |
for (let yt = y - 1; yt >= 0; yt--) { | |
if (yt === 0 || map[yt][x] >= value) { | |
score *= y - yt | |
break | |
} | |
} | |
for (let yb = y + 1; yb < height; yb++) { | |
if (yb === height - 1 || map[yb][x] >= value) { | |
score *= yb - y | |
break | |
} | |
} | |
return score | |
} | |
for (let y = 1; y < height - 1; y++) { | |
for (let x = 1; x < length - 1; x++) { | |
const score = getScore(x, y) | |
if (score > highestScore) { | |
highestScore = score | |
} | |
} | |
} | |
return highestScore | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class Map { | |
constructor(segmentCount = 2) { | |
this.map = [[1]] | |
this.count = 1 | |
this.width = 1 | |
this.height = 1 | |
this.rope = new Array(segmentCount).fill(0).map(() => ({x: 0, y: 0})) | |
this.head = this.rope[0] | |
this.segments = this.rope.filter((e, index) => index !== 0) | |
this.tail = this.rope[segmentCount - 1] | |
} | |
extendMap(dir, size) { | |
if (size <= 0) { | |
return | |
} | |
if (dir === 'L' || dir === 'R') { | |
this.width += size | |
if (dir === 'L') { | |
for (const segment of this.rope) { | |
segment.x += size | |
} | |
} | |
for (const row of this.map) { | |
const newColumns = new Array(size).fill(0) | |
if (dir === 'L') { | |
row.splice(0, 0, ...newColumns) | |
} else { | |
row.push(...newColumns) | |
} | |
} | |
} else if (dir === 'U' || dir === 'D') { | |
this.height += size | |
if (dir === 'U') { | |
for (const segment of this.rope) { | |
segment.y += size | |
} | |
} | |
const newRows = new Array(size).fill(0).map(() => new Array(this.width).fill(0)) | |
if (dir === 'U') { | |
this.map.splice(0, 0, ...newRows) | |
} else { | |
this.map.push(...newRows) | |
} | |
} | |
} | |
maybeRepositionSegments() { | |
for (let i = 1; i < this.rope.length; i++) { | |
const prev = this.rope[i - 1] | |
const next = this.rope[i] | |
const prevRelX = prev.x - next.x | |
const prevRelY = prev.y - next.y | |
if (Math.abs(prevRelX) <= 1 && Math.abs(prevRelY) <= 1) { | |
continue | |
} | |
next.x += Math.sign(prevRelX) | |
next.y += Math.sign(prevRelY) | |
if (next === this.tail && !this.map[next.y][next.x]) { | |
this.map[next.y][next.x] = 1 | |
this.count += 1 | |
} | |
} | |
} | |
moveHead(dir, dist) { | |
if (dir === 'L') { | |
this.extendMap(dir, dist - this.head.x) | |
for (let x = 0; x < dist; x++) { | |
this.head.x -= 1 | |
this.maybeRepositionSegments() | |
} | |
} else if (dir === 'R') { | |
this.extendMap(dir, dist - ((this.width - 1) - this.head.x)) | |
for (let x = 0; x < dist; x++) { | |
this.head.x += 1 | |
this.maybeRepositionSegments() | |
} | |
} else if (dir === 'U') { | |
this.extendMap(dir, dist - this.head.y) | |
for (let y = 0; y < dist; y++) { | |
this.head.y -= 1 | |
this.maybeRepositionSegments() | |
} | |
} else if (dir === 'D') { | |
this.extendMap(dir, dist - ((this.height - 1) - this.head.y)) | |
for (let y = 0; y < dist; y++) { | |
this.head.y += 1 | |
this.maybeRepositionSegments() | |
} | |
} | |
} | |
} | |
function adventOfCode2022_Day09_Part1(str) { | |
const map = new Map() | |
for (const move of str.trim().split('\n')) { | |
const dir = move[0] | |
const dist = Number.parseInt(move.substring(2)) | |
map.moveHead(dir, dist) | |
} | |
return map.count | |
} | |
function adventOfCode2022_Day09_Part2(str) { | |
const map = new Map(/* head= */ 1 + /* segments= */ 9) | |
for (const move of str.trim().split('\n')) { | |
const dir = move[0] | |
const dist = Number.parseInt(move.substring(2)) | |
map.moveHead(dir, dist) | |
} | |
return map.count | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
function adventOfCode2022_Day10_Part1(str) { | |
const checkpoints = new Array(6).fill(0).map((z, i) => (20 + (40 * i))) | |
let x = 1 | |
let cycle = 0 | |
let sum = 0 | |
function incrementCycle() { | |
cycle += 1 | |
if (checkpoints.includes(cycle)) { | |
sum += (x * cycle) | |
} | |
} | |
for (const command of str.trim().split('\n')) { | |
if (command === 'noop') { | |
incrementCycle() | |
} else if (command.startsWith('addx')) { | |
incrementCycle() | |
incrementCycle() | |
x += Number.parseInt(command.substring(5)) | |
} else { | |
throw new Error(`Unknown command: ${command}`) | |
} | |
} | |
return sum | |
} | |
function adventOfCode2022_Day10_Part2(str) { | |
let x = 1 | |
let cycle = 0 | |
const screen = [] | |
function incrementCycle() { | |
const pos = cycle % 40 | |
if (pos === 0) { | |
screen.push(line = []) | |
} | |
if (x - 1 === pos || x === pos || x + 1 === pos) { | |
line[pos] = '#' | |
} else { | |
line[pos] = ' ' | |
} | |
cycle += 1 | |
} | |
for (const command of str.trim().split('\n')) { | |
if (command === 'noop') { | |
incrementCycle() | |
} else if (command.startsWith('addx')) { | |
incrementCycle() | |
incrementCycle() | |
x += Number.parseInt(command.substring(5)) | |
} else { | |
throw new Error(`Unknown command: ${command}`) | |
} | |
} | |
console.log(screen.map((line) => line.join('')).join('\n')) | |
return screen | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
function parse(str) { | |
const monkeys = [] | |
let lastMonkey = null | |
for (const line of str.trim().split('\n')) { | |
if (line.startsWith('Monkey ')) { | |
const index = Number.parseInt(line[7]) | |
if (monkeys.length !== index) { | |
throw new Error(`Unexpected out-of-order index: ${index}`) | |
} | |
monkeys[index] = lastMonkey = { | |
index, | |
inspected: 0, | |
} | |
} else if (line.startsWith(' Starting items: ')) { | |
lastMonkey.items = line.substring(18).split(', ').map((n) => Number.parseInt(n)) | |
} else if (line.startsWith(' Operation: new = old ')) { | |
const op = line[23] | |
let op2 = line.substring(25) | |
if (/^\d+$/.test(op2)) { | |
op2 = Number.parseInt(op2) | |
} | |
lastMonkey.operation = function(oldValue) { | |
const b = op2 === 'old' ? oldValue : op2 | |
if (op === '+') { | |
return oldValue + b | |
} else if (op === '*') { | |
return oldValue * b | |
} | |
throw new Error(`Unknown operation: ${op}`) | |
} | |
} else if (line.startsWith(' Test: divisible by ')) { | |
lastMonkey.testDivisibleBy = Number.parseInt(line.substring(21)) | |
} else if (line.startsWith(' If true: throw to monkey ')) { | |
lastMonkey.ifDivisibleTrue = Number.parseInt(line.substring(29)) | |
} else if (line.startsWith(' If false: throw to monkey ')) { | |
lastMonkey.ifDivisibleFalse = Number.parseInt(line.substring(30)) | |
} else if (line) { | |
throw new Error(`Unknown directive: ${line}`) | |
} | |
} | |
return monkeys | |
} | |
function getMonkeyBusiness(monkeys, rounds, divide) { | |
const wrapValue = monkeys.reduce((wrapValue, {testDivisibleBy}) => wrapValue * testDivisibleBy, 1) | |
for (let round = 0; round < rounds; round++) { | |
for (const monkey of monkeys) { | |
if (monkey.items) { | |
for (const item of monkey.items) { | |
monkey.inspected += 1 | |
let newValue = monkey.operation(item) | |
if (divide) { | |
newValue = Math.floor(newValue / 3) | |
} | |
if (newValue > wrapValue) { | |
newValue %= wrapValue | |
} | |
const isDivisible = (newValue % monkey.testDivisibleBy) === 0 | |
monkeys[isDivisible ? monkey.ifDivisibleTrue : monkey.ifDivisibleFalse].items.push(newValue) | |
} | |
monkey.items = [] | |
} | |
} | |
} | |
return monkeys | |
.sort((a, b) => a.inspected > b.inspected ? 1 : -1) | |
.slice(-2) | |
.reduce((monkeyBusiness, monkey) => monkeyBusiness * monkey.inspected, 1) | |
} | |
function adventOfCode2022_Day11_Part1(str) { | |
return getMonkeyBusiness(parse(str), 20, true) | |
} | |
function adventOfCode2022_Day11_Part2(str) { | |
return getMonkeyBusiness(parse(str), 10000, false) | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Implemented based on Wikipedia pseudocode: https://en.wikipedia.org/wiki/A*_search_algorithm#Pseudocode | |
// A* finds a path from start to goal. | |
// h is the heuristic function. h(n) estimates the cost to reach goal from node n. | |
function A_Star(map, start, goal, h) { | |
// The set of discovered nodes that may need to be (re-)expanded. | |
// Initially, only the start node is known. | |
// This is usually implemented as a min-heap or priority queue rather than a hash-set. | |
const openSet = new Set([start]) | |
// For node n, cameFrom[n] is the node immediately preceding it on the cheapest path from start | |
// to n currently known. | |
const cameFrom = new Map() | |
// For node n, gScore[n] is the cost of the cheapest path from start to n currently known. | |
const gScore = new Map([[start, 0]]) | |
// For node n, fScore[n] := gScore[n] + h(n). fScore[n] represents our current best guess as to | |
// how cheap a path could be from start to finish if it goes through n. | |
const fScore = new Map([[start, h(goal, start)]]) | |
while (openSet.size) { | |
// This operation can occur in O(Log(N)) time if openSet is a min-heap or a priority queue | |
const current = Array.from(openSet).reduce((f, n) => { | |
const {lowFScore, fNode} = f | |
const nFScore = fScore.get(n) | |
if (nFScore < lowFScore) { | |
return { | |
fScore: nFScore, | |
fNode: n, | |
} | |
} | |
return f | |
}) | |
if (current === goal) { | |
let c = goal | |
const total_path = [c] | |
while (cameFrom.has(c)) { | |
c = cameFrom.get(c) | |
total_path.unshift(c) | |
} | |
return total_path | |
} | |
openSet.delete(current) | |
for (const neighbor of current.n) { | |
// d(current,neighbor) is the weight of the edge from current to neighbor | |
// tentative_gScore is the distance from start to the neighbor through current | |
const tentative_gScore = gScore.get(current) + 1 | |
if (tentative_gScore < (gScore.get(neighbor) ?? Number.POSITIVE_INFINITY)) { | |
// This path to neighbor is better than any previous one. Record it! | |
cameFrom.set(neighbor, current) | |
gScore.set(neighbor, tentative_gScore) | |
fScore.set(neighbor, tentative_gScore + h(goal, neighbor)) | |
openSet.add(neighbor) | |
} | |
} | |
} | |
// Open set is empty but goal was never reached | |
throw new Error('Goal unreachable') | |
} | |
function parseIntoMap(str) { | |
const min = 'a'.charCodeAt(0) | |
let s = null | |
let e = null | |
const map = str.trim().split('\n').map((line, y) => line.split('').map((letter, x) => { | |
const node = {x, y, h: 0, n: []} | |
if (letter === 'S') { | |
return s = node | |
} | |
if (letter === 'E') { | |
return e = node | |
} | |
node.h = letter.charCodeAt(0) - min | |
return node | |
})) | |
for (const row of map) { | |
for (const cell of row) { | |
for (const [xOff, yOff] of [[-1, 0], [1, 0], [0, -1], [0, 1]]) { | |
const neighbor = map[yOff + cell.y]?.[xOff + cell.x] | |
if (neighbor) { | |
if (cell.h >= neighbor.h - 1) { | |
cell.n.push(neighbor) | |
} | |
} | |
} | |
} | |
} | |
return { | |
s, | |
e, | |
map, | |
} | |
} | |
function distance(e, n) { | |
return Math.abs(e.x - n.x) + Math.abs(e.y - n.y) | |
} | |
function adventOfCode2022_Day12_Part1(str) { | |
const {s, e, map} = parseIntoMap(str) | |
const path = A_Star(map, s, e, distance) | |
// return map.map((row) => { | |
// return row.map((n) => { | |
// return path.includes(n) ? 'X' : ' ' | |
// }).join('') | |
// }).join('\n') | |
return path.length - 1 | |
} | |
function adventOfCode2022_Day12_Part2(str) { | |
const {e, map} = parseIntoMap(str) | |
return map.flat().filter((n) => n.h === 0 && n !== e).reduce((prevData, next) => { | |
try { | |
const aStar = A_Star(map, next, e, distance) | |
if (aStar.length < prevData.aStar.length) { | |
return { | |
aStar, | |
n: next, | |
} | |
} | |
} catch {} | |
return prevData | |
}, {aStar: {length: Number.POSITIVE_INFINITY}, n: null}).aStar.length - 1 | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
function isInOrder(left, right) { | |
for (let i = 0; i < left.length && i < right.length; i++) { | |
const l = left[i] | |
const r = right[i] | |
const lNumber = typeof l === 'number' | |
const rNumber = typeof r === 'number' | |
if (lNumber && rNumber) { | |
if (l !== r) { | |
return l < r | |
} | |
} else { | |
const result = isInOrder(lNumber ? [l] : l, rNumber ? [r] : r) | |
if (result !== null) { | |
return result | |
} | |
} | |
} | |
if (left.length !== right.length) { | |
return left.length < right.length | |
} | |
return null | |
} | |
function parseToLines(str) { | |
// Could have written a parser, but eh. | |
return str.trim().split('\n').filter((line) => line).map(JSON.parse) | |
} | |
function adventOfCode2022_Day13_Part1(str) { | |
return parseToLines(str).reduce((pairs, row, i) => { | |
if ((i & 1) === 0) { | |
pairs.push([row]) | |
} else { | |
pairs[pairs.length - 1].push(row) | |
} | |
return pairs | |
}, []).reduce((total, [left, right], index) => { | |
return total + (isInOrder(left, right) ? index + 1 : 0) | |
}, 0) | |
} | |
function adventOfCode2022_Day13_Part2(str) { | |
const sortedLines = parseToLines(str).concat([[[2]], [[6]]]).sort((a, b) => isInOrder(a,b) ? -1 : 1) | |
return (sortedLines.findIndex((arr) => arr.length === 1 && arr[0].length === 1 && arr[0][0] === 2) + 1) * | |
(sortedLines.findIndex((arr) => arr.length === 1 && arr[0].length === 1 && arr[0][0] === 6) + 1) | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
function parseToMap(str) { | |
let minX = Number.POSITIVE_INFINITY | |
let maxX = 0 | |
let minY = Number.POSITIVE_INFINITY | |
let maxY = 0 | |
const walls = str.trim().split('\n').map((line) => line.split(' -> ').map((point) => point.split(',').map(Number)).map(([x, y]) => ({x, y}))) | |
for (const wall of walls) { | |
for (const {x, y} of wall) { | |
if (x < minX) { | |
minX = x | |
} | |
if (x > maxX) { | |
maxX = x | |
} | |
if (y < minY) { | |
minY = y | |
} | |
if (y > maxY) { | |
maxY = y | |
} | |
} | |
} | |
const map = new Array(maxY + 2).fill(0).map(() => []) | |
for (const wall of walls) { | |
let from = null | |
for (const to of wall) { | |
if (from) { | |
for (let {x, y} = from; x !== to.x || y !== to.y; x += Math.sign(to.x - from.x), y += Math.sign(to.y - from.y)) { | |
map[y][x] = '#' | |
} | |
map[to.y][to.x] = '#' | |
} | |
from = to | |
} | |
} | |
return {map, minX, maxX, maxY} | |
} | |
function adventOfCode2022_Day14_Part1(str) { | |
const {map, minX, maxX, maxY} = parseToMap(str) | |
const sandSpawnX = 500 | |
let sandX = sandSpawnX | |
let sandY = 0 | |
let sandCount = 1 | |
for (let i = 0; i < 1000000; i++) { | |
sandY++ | |
if (!map[sandY][sandX]) { | |
map[sandY - 1][sandX] = undefined | |
map[sandY][sandX] = 'O' | |
} else if (!map[sandY][sandX - 1]) { | |
map[sandY - 1][sandX] = undefined | |
sandX-- | |
map[sandY][sandX] = 'O' | |
} else if (!map[sandY][sandX + 1]) { | |
map[sandY - 1][sandX] = undefined | |
sandX++ | |
map[sandY][sandX] = 'O' | |
} else { | |
sandX = sandSpawnX | |
sandY = 0 | |
map[sandY][sandX] = 'O' | |
sandCount++ | |
} | |
if (sandX < minX || sandX >= maxX || sandY >= maxY) { | |
break | |
} | |
} | |
return sandCount - 1 | |
} | |
function adventOfCode2022_Day14_Part2(str) { | |
const {map, minX, maxX, maxY} = parseToMap(str) | |
const sandSpawnX = 500 | |
let sandX = sandSpawnX | |
let sandY = 0 | |
let sandCount = 1 | |
for (let i = 0; i < 1000000000; i++) { | |
sandY++ | |
if (sandY === maxY + 2) { | |
sandX = sandSpawnX | |
sandY = 0 | |
map[sandY][sandX] = 'O' | |
sandCount++ | |
} else if (!map[sandY][sandX]) { | |
map[sandY - 1][sandX] = undefined | |
map[sandY][sandX] = 'O' | |
} else if (!map[sandY][sandX - 1]) { | |
map[sandY - 1][sandX] = undefined | |
sandX-- | |
map[sandY][sandX] = 'O' | |
} else if (!map[sandY][sandX + 1]) { | |
map[sandY - 1][sandX] = undefined | |
sandX++ | |
map[sandY][sandX] = 'O' | |
} else { | |
if (sandY === 1) { | |
break | |
} | |
sandX = sandSpawnX | |
sandY = 0 | |
map[sandY][sandX] = 'O' | |
sandCount++ | |
} | |
} | |
return sandCount | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment