|
|
@@ -0,0 +1,135 @@
|
|
|
+
|
|
|
+function readAll(cb) {
|
|
|
+ require("fs").readFile('./input', 'utf8', (err, data) => {
|
|
|
+ var world = new World();
|
|
|
+ data.split("\n").forEach(line => {
|
|
|
+ if (!line)
|
|
|
+ return;
|
|
|
+ var numbers = line.match(/(\d+)/g).map(i => parseInt(i));
|
|
|
+ if (line.charAt(0) == 'x')
|
|
|
+ world.addData(numbers[0], numbers[0], numbers[1], numbers[2], 'x');
|
|
|
+ else
|
|
|
+ world.addData(numbers[1], numbers[2], numbers[0], numbers[0], 'x');
|
|
|
+ });
|
|
|
+ cb(world);
|
|
|
+ });
|
|
|
+}
|
|
|
+
|
|
|
+function World() {
|
|
|
+ this.data = {};
|
|
|
+ this.sources = [[ 500, 0 ]];
|
|
|
+ this.minX = 500;
|
|
|
+ this.maxX = 500;
|
|
|
+ this.minY = 0;
|
|
|
+ this.maxY = 0;
|
|
|
+}
|
|
|
+
|
|
|
+World.prototype.addData = function(x1, x2, y1, y2, type) {
|
|
|
+ while (y1 <= y2) {
|
|
|
+ for (var j = x1; j <= x2; ++j) {
|
|
|
+ this.data[y1] = this.data[y1] || {};
|
|
|
+ this.data[y1][j] = type;
|
|
|
+ }
|
|
|
+ ++y1;
|
|
|
+ }
|
|
|
+ this.minX = Math.min(x1, this.minX);
|
|
|
+ this.maxX = Math.max(x2, this.maxX);
|
|
|
+ if (type == 'x') {
|
|
|
+ this.minY = Math.min(y1, this.minY);
|
|
|
+ this.maxY = Math.max(y2, this.maxY);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+World.prototype.getChar = function(x, y, defaultValue) {
|
|
|
+ return this.data[y] ? this.data[y][x] || defaultValue : defaultValue;
|
|
|
+}
|
|
|
+
|
|
|
+World.prototype.isEmpty = function(x, y) {
|
|
|
+ var c = this.getChar(x, y, false);
|
|
|
+ return !c || c == '|';
|
|
|
+}
|
|
|
+
|
|
|
+World.prototype.nextClay = function(x, y, direction) {
|
|
|
+ for (; x >= this.minX && x <= this.maxX; x += direction) {
|
|
|
+ if (this.isEmpty(x, y +1))
|
|
|
+ return false;
|
|
|
+ if (!this.isEmpty(x, y))
|
|
|
+ return x;
|
|
|
+ }
|
|
|
+ return false;
|
|
|
+}
|
|
|
+
|
|
|
+World.prototype.trySettle = function(minX, maxX, y) {
|
|
|
+ if (minX === false || maxX === false || minX === maxX)
|
|
|
+ return false;
|
|
|
+ this.addData(minX +1, maxX -1, y, y, '~');
|
|
|
+ return true;
|
|
|
+}
|
|
|
+
|
|
|
+World.prototype.water = function() {
|
|
|
+ var x = this.sources[0][0],
|
|
|
+ y = this.sources[0][1];
|
|
|
+ while (this.isEmpty(x, y +1) && y <= this.maxY)
|
|
|
+ ++y;
|
|
|
+ if (y <= this.maxY) {
|
|
|
+ var minX = this.nextClay(x, y, -1),
|
|
|
+ maxX = this.nextClay(x, y, +1);
|
|
|
+ if (this.trySettle(minX, maxX, y))
|
|
|
+ return;
|
|
|
+ var found = false;
|
|
|
+ for (var tmp = x; this.isEmpty(tmp, y); ++tmp) {
|
|
|
+ this.addData(tmp, tmp, y, y, '|');
|
|
|
+ if (this.isEmpty(tmp, y +1)) {
|
|
|
+ this.sources.push([ tmp, y ]);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ for (var tmp = x; this.isEmpty(tmp, y); --tmp) {
|
|
|
+ this.addData(tmp, x, y, y, '|');
|
|
|
+ if (this.isEmpty(tmp, y +1)) {
|
|
|
+ this.addData(tmp, x, y, y, '|');
|
|
|
+ this.sources.push([ tmp, y ]);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ this.addData(x, x, this.sources[0][1], y, '|');
|
|
|
+ this.sources.splice(0, 1);
|
|
|
+}
|
|
|
+
|
|
|
+World.prototype.countWater = function() {
|
|
|
+ var count = { settled: 0, unsettled: 0 };
|
|
|
+ for (var i in this.data)
|
|
|
+ if (i != 0)
|
|
|
+ for (var j in this.data[i])
|
|
|
+ switch (this.data[i][j]) {
|
|
|
+ case '~':
|
|
|
+ ++count.settled;
|
|
|
+ break;
|
|
|
+ case '|':
|
|
|
+ ++count.unsettled;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ count.total = count.settled +count.unsettled;
|
|
|
+ return count;
|
|
|
+}
|
|
|
+
|
|
|
+World.prototype.print = function() {
|
|
|
+ for (var i =this.minY; i <= this.maxY; ++i) {
|
|
|
+ for (var j =this.minX; j <= this.maxX; ++j)
|
|
|
+ process.stdout.write(this.getChar(j, i, ' '));
|
|
|
+ process.stdout.write("\n");
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+function run() {
|
|
|
+ readAll(world => {
|
|
|
+ while (world.sources.length)
|
|
|
+ world.water();
|
|
|
+ //world.print();
|
|
|
+ console.log("Water cell: ", world.countWater());
|
|
|
+ });
|
|
|
+}
|
|
|
+
|
|
|
+run();
|
|
|
+
|