[TASK] Solved Day 15 (although there is room for speed improvements)
This commit is contained in:
94
functions/src/Grid.ts
Normal file
94
functions/src/Grid.ts
Normal 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
77
functions/src/day15.ts
Normal 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;
|
||||
@@ -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) }),
|
||||
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user