[TASK] Implemented Day 9
This commit is contained in:
122
functions/src/day9.ts
Normal file
122
functions/src/day9.ts
Normal file
@@ -0,0 +1,122 @@
|
||||
import Day from "./day";
|
||||
import Vector2 from "./vector2";
|
||||
import Utils from "./utils";
|
||||
|
||||
class Day9 implements Day {
|
||||
|
||||
part1(input: string[]): number | string {
|
||||
|
||||
const heightMap = HeightMap.parseInput(input);
|
||||
const lowPoints = heightMap.findLowPoints();
|
||||
return Utils.sum(lowPoints.map(lp => heightMap.height(lp.x, lp.y) + 1));
|
||||
}
|
||||
|
||||
part2(input: string[]): number | string {
|
||||
|
||||
const heightMap = HeightMap.parseInput(input);
|
||||
const basins = heightMap.findBasins();
|
||||
basins.sort((a, b) => b.size - a.size);
|
||||
|
||||
return basins[0].size * basins[1].size * basins[2].size;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class HeightMap {
|
||||
|
||||
readonly heightMap: number[];
|
||||
readonly width: number;
|
||||
readonly maxY: number;
|
||||
|
||||
constructor(heightMap: number[], width: number) {
|
||||
this.heightMap = heightMap;
|
||||
this.width = width;
|
||||
this.maxY = this.heightMap.length / width;
|
||||
}
|
||||
|
||||
static parseInput(input: string[]): HeightMap {
|
||||
|
||||
const width = input[0].length;
|
||||
const heightMap = [];
|
||||
for (const line of input) {
|
||||
if (line.length !== width) {
|
||||
throw Error("Invalid input");
|
||||
}
|
||||
|
||||
heightMap.push(...line.split("").map(s => parseInt(s)));
|
||||
}
|
||||
|
||||
return new HeightMap(heightMap, width);
|
||||
|
||||
}
|
||||
|
||||
height(x: number, y: number): number {
|
||||
if (x < 0 || x >= this.width || y < 0 || y >= this.maxY) return 9;
|
||||
return this.heightMap[x + y * this.width];
|
||||
}
|
||||
|
||||
findLowPoints(): Vector2[] {
|
||||
|
||||
const res = [];
|
||||
for (let y = 0; y < this.maxY; y++) {
|
||||
for (let x = 0; x < this.width; x++) {
|
||||
if (this.isLowPoint(x, y)) {
|
||||
res.push(new Vector2(x, y));
|
||||
}
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
isLowPoint(x: number, y: number): boolean {
|
||||
|
||||
const height = this.height(x, y);
|
||||
return height < this.height(x - 1, y) &&
|
||||
height < this.height(x + 1, y) &&
|
||||
height < this.height(x, y - 1) &&
|
||||
height < this.height(x, y + 1);
|
||||
}
|
||||
|
||||
findBasins(): Basin[] {
|
||||
|
||||
const lowPoints = this.findLowPoints();
|
||||
|
||||
// From every low point, we start to grow "upwards" to claim every point that is part of it's basin:
|
||||
const basinIds = Array(this.width * this.maxY).fill(-1);
|
||||
for (let i = 0; i < lowPoints.length; i++) {
|
||||
|
||||
const stack: Vector2[] = [lowPoints[i]];
|
||||
|
||||
while(stack.length > 0) {
|
||||
const pos = stack.pop();
|
||||
if (!pos) {
|
||||
throw Error("Stack is empty?")
|
||||
}
|
||||
const posHeight = this.height(pos.x, pos.y);
|
||||
basinIds[pos.x + pos.y * this.width] = i;
|
||||
// We add all neighbors to the stack that are low enough, and haven't been assigned to a basin:
|
||||
const neighbors = [pos.addX(1), pos.addX(-1), pos.addY(1), pos.addY(-1)];
|
||||
for (const neighbor of neighbors) {
|
||||
const neighborHeight = this.height(neighbor.x, neighbor.y);
|
||||
if (neighborHeight < 9 && neighborHeight > posHeight && basinIds[neighbor.x + neighbor.y * this.width] === -1) {
|
||||
stack.push(neighbor);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return lowPoints.map((lp, i) => new Basin(lp, basinIds.filter(b => b === i).length));
|
||||
}
|
||||
}
|
||||
|
||||
class Basin {
|
||||
readonly lowPoint: Vector2;
|
||||
readonly size: number;
|
||||
|
||||
constructor(lowPoint: Vector2, size: number) {
|
||||
this.lowPoint = lowPoint;
|
||||
this.size = size;
|
||||
}
|
||||
}
|
||||
|
||||
export default Day9;
|
||||
@@ -10,6 +10,7 @@ import Day5 from "./day5";
|
||||
import Day6 from "./day6";
|
||||
import Day7 from "./day7";
|
||||
import Day8 from "./day8";
|
||||
import Day9 from "./day9";
|
||||
|
||||
|
||||
// // Start writing Firebase Functions
|
||||
@@ -34,6 +35,7 @@ export const day = {
|
||||
6: functions.region("europe-west1").https.onRequest((request, response) => { processDay(new Day6(), request, response) }),
|
||||
7: functions.region("europe-west1").https.onRequest((request, response) => { processDay(new Day7(), request, response) }),
|
||||
8: functions.region("europe-west1").https.onRequest((request, response) => { processDay(new Day8(), request, response) }),
|
||||
9: functions.region("europe-west1").https.onRequest((request, response) => { processDay(new Day9(), request, response) }),
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -18,6 +18,14 @@ class Vector2 {
|
||||
key(): string {
|
||||
return `${this.x},${this.y}`;
|
||||
}
|
||||
|
||||
addX(x: number): Vector2 {
|
||||
return new Vector2(this.x + x, this.y);
|
||||
}
|
||||
|
||||
addY(y: number): Vector2 {
|
||||
return new Vector2(this.x, this.y + y);
|
||||
}
|
||||
}
|
||||
|
||||
export default Vector2;
|
||||
Reference in New Issue
Block a user