.
This commit is contained in:
179
2021/19.js
Normal file
179
2021/19.js
Normal file
@@ -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
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
|
||||
2
grid.js
2
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) {
|
||||
|
||||
80
out.js
80
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")
|
||||
|
||||
61
proto.js
61
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)
|
||||
|
||||
15
pt.js
15
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()) }
|
||||
|
||||
|
||||
2
test.js
2
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")
|
||||
|
||||
Reference in New Issue
Block a user