advent-of-code/2022/17-Pyroclastic-Flow/second.js
2022-12-27 18:47:23 +01:00

213 lines
4.8 KiB
JavaScript

import * as fs from "fs";
function load(filename) {
let data = fs.readFileSync(filename, "utf8").trim();
const l = data.length;
let i = 0;
return {
next: function () {
const c = data[i];
i = (i + 1) % l;
return c;
}
};
}
const masks = [
0b1000000,
0b0100000,
0b0010000,
0b0001000,
0b0000100,
0b0000010,
0b0000001,
];
const shapes = [
[0b0011110],
[0b0001000, 0b0011100, 0b0001000],
[0b0000100, 0b0000100, 0b0011100],
[0b0010000, 0b0010000, 0b0010000, 0b0010000],
[0b0011000, 0b0011000],
];
const shapes_len = shapes.length;
class Shape {
constructor(shape) {
this.shape = shape;
this.length = shape.length;
}
fall(field) { // returns true if the shape fell without colliding
for (let i=0; i<this.length; ++i) {
const next = field.get(i+1);
if (next === undefined || next & this.shape[i]) { //collision
field.solidify(this.shape);
return false;
}
}
field.fall();
return true;
}
shift(field, direction) {
let collision = false;
let tmp;
switch(direction) {
case ">":
tmp = this.shape.map(line => {
if (line & 0b1) { // we touch the right edge already
collision = true;
}
return line >>> 1;
});
break;
case "<":
tmp = this.shape.map(line => {
if (line & 0b1000000) { // we touch the left edge already
collision = true;
}
return line << 1;
});
break;
default:
throw "invalid direction character in shape shift: " + direction;
}
if (collision) {
return;
}
for (let i=0; i<this.length; ++i) {
if (field.get(i) & tmp[i]) { //collision
return;
}
}
this.shape = tmp;
}
print() {
this.shape.forEach(line => {
console.log(
line & 0b1000000 ? "#" : ".",
line & 0b0100000 ? "#" : ".",
line & 0b0010000 ? "#" : ".",
line & 0b0001000 ? "#" : ".",
line & 0b0000100 ? "#" : ".",
line & 0b0000010 ? "#" : ".",
line & 0b0000001 ? "#" : ".",
);
});
console.log("=======");
}
}
class Shaper {
constructor() {
this.index = 0;
}
next(field) {
const s = new Shape(shapes[this.index]);
field.accomodate(shapes[this.index].length + 3);
this.index = (this.index + 1) % shapes_len;
return s;
}
}
class Field {
constructor() {
this.data = [];
this.offset = 0;
}
accomodate(n) {
this.offset = 0;
for(let i=0; i<n; ++i) {
this.data.unshift(0);
}
}
fall() {
if (this.data[0] !== 0) { // we need to fall bellow the top line?
this.offset++;
} else {
this.data.shift();
}
}
get(offset) {
return this.data[this.offset + offset];
}
heightMap() {
const max = this.data.length;
let heights = [max, max, max, max, max, max, max];
heights.forEach((h, i) => {
for(let j=0; j<this.data.length; ++j) {
if (this.data[j] & masks[i]) {
heights[i] = j;
break;
}
}
});
return heights;
}
print() {
this.data.forEach(line => {
console.log(
line & 0b1000000 ? "#" : ".",
line & 0b0100000 ? "#" : ".",
line & 0b0010000 ? "#" : ".",
line & 0b0001000 ? "#" : ".",
line & 0b0000100 ? "#" : ".",
line & 0b0000010 ? "#" : ".",
line & 0b0000001 ? "#" : ".",
);
});
console.log("-------");
}
solidify(shape) {
for(let i=0; i<shape.length; ++i) {
this.data[i+this.offset] |= shape[i];
}
}
}
let example = load("example");
let input = load("input");
function solve(input) {
let field = new Field();
let shaper = new Shaper();
let height = 0;
let heightmaps = [];
const sky = 1000000000000;
for(let i=0; i<sky; ++i) {
let shape = shaper.next(field);
do {
shape.shift(field, input.next());
} while(shape.fall(field));
const heightmap = field.heightMap();
let cycle = -1;
let cycleHeight = 0;
heightmaps.forEach((h, n) => {
if (h.heightmap.every((v, idx) => v === heightmap[idx])) {
let nextCycle = heightmaps[(n+1)*2-1];
if (nextCycle !== undefined && nextCycle.heightmap.every((v, idx) => v === heightmap[idx])) {
let nextCycle2 = heightmaps[(n+1)*3-1];
if (nextCycle2 !== undefined && nextCycle2.heightmap.every((v, idx) => v === heightmap[idx])) {
cycle = n+1;
cycleHeight = field.data.length - h.height;
}
}
}
});
if (cycle > 0) {
console.log("got cycle of", cycle, "with a height of", cycleHeight, "after", i, "iterations");
const cycles = Math.floor((sky - i) / cycle);
i += cycles * cycle;
height = cycles * cycleHeight;
console.log("computed a height of", height, "after", cycles * cycle, "additional cycles");
heightmaps = [];
}
heightmaps.unshift({ heightmap: heightmap, height: field.data.length });
}
return height + field.data.length;
}
const exampleOutput = solve(example);
if (exampleOutput !== 1514285714288) {
console.log("Example failed with height " + exampleOutput, "diff is", exampleOutput - 1514285714288, "positive means too high");
process.exit(1); // eslint-disable-line
}
console.log(solve(input));