[TASK] Solved Day 15 (although there is room for speed improvements)

This commit is contained in:
2021-12-16 21:13:18 +01:00
parent 07b36d66d5
commit e3988e91b7
5 changed files with 293 additions and 0 deletions

94
functions/src/Grid.ts Normal file
View File

@@ -0,0 +1,94 @@
import Vector2 from "./vector2";
class Grid<T> {
data: T[];
outOfBoundsResult?: T;
maxX: number;
maxY: number;
constructor(data: T[], maxX: number, outOfBoundsResult: T | undefined = undefined) {
if (data.length % maxX !== 0) throw Error("Invalid grid data length, must be a multiple of the width");
this.data = data;
this.outOfBoundsResult = outOfBoundsResult;
this.maxX = maxX;
this.maxY = this.data.length / maxX;
}
at(x: number, y: number): T {
if (!this.isInBounds(x, y)) {
if (this.outOfBoundsResult) {
return this.outOfBoundsResult
} else {
throw Error(`Invalid coordinate: (${x}, ${y})`);
}
}
return this.data[this.index(x, y)];
}
setAt(x: number, y: number, value: T) {
if (!this.isInBounds(x, y)) {
throw Error(`Invalid coordinate: (${x}, ${y})`);
}
this.data[this.index(x, y)] = value;
}
allPositions(): Vector2[] {
const res =[];
for (let y = 0; y < this.maxY; y++) {
for (let x = 0; x < this.maxX; x++) {
res.push(new Vector2(x, y));
}
}
return res;
}
isInBounds(x: number, y: number): boolean {
return x >= 0 && x < this.maxX && y >= 0 && y < this.maxY;
}
inBoundsNeighbors(x: number, y: number, includeDiagonal: boolean = false): Vector2[] {
const res = [
new Vector2(x + 1, y),
new Vector2(x - 1, y),
new Vector2(x, y + 1),
new Vector2(x, y - 1)
];
if (includeDiagonal) {
res.push(
new Vector2(x + 1, y + 1),
new Vector2(x + 1, y - 1),
new Vector2(x - 1, y + 1),
new Vector2(x - 1, y - 1)
);
}
return res.filter(v => this.isInBounds(v.x, v.y));
}
/**
* Pastes the given grid into this grid, with it's top left position at the given coordinate
*/
paste(grid: Grid<T>, posX: number, posY: number) {
if (posX + grid.maxX > this.maxX || posY + grid.maxY > this.maxY) throw Error(`Can't paste grid at (${posX}, ${posY}), this grid is not big enough`);
for (let y = 0; y < grid.maxY; y++) {
for (let x = 0; x < grid.maxX; x++) {
this.setAt(posX + x, posY + y, grid.at(x, y));
}
}
}
index(x: number, y: number): number {
return x + y * this.maxX;
}
}
export default Grid;

77
functions/src/day15.ts Normal file
View File

@@ -0,0 +1,77 @@
import Day from "./day";
import Grid from "./Grid";
import Vector2 from "./vector2";
class Day15 implements Day {
part1(input: string[]): number | string {
return this.findLowestRiskScore(Day15.parseInput(input));
}
part2(input: string[]): number | string {
const grid = Day15.parseInput(input);
const bigGrid = new Grid(Array((grid.maxX * 5) * (grid.maxY * 5)).fill(0), grid.maxX * 5, Number.MAX_SAFE_INTEGER);
for (let y = 0; y < 5; y++) {
for (let x = 0; x < 5; x++) {
const subGrid = new Grid(grid.data.map(c => c + x + y).map(c => c > 9 ? c % 9 : c), grid.maxX);
bigGrid.paste(subGrid, x * grid.maxX, y * grid.maxY);
}
}
return this.findLowestRiskScore(bigGrid);
}
private findLowestRiskScore(grid: Grid<number>): number {
const start = new Vector2(0, 0);
const destination = new Vector2(grid.maxX - 1, grid.maxY - 1);
const distances = new Grid(new Array(grid.data.length).fill(Number.MAX_SAFE_INTEGER), grid.maxX);
distances.setAt(start.x, start.y, 0);
const searchStack = [start];
while (searchStack.length > 0) {
// Simulate a priority queue on distance:
searchStack.sort((a, b) => distances.at(b.x, b.y) - distances.at(a.x, a.y));
const cur = searchStack.pop()!;
if (cur.x === destination.x && cur.y === destination.y) {
break;
}
const curDist = distances.at(cur.x, cur.y);
const neighbors = grid.inBoundsNeighbors(cur.x, cur.y);
for (const neighbor of neighbors) {
const newDist = curDist + grid.at(neighbor.x, neighbor.y);
if (newDist < distances.at(neighbor.x, neighbor.y)) {
distances.setAt(neighbor.x, neighbor.y, newDist);
searchStack.push(neighbor);
}
}
}
return distances.at(destination.x, destination.y);
}
private static parseInput(input: string[]): Grid<number> {
return new Grid<number>(
input.flatMap(l => l.split("").map(n => parseInt(n))),
input[0].length, Number.MAX_SAFE_INTEGER);
}
}
export default Day15;

View File

@@ -16,6 +16,7 @@ import Day11 from "./day11";
import Day12 from "./day12";
import Day13 from "./day13";
import Day14 from "./day14";
import Day15 from "./day15";
// // Start writing Firebase Functions
@@ -46,6 +47,7 @@ export const day = {
12: functions.region("europe-west1").https.onRequest((request, response) => { processDay(new Day12(), request, response) }),
13: functions.region("europe-west1").https.onRequest((request, response) => { processDay(new Day13(), request, response) }),
14: functions.region("europe-west1").https.onRequest((request, response) => { processDay(new Day14(), request, response) }),
15: functions.region("europe-west1").https.onRequest((request, response) => { processDay(new Day15(), request, response) }),
}