From 1abf42137aa7bf0b24622f333e22af7db67a8fff Mon Sep 17 00:00:00 2001 From: nim-ka Date: Tue, 22 Nov 2022 21:12:29 +0000 Subject: [PATCH] . --- 2021/19.js | 179 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 2021/9.js | 4 +- grid.js | 2 +- out.js | 80 ++++++++++++++++++++---- proto.js | 61 +++++++++++++++--- pt.js | 15 +++++ test.js | 2 +- 7 files changed, 317 insertions(+), 26 deletions(-) create mode 100644 2021/19.js diff --git a/2021/19.js b/2021/19.js new file mode 100644 index 0000000..dc67a62 --- /dev/null +++ b/2021/19.js @@ -0,0 +1,179 @@ +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 tree = Array(scans.length).fill().map((_, i) => [i]) + + for (let i = 0; i < scans.length - 1; i++) { + let scan = scans[i] + + for (let j = 0; j < scan.length; j++) { + if (scan[j].link) { + continue + } + + let scanRel = scan.map((e) => e.sub(scan[j])) + + out: for (let k = i + 1; k < scans.length; k++) { + if (transforms[i][k]) { + continue + } + + if (tree[0].includes(i) && tree[0].includes(k)) { + continue + } + + for (let t2 = 0; t2 < 24; t2++) { + let scan2 = scans[k].map((e) => rotate(e, t2)) + + for (let l = 0; l < scan2.length; l++) { + let scanRel2 = scan2.map((e) => e.sub(scan2[l])) + + let overlaps = scanRel.int(scanRel2) + + if (overlaps.length >= 12) { + console.log([i, j, k, l, t2]) + + tree[i].push(k) + tree[k].push(i) + + if (tree[0].includes(i)) { + tree[0].pushUniq(k) + } + + if (tree[0].includes(k)) { + tree[0].pushUniq(i) + } + + transforms[i][k] = new Transform(scan2[l].sub(scan[j]), inverseRotations[t2]) + transforms[k][i] = transforms[i][k].invert() + + break out + } + } + } + } + + console.log(i, j) + } + } + + // this sucks on another level but hey it works ? + Array(transforms.length).fill().forEach((_, i) => { + (function recurse(i) { + for (let j = 0; j < transforms[i].length; j++) { + if (i == j || !transforms[i][j]) { + continue + } + + for (let k = 0; k < transforms[j].length; k++) { + if (transforms[i][k] || !transforms[j][k]) { + continue + } + + transforms[i][k] = transforms[i][j].compose(transforms[j][k]) + transforms[k][i] = transforms[i][k].invert() + + console.log(`filled in ${i}<->${k} from ${i}<->${j}<->${k}`) + + recurse(j) + } + } + })(i) + }) + + 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 +} diff --git a/2021/9.js b/2021/9.js index 068f5d1..4574ff9 100644 --- a/2021/9.js +++ b/2021/9.js @@ -4,10 +4,10 @@ function day9(input, part2) { let lows = grid.findAllIndices((e, pt, g) => g.getAdjNeighbors(pt).every((nb) => e < g.get(nb))) if (!part2) { - return lows.map((e) => grid.get(e) + 1).sum() + return lows.mapArr((e) => grid.get(e) + 1).sum() } - let sizes = lows.map((low) => grid.bfs(low, (e, pt, g) => { + 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) diff --git a/grid.js b/grid.js index d75990d..bafd868 100644 --- a/grid.js +++ b/grid.js @@ -108,7 +108,7 @@ Grid = class Grid { } findAll(func) { - return this.findAllIndices(func).map((pt) => this.get(pt)) + return this.findAllIndices(func).mapArr((pt) => this.get(pt)) } count(func) { diff --git a/out.js b/out.js index f2eb90f..0e7c38f 100644 --- a/out.js +++ b/out.js @@ -339,6 +339,21 @@ Pt = Point = class Point { return this } + mult(n) { return new Point(this.x * n, this.y * n, this.is3D ? this.z * n : undefined) } + multMut(n) { + this.x *= n + this.y *= n + + if (this.is3D) { + this.z *= n + } + + return this + } + + neg(n) { return this.mult(-1) } + negMut(n) { return this.multMut(-1) } + squaredMag() { return this.x * this.x + this.y * this.y + (this.is3D ? this.z * this.z : 0) } mag() { return Math.sqrt(this.squaredMag()) } @@ -484,7 +499,7 @@ Grid = class Grid { } findAll(func) { - return this.findAllIndices(func).map((pt) => this.get(pt)) + return this.findAllIndices(func).mapArr((pt) => this.get(pt)) } count(func) { @@ -897,6 +912,18 @@ load = function load() { }, configurable: true }, + mapArr: { + value: function(fn) { + const mapped = new Array(this.length) + + for (let i = 0; i < this.length; i++) { + mapped[i] = fn(this[i], i, this) + } + + return mapped + }, + configurable: true + }, sum: { value: function(val = 0) { return this.reduce((a, b) => a + b, val) @@ -923,7 +950,7 @@ load = function load() { }, transpose: { value: function() { - return Array(this[0].length).fill().map((_, i) => this.map(e => e[i])) + return this[0].map((_, i) => this.map(e => e[i])) }, configurable: true }, @@ -955,7 +982,7 @@ load = function load() { }, configurable: true }, - rotate: { + rotateLeft: { value: function(n) { let k = (this.length + n) % this.length return [...this.slice(k), ...this.slice(0, k)] @@ -1095,19 +1122,13 @@ load = function load() { }, configurable: true }, - map: { - value: function(...args) { - return this.arr.map(...args) - }, - configurable: true - }, splitOnElement: { value: function(sep) { - let arr = [[]] + let arr = [new PointArray()] for (let i = 0; i < this.length; i++) { if (this[i].equals(sep)) { - arr.push([]) + arr.push(new PointArray()) } else { arr[arr.length - 1].push(this[i]) } @@ -1117,6 +1138,41 @@ load = function load() { }, configurable: true }, + map: { + value: function(fn) { + const mapped = new PointArray(this.length) + + for (let i = 0; i < this.length; i++) { + mapped[i] = fn(this[i], i, this) + } + + return mapped + }, + configurable: true + }, + cartProduct: { + value: function(that) { + return this.flatMap((e) => that.map((f) => new PointArray(e, f))) + }, + configurable: true + }, + interleave: { + value: function(that = new PointArray()) { + return new PointArray(this, that).transpose().flat() + }, + configurable: true + }, + rotateLeft: { + value: function(n) { + if (this.length == 1) { + return this.copy() + } + + let k = (this.length + n) % this.length + return new PointArray(...this.slice(k), ...this.slice(0, k)) + }, + configurable: true + }, sort: { value: function(func = (a, b) => a.readingOrderCompare(b)) { return Array.prototype.sort.apply(this, func) @@ -1188,7 +1244,7 @@ if (typeof window == "undefined" && process.argv[2] == "test") { const year = "2021" - for (let i = 1; i <= 18; i++) { + for (let i = +process.argv[3] || 1; i <= 19; i++) { const func = require(`./${year}/${i}.js`) const input = fs.readFileSync(`./${year}/inputs/${i}`, "utf8") const answers = fs.readFileSync(`./${year}/answers/${i}`, "utf8").split("\n-----\n") diff --git a/proto.js b/proto.js index f8fbd31..948e7f8 100644 --- a/proto.js +++ b/proto.js @@ -96,6 +96,18 @@ load = function load() { }, configurable: true }, + mapArr: { + value: function(fn) { + const mapped = new Array(this.length) + + for (let i = 0; i < this.length; i++) { + mapped[i] = fn(this[i], i, this) + } + + return mapped + }, + configurable: true + }, sum: { value: function(val = 0) { return this.reduce((a, b) => a + b, val) @@ -122,7 +134,7 @@ load = function load() { }, transpose: { value: function() { - return Array(this[0].length).fill().map((_, i) => this.map(e => e[i])) + return this[0].map((_, i) => this.map(e => e[i])) }, configurable: true }, @@ -154,7 +166,7 @@ load = function load() { }, configurable: true }, - rotate: { + rotateLeft: { value: function(n) { let k = (this.length + n) % this.length return [...this.slice(k), ...this.slice(0, k)] @@ -294,19 +306,13 @@ load = function load() { }, configurable: true }, - map: { - value: function(...args) { - return this.arr.map(...args) - }, - configurable: true - }, splitOnElement: { value: function(sep) { - let arr = [[]] + let arr = [new PointArray()] for (let i = 0; i < this.length; i++) { if (this[i].equals(sep)) { - arr.push([]) + arr.push(new PointArray()) } else { arr[arr.length - 1].push(this[i]) } @@ -316,6 +322,41 @@ load = function load() { }, configurable: true }, + map: { + value: function(fn) { + const mapped = new PointArray(this.length) + + for (let i = 0; i < this.length; i++) { + mapped[i] = fn(this[i], i, this) + } + + return mapped + }, + configurable: true + }, + cartProduct: { + value: function(that) { + return this.flatMap((e) => that.map((f) => new PointArray(e, f))) + }, + configurable: true + }, + interleave: { + value: function(that = new PointArray()) { + return new PointArray(this, that).transpose().flat() + }, + configurable: true + }, + rotateLeft: { + value: function(n) { + if (this.length == 1) { + return this.copy() + } + + let k = (this.length + n) % this.length + return new PointArray(...this.slice(k), ...this.slice(0, k)) + }, + configurable: true + }, sort: { value: function(func = (a, b) => a.readingOrderCompare(b)) { return Array.prototype.sort.apply(this, func) diff --git a/pt.js b/pt.js index c543bc6..7ff840b 100644 --- a/pt.js +++ b/pt.js @@ -167,6 +167,21 @@ Pt = Point = class Point { return this } + mult(n) { return new Point(this.x * n, this.y * n, this.is3D ? this.z * n : undefined) } + multMut(n) { + this.x *= n + this.y *= n + + if (this.is3D) { + this.z *= n + } + + return this + } + + neg(n) { return this.mult(-1) } + negMut(n) { return this.multMut(-1) } + squaredMag() { return this.x * this.x + this.y * this.y + (this.is3D ? this.z * this.z : 0) } mag() { return Math.sqrt(this.squaredMag()) } diff --git a/test.js b/test.js index e78fb54..d55b65e 100644 --- a/test.js +++ b/test.js @@ -3,7 +3,7 @@ if (typeof window == "undefined" && process.argv[2] == "test") { const year = "2021" - for (let i = 1; i <= 18; i++) { + for (let i = +process.argv[3] || 1; i <= 19; i++) { const func = require(`./${year}/${i}.js`) const input = fs.readFileSync(`./${year}/inputs/${i}`, "utf8") const answers = fs.readFileSync(`./${year}/answers/${i}`, "utf8").split("\n-----\n")