From 521cbca0b03ac59544e6ba97e16125839cd56cce Mon Sep 17 00:00:00 2001 From: nim-ka Date: Mon, 28 Nov 2022 03:38:22 +0000 Subject: [PATCH] . --- out.js | 2 +- out.txt | 96 ++-- proto.js | 2 +- t.js | 1494 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 1544 insertions(+), 50 deletions(-) create mode 100644 t.js diff --git a/out.js b/out.js index 6419e54..6be8e52 100644 --- a/out.js +++ b/out.js @@ -1180,7 +1180,7 @@ load = function load() { }, sort: { value: function(func = (a, b) => a.readingOrderCompare(b)) { - return Array.prototype.sort.apply(this, func) + return Array.prototype.sort.apply(this, [func]) }, configurable: true }, diff --git a/out.txt b/out.txt index 06f847e..e7b5e35 100644 --- a/out.txt +++ b/out.txt @@ -1,79 +1,79 @@ -2021 day 1 part 1: 0.685ms +2021 day 1 part 1: 0.717ms 2021 day 1 part 1: Got 1292, expected 1292 2021 day 1 part 1: SUCCESS -2021 day 1 part 2: 0.464ms +2021 day 1 part 2: 0.518ms 2021 day 1 part 2: Got 1262, expected 1262 2021 day 1 part 2: SUCCESS -2021 day 2 part 1: 3.003ms +2021 day 2 part 1: 3.256ms 2021 day 2 part 1: Got 1813801, expected 1813801 2021 day 2 part 1: SUCCESS -2021 day 2 part 2: 2.982ms +2021 day 2 part 2: 2.952ms 2021 day 2 part 2: Got 1960569556, expected 1960569556 2021 day 2 part 2: SUCCESS -2021 day 3 part 1: 19.07ms +2021 day 3 part 1: 18.062ms 2021 day 3 part 1: Got 3882564, expected 3882564 2021 day 3 part 1: SUCCESS -2021 day 3 part 2: 7.878ms +2021 day 3 part 2: 8.054ms 2021 day 3 part 2: Got 3385170, expected 3385170 2021 day 3 part 2: SUCCESS -2021 day 4 part 1: 21.9ms +2021 day 4 part 1: 21.45ms 2021 day 4 part 1: Got 54275, expected 54275 2021 day 4 part 1: SUCCESS -2021 day 4 part 2: 14.157ms +2021 day 4 part 2: 13.453ms 2021 day 4 part 2: Got 13158, expected 13158 2021 day 4 part 2: SUCCESS -2021 day 5 part 1: 65.96ms +2021 day 5 part 1: 66.88ms 2021 day 5 part 1: Got 6856, expected 6856 2021 day 5 part 1: SUCCESS -2021 day 5 part 2: 67.948ms +2021 day 5 part 2: 63.166ms 2021 day 5 part 2: Got 20666, expected 20666 2021 day 5 part 2: SUCCESS -2021 day 6 part 1: 0.242ms +2021 day 6 part 1: 0.249ms 2021 day 6 part 1: Got 353274, expected 353274 2021 day 6 part 1: SUCCESS -2021 day 6 part 2: 0.172ms +2021 day 6 part 2: 0.155ms 2021 day 6 part 2: Got 1609314870967, expected 1609314870967 2021 day 6 part 2: SUCCESS -2021 day 7 part 1: 79.145ms +2021 day 7 part 1: 79.257ms 2021 day 7 part 1: Got 347011, expected 347011 2021 day 7 part 1: SUCCESS -2021 day 7 part 2: 79.347ms +2021 day 7 part 2: 78.681ms 2021 day 7 part 2: Got 98363777, expected 98363777 2021 day 7 part 2: SUCCESS -2021 day 8 part 1: 4.304ms +2021 day 8 part 1: 4.299ms 2021 day 8 part 1: Got 525, expected 525 2021 day 8 part 1: SUCCESS -2021 day 8 part 2: 11.439ms +2021 day 8 part 2: 11.329ms 2021 day 8 part 2: Got 1083859, expected 1083859 2021 day 8 part 2: SUCCESS -2021 day 9 part 1: 62.579ms +2021 day 9 part 1: 67.729ms 2021 day 9 part 1: Got 585, expected 585 2021 day 9 part 1: SUCCESS -2021 day 9 part 2: 259.156ms +2021 day 9 part 2: 258.077ms 2021 day 9 part 2: Got 827904, expected 827904 2021 day 9 part 2: SUCCESS -2021 day 10 part 1: 2.111ms +2021 day 10 part 1: 4.644ms 2021 day 10 part 1: Got 469755, expected 469755 2021 day 10 part 1: SUCCESS -2021 day 10 part 2: 1.662ms +2021 day 10 part 2: 1.599ms 2021 day 10 part 2: Got 2762335572, expected 2762335572 2021 day 10 part 2: SUCCESS -2021 day 11 part 1: 28.949ms +2021 day 11 part 1: 20.053ms 2021 day 11 part 1: Got 1691, expected 1691 2021 day 11 part 1: SUCCESS -2021 day 11 part 2: 35.651ms +2021 day 11 part 2: 30.856ms 2021 day 11 part 2: Got 216, expected 216 2021 day 11 part 2: SUCCESS -2021 day 12 part 1: 33.724ms +2021 day 12 part 1: 30.68ms 2021 day 12 part 1: Got 4241, expected 4241 2021 day 12 part 1: SUCCESS -2021 day 12 part 2: 698.032ms +2021 day 12 part 2: 791.113ms 2021 day 12 part 2: Got 122134, expected 122134 2021 day 12 part 2: SUCCESS -2021 day 13 part 1: 23.464ms +2021 day 13 part 1: 24.31ms 2021 day 13 part 1: Got 695, expected 695 2021 day 13 part 1: SUCCESS -2021 day 13 part 2: 73.91ms +2021 day 13 part 2: 74.591ms 2021 day 13 part 2: Got ## ## #### ## # # # ### ## # # # # # # # # # # # # # # # # # # # # # # @@ -86,34 +86,34 @@ # # # # # # # # # # # # # ### ## #### ### #### ## # ## 2021 day 13 part 2: SUCCESS -2021 day 14 part 1: 1.793ms +2021 day 14 part 1: 1.854ms 2021 day 14 part 1: Got 2068, expected 2068 2021 day 14 part 1: SUCCESS -2021 day 14 part 2: 4.33ms +2021 day 14 part 2: 4.53ms 2021 day 14 part 2: Got 2158894777814, expected 2158894777814 2021 day 14 part 2: SUCCESS -2021 day 15 part 1: 119.837ms +2021 day 15 part 1: 134.365ms 2021 day 15 part 1: Got 498, expected 498 2021 day 15 part 1: SUCCESS -2021 day 15 part 2: 2.208s +2021 day 15 part 2: 2.215s 2021 day 15 part 2: Got 2901, expected 2901 2021 day 15 part 2: SUCCESS -2021 day 16 part 1: 29.437ms +2021 day 16 part 1: 33.926ms 2021 day 16 part 1: Got 1038, expected 1038 2021 day 16 part 1: SUCCESS -2021 day 16 part 2: 17.807ms +2021 day 16 part 2: 15.633ms 2021 day 16 part 2: Got 246761930504, expected 246761930504 2021 day 16 part 2: SUCCESS -2021 day 17 part 1: 64.043ms +2021 day 17 part 1: 66.095ms 2021 day 17 part 1: Got 4656, expected 4656 2021 day 17 part 1: SUCCESS -2021 day 17 part 2: 66.007ms +2021 day 17 part 2: 53.528ms 2021 day 17 part 2: Got 1908, expected 1908 2021 day 17 part 2: SUCCESS -2021 day 18 part 1: 38.299ms +2021 day 18 part 1: 27.293ms 2021 day 18 part 1: Got 3494, expected 3494 2021 day 18 part 1: SUCCESS -2021 day 18 part 2: 243.259ms +2021 day 18 part 2: 255.399ms 2021 day 18 part 2: Got 4712, expected 4712 2021 day 18 part 2: SUCCESS linked 0<->5 (0 point 0 == 5 point 9 with rotation 8) @@ -151,7 +151,7 @@ linked 35<->9 (35 point 0 == 9 point 11 with rotation 9) linked 9<->25 (9 point 0 == 25 point 18 with rotation 5) linked 25<->29 (25 point 0 == 29 point 7 with rotation 23) linked 29<->21 (29 point 0 == 21 point 6 with rotation 3) -2021 day 19 part 1: 36.375s +2021 day 19 part 1: 36.412s 2021 day 19 part 1: Got 447, expected 447 2021 day 19 part 1: SUCCESS linked 0<->5 (0 point 0 == 5 point 9 with rotation 8) @@ -189,25 +189,25 @@ linked 35<->9 (35 point 0 == 9 point 11 with rotation 9) linked 9<->25 (9 point 0 == 25 point 18 with rotation 5) linked 25<->29 (25 point 0 == 29 point 7 with rotation 23) linked 29<->21 (29 point 0 == 21 point 6 with rotation 3) -2021 day 19 part 2: 36.165s +2021 day 19 part 2: 35.544s 2021 day 19 part 2: Got 15672, expected 15672 2021 day 19 part 2: SUCCESS -2021 day 20 part 1: 81.257ms +2021 day 20 part 1: 71.748ms 2021 day 20 part 1: Got 4873, expected 4873 2021 day 20 part 1: SUCCESS -2021 day 20 part 2: 1.974s +2021 day 20 part 2: 1.915s 2021 day 20 part 2: Got 16394, expected 16394 2021 day 20 part 2: SUCCESS -2021 day 21 part 1: 0.586ms +2021 day 21 part 1: 0.675ms 2021 day 21 part 1: Got 597600, expected 597600 2021 day 21 part 1: SUCCESS -2021 day 21 part 2: 339.28ms +2021 day 21 part 2: 320.409ms 2021 day 21 part 2: Got 634769613696613, expected 634769613696613 2021 day 21 part 2: SUCCESS -2021 day 22 part 1: 41.164ms +2021 day 22 part 1: 42.162ms 2021 day 22 part 1: Got 589411, expected 589411 2021 day 22 part 1: SUCCESS -2021 day 22 part 2: 127.613ms +2021 day 22 part 2: 123.326ms 2021 day 22 part 2: Got 1130514303649907, expected 1130514303649907 2021 day 22 part 2: SUCCESS iteration 0 heap size 28 min energy 0 @@ -217,7 +217,7 @@ iteration 30000 heap size 14414 min energy 7695 iteration 40000 heap size 15108 min energy 8555 iteration 50000 heap size 14881 min energy 9425 iteration 60000 heap size 12860 min energy 10003 -2021 day 23 part 1: 2.581s +2021 day 23 part 1: 2.580s 2021 day 23 part 1: Got 10607, expected 10607 2021 day 23 part 1: SUCCESS iteration 0 heap size 28 min energy 0 @@ -229,15 +229,15 @@ iteration 50000 heap size 11889 min energy 9313 iteration 60000 heap size 11552 min energy 10455 iteration 70000 heap size 7414 min energy 12084 iteration 80000 heap size 3371 min energy 14557 -2021 day 23 part 2: 2.645s +2021 day 23 part 2: 2.733s 2021 day 23 part 2: Got 59071, expected 59071 2021 day 23 part 2: SUCCESS -2021 day 24 part 1: 0.262ms +2021 day 24 part 1: 0.295ms 2021 day 24 part 1: Got 99691891979938, expected 99691891979938 2021 day 24 part 1: SUCCESS 2021 day 24 part 2: 0.106ms 2021 day 24 part 2: Got 27141191213911, expected 27141191213911 2021 day 24 part 2: SUCCESS -2021 day 25: 2.337s +2021 day 25: 2.511s 2021 day 25: Got 534, expected 534 2021 day 25: SUCCESS diff --git a/proto.js b/proto.js index 948e7f8..25fba5e 100644 --- a/proto.js +++ b/proto.js @@ -359,7 +359,7 @@ load = function load() { }, sort: { value: function(func = (a, b) => a.readingOrderCompare(b)) { - return Array.prototype.sort.apply(this, func) + return Array.prototype.sort.apply(this, [func]) }, configurable: true }, diff --git a/t.js b/t.js new file mode 100644 index 0000000..80074f9 --- /dev/null +++ b/t.js @@ -0,0 +1,1494 @@ +const opens = ["(", "[", "{", "<"] +const closes = [")", "]", "}", ">"] +const scores1 = { ")": 3, "]": 57, "}": 1197, ">": 25137 } +const scores2 = { ")": 1, "]": 2, "}": 3, ">": 4 } + +function day10(input, part2) { + let results = input.split("\n").map((str) => { + let stack = [] + + for (let i = 0; i < str.length; i++) { + let char = str[i] + + if (opens.includes(char)) { + stack.push(closes[opens.indexOf(char)]) + } else if (char != stack.pop()) { + return { error: true, score: scores1[char] } + } + } + + return { error: false, score: stack.reduceRight((a, b) => a * 5 + scores2[b], 0) } + }) + + return results.filter((e) => part2 ^ e.error).map((e) => e.score)[part2 ? "medianNumeric" : "sum"]() +} + +if (typeof window == "undefined") { + module.exports = day10 +} +function day11(input, part2) { + let grid = Grid.fromStr(input).mapMut((e) => ({ energy: +e, flashed: false })) + + let steps = 0 + let flashes = 0 + let totalFlashes = 0 + + while (part2 ? flashes != grid.width * grid.height : steps < 100) { + flashes = 0 + + grid.forEach(function flash(e, pt, g) { + e.energy++ + + if (!e.flashed && e.energy > 9) { + e.flashed = true + flashes++ + + g.getAllNeighborsThat(pt, (e) => !e.flashed).forEach((pt) => flash(g.get(pt), pt, g)) + } + }) + + grid.forEach((e) => { + if (e.flashed) { + e.energy = 0 + e.flashed = false + } + }) + + steps++ + totalFlashes += flashes + } + + return part2 ? steps : totalFlashes +} + +if (typeof window == "undefined") { + module.exports = day11 +} +// this one used to be a lot shorter but i made it less shoddy and slow at expense of line count +function day12(input, part2) { + let cxns = input.split("\n").map((line) => line.split("-")) + + let caves = [] + let startId + + for (let cxn of cxns) { + let ids = [] + + for (let i = 0; i < 2; i++) { + let cave = caves.find((e) => e.name == cxn[i]) + + if (!cave) { + caves.push(cave = { + name: cxn[i], + id: caves.length, + isStart: cxn[i] == "start", + isEnd: cxn[i] == "end", + isSmall: cxn[i] == cxn[i].toLowerCase(), + cxns: new Set() + }) + + if (cave.isStart) { + startId = cave.id + } + } + + ids.push(cave.id) + } + + caves[ids[0]].cxns.add(ids[1]) + caves[ids[1]].cxns.add(ids[0]) + } + + let count = 0 + let paths = [{ + cur: startId, + smallVisited: new Set(), + visitedSmallTwice: false + }] + + let i = 0 + + while (paths.length) { + let newPaths = [] + + for (let path of paths) { + for (let nextId of caves[path.cur].cxns) { + let next = caves[nextId] + + if (next.isStart) { + continue + } + + if (next.isEnd) { + count++ + continue + } + + let visitedSmallTwice = path.visitedSmallTwice + + if (next.isSmall && path.smallVisited.has(nextId)) { + if (part2 && !visitedSmallTwice) { + visitedSmallTwice = true + } else { + continue + } + } + + newPaths.push({ + cur: nextId, + smallVisited: new Set(path.smallVisited).add(nextId), + visitedSmallTwice: visitedSmallTwice + }) + } + } + + paths = newPaths + } + + return count +} + +if (typeof window == "undefined") { + module.exports = day12 +} +function day13(input, part2) { + let lines = input.split("\n").splitOnElement("") + + let points = lines[0].map((e) => new Point(...e.split(",").num())).pt + let instructions = lines[1].map((e) => e.split(" ").last.split("=")).map((e) => [e[0], +e[1]]) + + for (let instruction of instructions) { + let pos = instruction[1] + let newPoints = [].pt + + for (let point of points) { + if (instruction[0] == "x") { + newPoints.push(point.x < pos ? point : new Point(2 * pos - point.x, point.y)) + } else { + newPoints.push(point.y < pos ? point : new Point(point.x, 2 * pos - point.y)) + } + } + + points = newPoints.uniq() + + if (!part2) { + return points.length + } + } + + return new Grid(points.max((e) => e.x).x + 1, points.max((e) => e.y).y + 1).mapMut((_, pt) => pt.isIn(points) ? "#" : " ").toString("") +} + +if (typeof window == "undefined") { + module.exports = day13 +} +function getNum(letter) { + return letter.charCodeAt(0) - "A".charCodeAt(0) +} + +function day14(input, part2) { + let lines = input.split("\n").splitOnElement("") + + let init = lines[0][0].split("") + let rules = lines[1].map((e) => e.split(" -> ")) + + let counts = {} + + for (let i = 0; i < init.length - 1; i++) { + let substr = init[i] + init[i + 1] + counts[substr] = (counts[substr] || 0) + 1 + } + + for (let i = 0; i < (part2 ? 40 : 10); i++) { + let changes = {} + + for (let rule of rules) { + let target = rule[0][0] + rule[0][1] + let left = rule[0][0] + rule[1] + let right = rule[1] + rule[0][1] + + changes[left] = (changes[left] || 0) + (counts[target] || 0) + changes[right] = (changes[right] || 0) + (counts[target] || 0) + changes[target] = (changes[target] || 0) - (counts[target] || 0) + } + + for (let pair in changes) { + counts[pair] = (counts[pair] || 0) + changes[pair] + } + } + + // this double counts all letters except the first and last so we add one to those and / 2 at end + let letters = Array(26).fill(0) + letters[getNum(init[0])]++ + letters[getNum(init[init.length - 1])]++ + + for (let pair in counts) { + letters[getNum(pair[0])] += counts[pair] + letters[getNum(pair[1])] += counts[pair] + } + + letters = letters.truthy() + + return (letters.max() - letters.min()) / 2 +} + +if (typeof window == "undefined") { + module.exports = day14 +} +function encodePoint(pt) { + return (pt.x << 9) | pt.y +} + +function decodePoint(num) { + return new Point((num >> 9) & 0b111111111, num & 0b111111111) +} + +function day15(input, part2) { + let grid = Grid.fromStr(input).num() + + if (part2) { + grid = new Grid(grid.width * 5, grid.height * 5) + .mapMut((e, pt) => grid.get(new Pt(pt.x % grid.width, pt.y % grid.height)) + (pt.x / grid.width | 0) + (pt.y / grid.height | 0)) + .mapMut((e) => ((e - 1) % 9) + 1) + } + + let start = encodePoint(new Point(0, 0)) + + let visited = {} + visited[start] = true + + let heap = new BinHeap((a, b) => a.risk < b.risk) + heap.insert({ pt: start, risk: 0 }) + + while (true) { + let top = heap.extract() + let decoded = decodePoint(top.pt) + + if (decoded.x == grid.width - 1 && decoded.y == grid.height - 1) { + return top.risk + } + + for (let pt of grid.getAdjNeighbors(decoded)) { + let encoded = encodePoint(pt) + let risk = top.risk + grid.get(pt) + + if (encoded in visited) { + let idx = heap.data.findIndex((e) => e.pt == encoded) + if (idx > -1 && risk < heap.data[idx].risk) { + heap.data[idx].risk = risk + heap.up(idx) + } + } else { + visited[encoded] = true + heap.insert({ pt: encoded, risk: risk }) + } + } + } +} + +if (typeof window == "undefined") { + module.exports = day15 +} +function toInt(bits) { + return parseInt(bits.join(""), 2) +} + +class Packet { + full = [] + + version = [] + type = [] + groups = [[]] + lengthType = [] + length = [] + + children = [] + done = false + + constructor(bits = []) { + for (let bit of bits) { + this.pushBit(bit) + } + } + + pushBit(bit) { + if (this.version.length < 3) { + this.version.push(bit) + return this.full.push(bit) + } + + if (this.type.length < 3) { + this.type.push(bit) + return this.full.push(bit) + } + + if (this.isLiteral()) { + if (this.groups.last.length < 5) { + this.groups.last.push(bit) + return this.full.push(bit) + } + + if (this.groups.last[0]) { + this.groups.push([bit]) + return this.full.push(bit) + } + } else { + if (this.children.length == 0) { + this.children.push(new Packet()) + } + + if (this.lengthType.length < 1) { + this.lengthType.push(bit) + return this.full.push(bit) + } + + if (!this.lengthType[0]) { + if (this.length.length < 15) { + this.length.push(bit) + return this.full.push(bit) + } + + if (this.children.map((e) => e.full.length).sum() < toInt(this.length)) { + this.children.last.pushBit(bit) + + if (this.children.last.done) { + this.children.push(new Packet([bit])) + } + + return this.full.push(bit) + } + } else { + if (this.length.length < 11) { + this.length.push(bit) + return this.full.push(bit) + } + + if (!this.children.last.done) { + this.children.last.pushBit(bit) + + if (this.children.last.done) { + if (this.children.length < toInt(this.length)) { + this.children.push(new Packet([bit])) + return this.full.push(bit) + } + } else { + return this.full.push(bit) + } + } + } + } + + this.done = true + } + + isLiteral() { + return toInt(this.type) == 4 + } + + getLiteralValue() { + return toInt(this.groups.flatMap((e) => e.slice(1))) + } + + getVersionSum() { + return toInt(this.version) + this.children.map((e) => e.getVersionSum()).sum() + } + + getValue() { + if (this.isLiteral()) { + return this.getLiteralValue() + } else { + let operands = this.children.map((e) => e.getValue()) + + switch (toInt(this.type)) { + case 0: + return operands.sum() + + case 1: + return operands.prod() + + case 2: + return operands.min() + + case 3: + return operands.max() + + case 5: + return +(operands[0] > operands[1]) + + case 6: + return +(operands[0] < operands[1]) + + case 7: + return +(operands[0] == operands[1]) + } + } + } +} + +function day16(input, part2) { + let bits = input.split("").flatMap((e) => parseInt(e, 16).toString(2).padStart(4, "0").split("").num()) + return new Packet(bits)[part2 ? "getValue" : "getVersionSum"]() +} + +if (typeof window == "undefined") { + module.exports = day16 +} +function day17(input, part2) { + let [minX, maxX, minY, maxY] = input.match(/([-\d]+)/g).num() + + let xVelRange = utils.signAgnosticInclusiveRange(0, maxX) + let yVelRange = utils.signAgnosticInclusiveRange(-minY, minY) + + let vels = xVelRange.cartProduct(yVelRange).map((e) => new Point(...e)).pt.filter((e) => { + let pos = new Point(0, 0) + let vel = e.copy() + + while (pos.x <= maxX && pos.y >= minY) { + if (pos.x >= minX && pos.y <= maxY) { + return true + } + + pos.addMut(vel) + + if (vel.x > 0) { + vel.x-- + } + + vel.y-- + + if (vel.y == 0) { + e.maxYPos = pos.y + } + } + + return false + }) + + if (!part2) { + // i feel like this is probably equivalent to minY * (minY + 1) / 2 but im not 100% sure for all inputs + return vels.max((e) => e.y).maxYPos + } else { + return vels.length + } +} + +if (typeof window == "undefined") { + module.exports = day17 +} +class SnailfishNumber { + static fromArr(arr, parent = null, isRight) { + let num = new SnailfishNumber() + return num.initialize( + arr[0] instanceof Array ? SnailfishNumber.fromArr(arr[0], num, false) : arr[0], + arr[1] instanceof Array ? SnailfishNumber.fromArr(arr[1], num, true) : arr[1], + parent, isRight) + } + + initialize(left, right, parent, isRight) { + this.left = left + this.right = right + this.parent = parent + this.isRight = isRight + return this + } + + explode(depth = 0) { + if (this.left instanceof SnailfishNumber) { + if (this.left.explode(depth + 1)) { + return true + } + } + + if (this.right instanceof SnailfishNumber) { + if (this.right.explode(depth + 1)) { + return true + } + } + + if (!(this.left instanceof SnailfishNumber) && !(this.right instanceof SnailfishNumber) && depth >= 4) { + let lastNode = this + let node = this.parent + + while (node && node.left == lastNode) { + lastNode = node + node = node.parent + } + + if (node) { + if (!(node.left instanceof SnailfishNumber)) { + node.left += this.left + } else { + node = node.left + + while (node.right instanceof SnailfishNumber) { + node = node.right + } + + node.right += this.left + } + } + + lastNode = this + node = this.parent + + while (node && node.right == lastNode) { + lastNode = node + node = node.parent + } + + if (node) { + if (!(node.right instanceof SnailfishNumber)) { + node.right += this.right + } else { + node = node.right + + while (node.left instanceof SnailfishNumber) { + node = node.left + } + + node.left += this.right + } + } + + if (!this.isRight) { + this.parent.left = 0 + } else { + this.parent.right = 0 + } + + return true + } + + return false + } + + split() { + if (this.left instanceof SnailfishNumber) { + if (this.left.split()) { + return true + } + } else if (this.left >= 10) { + this.left = SnailfishNumber.fromArr([Math.floor(this.left / 2), Math.ceil(this.left / 2)], this, false) + return true + } + + if (this.right instanceof SnailfishNumber) { + if (this.right.split()) { + return true + } + } else if (this.right >= 10) { + this.right = SnailfishNumber.fromArr([Math.floor(this.right / 2), Math.ceil(this.right / 2)], this, true) + return true + } + + return false + } + + reduce() { + while (this.explode() || this.split()); + return this + } + + add(that) { + let parent = SnailfishNumber.fromArr([this, that]) + this.parent = parent + this.isRight = false + that.parent = parent + that.isRight = true + return parent.reduce() + } + + magnitude() { + return 3 * (this.left instanceof SnailfishNumber ? this.left.magnitude() : this.left) + + 2 * (this.right instanceof SnailfishNumber ? this.right.magnitude() : this.right) + } + + toString() { + return "[" + this.left.toString() + "," + this.right.toString() + "]" + } +} + +function day18(input, part2) { + let lines = input.split("\n") + + if (!part2) { + let nums = lines.map((line) => SnailfishNumber.fromArr(JSON.parse(line))) + return nums.reduce((a, b) => a.add(b)).magnitude() + } else { + let pairs = lines.flatMap((e) => lines.filter((f) => e != f).map((f) => [e, f].map((e) => SnailfishNumber.fromArr(JSON.parse(e))))) + return pairs.map((e) => e[0].add(e[1]).magnitude()).max() + } +} + +if (typeof window == "undefined") { + module.exports = day18 +} +function allRotations(pt) { + return new PointArray( + new Point(pt.x, pt.y, pt.z), // facing +x, +z up + new Point(pt.x, pt.z, -pt.y), + new Point(pt.x, -pt.y, -pt.z), + new Point(pt.x, -pt.z, pt.y), + new Point(-pt.x, -pt.y, pt.z), // facing -x, +z up + new Point(-pt.x, -pt.z, -pt.y), + new Point(-pt.x, pt.y, -pt.z), + new Point(-pt.x, pt.z, pt.y), + new Point(-pt.y, pt.x, pt.z), // facing +y, +z up + new Point(-pt.y, pt.z, -pt.x), + new Point(-pt.y, -pt.x, -pt.z), + new Point(-pt.y, -pt.z, pt.x), + new Point(pt.y, -pt.x, pt.z), // facing -y, +z up + new Point(pt.y, -pt.z, -pt.x), + new Point(pt.y, pt.x, -pt.z), + new Point(pt.y, pt.z, pt.x), + new Point(pt.z, -pt.y, pt.x), // facing +z, -x up + new Point(pt.z, -pt.x, -pt.y), + new Point(pt.z, pt.y, -pt.x), + new Point(pt.z, pt.x, pt.y), + new Point(-pt.z, pt.y, pt.x), // facing -z, +x up + new Point(-pt.z, pt.x, -pt.y), + new Point(-pt.z, -pt.y, -pt.x), + new Point(-pt.z, -pt.x, pt.y) + ) +} + +const rotationsProto = allRotations(new Point(1, 2, 3)) + +const composedRotations = rotationsProto.mapArr((e) => allRotations(e).mapArr((e) => rotationsProto.indexOf(e))) +const inverseRotations = composedRotations.map((e) => e.indexOf(0)) + +function rotate(pt, t) { + return allRotations(pt)[t < 0 ? inverseRotations[-t] : t] +} + +class Transform { + constructor(translate, rotate) { + this.translate = translate + this.rotate = rotate + } + + transform(pt) { + return rotate(pt.add(this.translate), this.rotate) + } + + // R(x + T) = x' + // x = R'(x') - T = R'(x' - R(T)) + invert() { + return new Transform(rotate(this.translate, this.rotate).neg(), inverseRotations[this.rotate]) + } + + // x' = R2(R1(x + T1) + T2) = R2(R1(x) + R1(T1) + T2) + // T' = R1(T1) + T2; x' = (R2 o R1)((x) + R1^-1 (T')) + compose(that) { + return new Transform( + rotate(rotate(this.translate, this.rotate).add(that.translate), inverseRotations[this.rotate]), + composedRotations[this.rotate][that.rotate] + ) + } +} + +function day19(input, part2) { + let lines = input.split("\n") + + let scans = new PointArray() + + for (let line of lines) { + if (line.includes("---")) { + scans.push(new PointArray()) + } else if (line) { + scans.last.push(new Point(...line.split(",").num())) + } + } + + let transforms = Array(scans.length).fill().map((_, i) => { + let a = Array(scans.length).fill() + a[i] = new Transform(new Point(0, 0, 0), 0) + return a + }) + + let recurse = function(i) { + let scan = scans[i] + let sharedCount = 0 + + scan.checked = true + + for (let j = 0; j < scan.length - 11 - sharedCount; j++) { + let scanRel = scan.map((e) => e.sub(scan[j])) + let shared = false + + for (let k = 0; k < scans.length; k++) { + if (scans[k].checked) { + continue + } + + out: for (let t = 0; t < 24; t++) { + let scan2 = scans[k].map((e) => rotate(e, t)) + + for (let l = 0; l < scan2.length; l++) { + let scanRel2 = scan2.map((e) => e.sub(scan2[l])) + + let count = 0 + + for (let m = 0; m < scanRel.length; m++) { + if (m > scanRel.length - 12 + count) { + break + } + + for (let pt of scanRel2) { + if (scanRel[m].equals(pt)) { + count++ + } + } + } + + if (count >= 12) { + transforms[i][k] = new Transform(scan2[l].sub(scan[j]), inverseRotations[t]) + transforms[k][i] = transforms[i][k].invert() + + for (let m = 0; m < transforms.length; m++) { + if (m != i && transforms[m][i] && !transforms[m][k]) { + transforms[m][k] = transforms[m][i].compose(transforms[i][k]) + transforms[k][m] = transforms[m][k].invert() + } + + if (m != k && transforms[m][k] && !transforms[m][i]) { + transforms[m][i] = transforms[m][k].compose(transforms[k][i]) + transforms[i][m] = transforms[m][i].invert() + } + } + + console.log(`linked ${i}<->${k}\t(${i} point ${j} == ${k} point ${l} with rotation ${t})`) + + recurse(k) + + shared = true + break out + } + } + } + } + + if (shared) { + sharedCount++ + } + } + } + + recurse(0) + + if (!part2) { + let beacons = new PointArray() + + for (let i = 0; i < scans.length; i++) { + beacons.pushUniq(...scans[i].map((pt) => transforms[i][0].transform(pt))) + } + + return beacons.length + } else { + return transforms.flatMap((e) => e.map((f) => Math.abs(f.translate.x) + Math.abs(f.translate.y) + Math.abs(f.translate.z))).max() + } +} + +if (typeof window == "undefined") { + module.exports = day19 +} +function day1(input, part2) { + return input.split("\n").num().count((e, i, a, k = a[i - (part2 ? 3 : 1)]) => k && e > k) +} + +if (typeof window == "undefined") { + module.exports = day1 +} +function isEdge(pt, g) { + return pt.x == 0 || pt.x == g.width - 1 || pt.y == 0 || pt.y == g.height - 1 +} + +function expand(grid, fill) { + return new Grid(grid.width + 2, grid.height + 2).mapMut((_, pt, g) => isEdge(pt, g) ? fill : grid.get(pt.ul())) +} + +function day20(input, part2) { + let lines = input.split("\n").splitOnElement("") + + let code = lines[0][0].split("").map((e) => e == "#") + + let width = lines[1][0].length + let height = lines[1].length + + let grid = expand(expand(Grid.fromArr(lines[1]).mapMut((e) => e == "#"), false), false) + + for (let i = 0; i < (part2 ? 50 : 2); i++) { + grid = new Grid(grid.width, grid.height).mapMut((_, pt, g) => code[pt.getUnfilteredAllNeighborsIncSelf().map((pt2) => grid.get(grid.contains(pt2) ? pt2 : pt)).reduce((a, b) => 2 * a + b)]) + grid = expand(grid, grid.get(new Point(0, 0))) + } + + return grid.findAll((e) => e).length +} + +if (typeof window == "undefined") { + module.exports = day20 +} +// turn: 0-1; 1 bit (0 = player 1; 1 = player 2) +// pos1: 1-10; 4 bits +// pos2: 1-10; 4 bits +// score1: 0-1009; 10 bits +// score2: 0-1009; 10 bits +const sizeS = 10 +const sizeP = 4 +const sizeT = 1 + +const maskS = (1 << sizeS) - 1 +const maskP = (1 << sizeP) - 1 +const maskT = (1 << sizeT) - 1 + +const offsetS2 = 0 +const offsetS1 = offsetS2 + sizeS +const offsetP2 = offsetS1 + sizeS +const offsetP1 = offsetP2 + sizeP +const offsetT = offsetP1 + sizeP + +function encodeState(turn, pos1, pos2, score1, score2) { + return (score2 << offsetS2) | + (score1 << offsetS1) | + (pos2 << offsetP2) | + (pos1 << offsetP1) | + (turn << offsetT) +} + +function getScore(state, player) { + return state >> (player ? offsetS2 : offsetS1) & maskS +} + +function advState(state, roll, win) { + let turn = state >> offsetT + + let offsetPos = turn ? offsetP2 : offsetP1 + let offsetScore = turn ? offsetS2 : offsetS1 + + let pos = (state >> offsetPos) & maskP + let score = (state >> offsetScore) & maskS + + pos = (((pos + roll) - 1) % 10) + 1 + score += pos + + if (score >= win) { + return turn + } + + state &= ~(maskP << offsetPos) + state |= pos << offsetPos + + state &= ~(maskS << offsetScore) + state |= score << offsetScore + + state ^= 1 << offsetT + + return state +} + +const rollCombos = [1, 2, 3].cartProduct([1, 2, 3]).cartProduct([1, 2, 3]).map((e) => e.flat().sum()) + +function day21(input, part2) { + let lines = input.split("\n") + + let pos1 = +lines[0].split(" ").last + let pos2 = +lines[1].split(" ").last + + let state = encodeState(0, pos1, pos2, 0, 0) + + if (!part2) { + const win = 1000 + + let die = 0 + let rolls = 0 + + while (getScore(state, 0) < win && getScore(state, 1) < win) { + let roll = ++die + die %= 100 + roll += ++die + die %= 100 + roll += ++die + die %= 100 + + rolls += 3 + + newState = advState(state, roll, win) + + if (newState == 0 || newState == 1) { + return getScore(state, 1 - newState) * rolls + } + + state = newState + } + } else { + let universes = {} + universes[state] = 1 + + let wins = [0, 0] + + while (true) { + let universesLeft = false + + for (let state in universes) { + if (!universes[state]) { + continue + } + + universesLeft = true + + state = +state + + for (let roll of rollCombos) { + let newState = advState(state, roll, 21) + + if (newState == 0 || newState == 1) { + wins[newState] += universes[state] + } else { + universes[newState] = (universes[newState] || 0) + universes[state] + } + } + + delete universes[state] + } + + if (!universesLeft) { + break + } + } + + return wins.max() + } +} + +if (typeof window == "undefined") { + module.exports = day21 +} +class Cuboid { + constructor(sgn, xs, xe, ys, ye, zs, ze) { + this.sgn = sgn + this.xs = Math.min(xs, xe) + this.xe = Math.max(xs, xe) + this.ys = Math.min(ys, ye) + this.ye = Math.max(ys, ye) + this.zs = Math.min(zs, ze) + this.ze = Math.max(zs, ze) + } + + vol() { return (this.xe - this.xs + 1) * (this.ye - this.ys + 1) * (this.ze - this.zs + 1) } + + int(that) { + if (that.xe < this.xs || this.xe < that.xs) return + if (that.ye < this.ys || this.ye < that.ys) return + if (that.ze < this.zs || this.ze < that.zs) return + + let sgn = -that.sgn + + let xs = Math.max(this.xs, that.xs) + let xe = Math.min(this.xe, that.xe) + let ys = Math.max(this.ys, that.ys) + let ye = Math.min(this.ye, that.ye) + let zs = Math.max(this.zs, that.zs) + let ze = Math.min(this.ze, that.ze) + + return new Cuboid(sgn, xs, xe, ys, ye, zs, ze) + } +} + +function day20(input, part2) { + let lines = input.split("\n") + + let cuboids = [] + + for (let line of lines) { + let data = line.match(/^(.+) x=(.+)\.\.(.+),y=(.+)\.\.(.+),z=(.+)\.\.(.+)$/) + let cuboid = new Cuboid(data[1] == "on", +data[2], +data[3], +data[4], +data[5], +data[6], +data[7]) + + if (!part2 && ( + cuboid.xs < -50 || cuboid.xe > 50 || + cuboid.ys < -50 || cuboid.ye > 50 || + cuboid.zs < -50 || cuboid.ze > 50)) { + continue + } + + let toAdd = [cuboid] + + for (let otherCuboid of cuboids) { + let intersection = cuboid.int(otherCuboid) + + if (intersection) { + toAdd.push(intersection) + } + } + + cuboids.push(...toAdd) + } + + return cuboids.map((e) => e.vol() * e.sgn).sum() +} + +if (typeof window == "undefined") { + module.exports = day20 +} +const statesArr = [] +const stateIdxs = {} + +function encodeState(arr) { + let key = arr.join("") + + if (!(key in stateIdxs)) { + stateIdxs[key] = statesArr.push(arr) - 1 + } + + return stateIdxs[key] +} + +function decodeState(num) { + return statesArr[num] +} + +// 0 is hallway, 1 is A room, 2 is B room, etc +function getRoom(pos) { + if (pos < 11) { + return 0 + } + + return ((pos - 11) % 4) + 1 +} + +let hallwayStops = [0, 1, 3, 5, 7, 9, 10] +let roomPoses = [] + +function possibleStates(num) { + let state = decodeState(num) + + let newStates = [] + + for (let pos = 0; pos < state.length; pos++) { + let amphipod = state[pos] + + if (!amphipod) { + continue + } + + let curRoom = getRoom(pos) + + let roomEntrance = 2 * curRoom + let destRoomEntrance = 2 * amphipod + + let curRoomPoses = roomPoses[getRoom(pos)] + let destRoomPoses = roomPoses[amphipod] + + let depthInRoom = curRoomPoses?.indexOf(pos) + + if (curRoom == amphipod && curRoomPoses.slice(depthInRoom).every((e) => state[e] == amphipod)) { + continue + } + + let dests = [] + + let destRoomAvailable = destRoomPoses.every((pos) => state[pos] == 0 || state[pos] == amphipod) + + let steps = 0 + let hallwayPos + + if (curRoom != 0) { + if (curRoomPoses.slice(0, depthInRoom).some((pos) => state[pos] != 0)) { + continue + } + + destRoomAvailable &&= roomEntrance != destRoomEntrance + + steps = depthInRoom + 1 + hallwayPos = roomEntrance + } else { + hallwayPos = pos + } + + let destRoomSteps = -1 + + let leftPath = hallwayStops.filter((pos) => pos < hallwayPos).reverse() + let rightPath = hallwayStops.filter((pos) => pos > hallwayPos) + + let leftBlock = leftPath.findIndex((pos) => state[pos] != 0) + let rightBlock = rightPath.findIndex((pos) => state[pos] != 0) + + let leftDests = (leftBlock > -1 ? leftPath.slice(0, leftBlock) : leftPath) + .map((pos) => [pos, steps + hallwayPos - pos]) + let rightDests = (rightBlock > -1 ? rightPath.slice(0, rightBlock) : rightPath) + .map((pos) => [pos, steps + pos - hallwayPos]) + + if (curRoom != 0) { + dests.push(...leftDests, ...rightDests) + } + + if (destRoomAvailable) { + destRoomSteps = + [...leftDests, [hallwayPos, steps]].find((e) => e[0] == destRoomEntrance + 1)?.[1] ?? + [...rightDests, [hallwayPos, steps]].find((e) => e[0] == destRoomEntrance - 1)?.[1] ?? -1 + } + + if (destRoomSteps > -1) { + let destRoomBlock = destRoomPoses.findIndex((pos) => state[pos] != 0) + let destPosIdx = (destRoomBlock > -1 ? destRoomBlock : destRoomPoses.length) - 1 + dests.push([destRoomPoses[destPosIdx], destRoomSteps + 2 + destPosIdx]) + } + + newStates.push(...dests.map((e) => { + let newState = state.copy() + + newState[pos] = 0 + newState[e[0]] = amphipod + + return { state: encodeState(newState), energy: e[1] * [1, 10, 100, 1000][amphipod - 1] } + })) + } + + return newStates +} + +function isSolved(num) { + let state = decodeState(num) + return roomPoses.every((poses, i) => poses.every((pos) => state[pos] == i)) +} + +function day23(input, part2) { + let lines = input.split("\n") + + if (part2) { + lines.splice(3, 0, " #D#C#B#A#", " #D#B#A#C#") + } + + for (let i = 1; i <= 4; i++) { + roomPoses[i] = Array(part2 ? 4 : 2).fill().map((_, j) => 10 + j * 4 + i) + } + + let state = encodeState(lines.join("").replace(/[# ]/g, "") + .replaceAll(".", 0) + .replaceAll("A", 1) + .replaceAll("B", 2) + .replaceAll("C", 3) + .replaceAll("D", 4).split("").num()) + + let energies = {} + energies[state] = 0 + + let stateHeap = new BinHeap((state1, state2) => state1.totalEnergy < state2.totalEnergy) + stateHeap.insert({ state: state, totalEnergy: 0 }) + + for (let i = 0; ; i++) { + let top = stateHeap.extract() + + if (isSolved(top.state)) { + return top.totalEnergy + } + + for (let res of possibleStates(top.state)) { + let energy = top.totalEnergy + res.energy + + if (res.state in energies) { + let idx = stateHeap.data.findIndex((e) => e.state == res.state) + if (idx > -1 && energy < stateHeap.data[idx].totalEnergy) { + stateHeap.data[idx].totalEnergy = energy + stateHeap.up(idx) + } + } else { + energies[res.state] = energy + stateHeap.insert({ state: res.state, totalEnergy: energy }) + } + } + + if (i % 10000 == 0) { + console.log("iteration", i, "heap size", stateHeap.data.length, "min energy", top.totalEnergy) + } + } +} + +if (typeof window == "undefined") { + module.exports = day23 +} +function day24(input, part2) { + let lines = input.split("\n") + let ops = lines.splitOnElement("inp w").filter((e) => e.length).transpose() + + let compareVals = ops[4].map((e) => +e.split(" ").last) + let stackVals = ops[14].map((e) => +e.split(" ").last) + + let len = compareVals.length + + let stack = [] + let num = Array(len).fill() + + for (let i = 0; i < len; i++) { + if (compareVals[i] > 0) { + stack.push([i, stackVals[i]]) + } else { + num[i] = stack.pop() + num[i][1] += compareVals[i] + num[num[i][0]] = [i, -num[i][1]] + } + } + + for (let i = 0; i < len; i++) { + if (num[i][0] > i) { + num[i] = part2 ? Math.max(1, 1 + num[i][1]) : Math.min(9, 9 + num[i][1]) + } else { + num[i] = num[num[i][0]] + num[i][1] + } + } + + return num.join("") +} + +if (typeof window == "undefined") { + module.exports = day24 +} +function step(grid, dir) { + let moved = false + + grid.mapMut((e, pt, g) => { + let target = dir == 1 ? + new Point((pt.x + 1) % g.width, pt.y) : + new Point(pt.x, (pt.y + 1) % g.height) + + if (e % 3 == dir && g.get(target) % 3 == 0) { + g.set(target, dir * 3) + moved = true + return e + } else { + return e * 3 + e + } + }).mapMut((e) => ((e / 3) | 0) % 3) + + return moved +} + +function day25(input) { + let grid = Grid.fromStr(input).mapMut((e) => ".>v".indexOf(e)) + + let moved + let steps = 0 + + do { + steps++ + } while (step(grid, 1) + step(grid, 2)) + + return steps +} + +if (typeof window == "undefined") { + module.exports = day25 +} +function day2(input, part2) { + let x = 0 + let y = 0 + let a = 0 + + for (let line of input.split("\n")) { + if (!part2) { + eval(line.replace("forward", "x +=") + .replace("up", "y -=") + .replace("down", "y +=")) + } else { + eval(line.replace(/forward (.+)/, "x += $1; y += a * $1") + .replace("up", "a -=") + .replace("down", "a +=")) + } + } + + return x * y +} + +if (typeof window == "undefined") { + module.exports = day2 +} +function day3(input, part2) { + let g = Grid.fromStr(input) + + if (!part2) { + let gamma = g.getColumns().map((col) => col.mode()) + let epsilon = g.getColumns().map((col) => col.antimode()) + return parseInt(gamma.join(""), 2) * parseInt(epsilon.join(""), 2) + } else { + let oxy = g.getRows() + let co2 = g.getRows() + + for (let i = 0; i < g.width; i++) { + let oxyBit = oxy.transpose()[i].mode((a, b) => a == 1 ? -1 : 1) + let co2Bit = co2.transpose()[i].antimode((a, b) => a == 0 ? -1 : 1) + oxy = oxy.filter((e) => e[i] == oxyBit) + co2 = co2.filter((e) => e[i] == co2Bit) + } + + return parseInt(oxy[0].join(""), 2) * parseInt(co2[0].join(""), 2) + } +} + +if (typeof window == "undefined") { + module.exports = day3 +} +function day4(input, part2) { + let lines = input.split("\n") + + let seq = lines.shift().split(",").num() + let grids = lines.splitOnElement("").filter((e) => e.length).map((grid) => Grid.fromStr(grid.map((line) => line.replace(/^ /, "")).join("\n"), /\s+/).mapMut((e) => [+e, false])) + + let score + + for (let num of seq) { + for (let grid of grids) { + if (grid.won) { + continue + } + + let pt = grid.findIndex((e) => e[0] == num) + + if (pt == Point.NONE) { + continue + } + + grid.set(pt, [num, true]) + + if ([...grid.getRows(), ...grid.getColumns()].some((row) => row.every((e) => e[1]))) { + grid.won = true + + if (!score || part2) { + score = grid.findAll((e) => !e[1]).map((e) => e[0]).sum() * num + } + } + } + } + + return score +} + +if (typeof window == "undefined") { + module.exports = day4 +} +function day5(input, part2) { + let lines = input.split("\n").map((e) => e.split(" -> ").map((e) => new Point(...e.split(",").num()))) + + if (!part2) { + lines = lines.filter((e) => e[0].x == e[1].x || e[0].y == e[1].y) + } + + let visitedOnce = new Set() + let visitedTwice = new Set() + + for (let line of lines) { + let xrange = utils.signAgnosticInclusiveRange(line[0].x, line[1].x) + let yrange = utils.signAgnosticInclusiveRange(line[0].y, line[1].y) + + if (xrange.length == 1) { + xrange = yrange.map(() => line[0].x) + } + + if (yrange.length == 1) { + yrange = xrange.map(() => line[0].y) + } + + for (let i = 0; i < xrange.length; i++) { + let mask = (xrange[i] << 10) | yrange[i] + + if (visitedOnce.has(mask)) { + visitedTwice.add(mask) + } else { + visitedOnce.add(mask) + } + } + } + + return visitedTwice.size +} + +if (typeof window == "undefined") { + module.exports = day5 +} +function day6(input, part2) { + let fish = input.split(",").num() + let counts = Array(9).fill().map((_, i) => fish.count(i)) + + for (let i = 0; i < (part2 ? 256 : 80); i++) { + counts.push(counts.shift()) + counts[6] += counts[8] + } + + return counts.sum() +} + +if (typeof window == "undefined") { + module.exports = day6 +} +function day7(input, part2) { + let crabs = input.split(",").num() + + let max = crabs.max() + let costs = [] + + for (let i = 0; i < max; i++) { + costs[i] = crabs.map((e) => { + let dist = Math.abs(e - i) + return part2 ? dist * (dist + 1) / 2 : dist + }).sum() + } + + return costs.min() +} + +if (typeof window == "undefined") { + module.exports = day7 +} +function day8(input, part2) { + let lines = input.split("\n").map((e) => e.split(" ").splitOnElement("|").map((e) => e.map((e) => e.split("").sort()))) + + if (!part2) { + return lines.map((e) => e[1].count((e) => [2, 4, 3, 7].includes(e.length))).sum() + } + + let sum = 0 + + for (let line of lines) { + let samples = line.flat() + + let n1 = samples.find((e) => e.length == 2) + let n7 = samples.find((e) => e.length == 3) + let n4 = samples.find((e) => e.length == 4) + let a235 = samples.filter((e) => e.length == 5) + let a069 = samples.filter((e) => e.length == 6) + let n8 = samples.find((e) => e.length == 7) + + let n6 = a069.find((e) => n1.sub(e).length == 1) + let n9 = a069.find((e) => n4.sub(e).length == 0) + let n0 = a069.find((e) => e != n6 && e != n9) + + let n2 = a235.find((e) => n4.sub(e).length == 2) + let n3 = a235.find((e) => n1.sub(e).length == 0) + let n5 = a235.find((e) => e != n2 && e != n3) + + sum += +line[1].map((e) => [n0, n1, n2, n3, n4, n5, n6, n7, n8, n9].findIndex((n) => n.join("") == e.join(""))).join("") + } + + return sum +} + +if (typeof window == "undefined") { + module.exports = day8 +} +function day9(input, part2) { + let grid = Grid.fromStr(input).num() + + let lows = grid.findAllIndices((e, pt, g) => g.getAdjNeighbors(pt).every((nb) => e < g.get(nb))) + + if (!part2) { + return lows.mapArr((e) => grid.get(e) + 1).sum() + } + + let sizes = lows.mapArr((low) => grid.bfs(low, (e, pt, g) => { + return e == 9 || g.get(pt.path.last) < e ? Grid.BFS_STOP : Grid.BFS_CONTINUE + }).filter((e) => e.result == Grid.BFS_CONTINUE).length).sort((a, b) => b - a) + + return sizes[0] * sizes[1] * sizes[2] +} + +if (typeof window == "undefined") { + module.exports = day9 +}