Skip to content

Commit

Permalink
18/2024
Browse files Browse the repository at this point in the history
  • Loading branch information
breadadams committed Dec 19, 2024
1 parent 9576ed2 commit e2d2bec
Show file tree
Hide file tree
Showing 4 changed files with 273 additions and 0 deletions.
25 changes: 25 additions & 0 deletions 2024/18/example.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
5,4
4,2
4,5
3,0
2,1
6,3
2,4
1,5
0,6
3,3
2,6
5,1
1,2
5,5
2,5
6,5
1,4
0,4
6,4
1,1
6,1
1,0
0,5
1,6
2,0
24 changes: 24 additions & 0 deletions 2024/18/index.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { expect, describe, test } from "bun:test";
import { part1, part2 } from ".";
import { getInputs } from "../../utils/get-inputs";

const { exampleInput, puzzleInput } = await getInputs("2024/18");

describe("part 1", () => {
test("example", () => {
expect(part1(exampleInput, 6, 12)).toBe(22);
});

test("puzzle", () => {
expect(part1(puzzleInput, 70, 1024)).toBe(364);
});
});

describe("part 2", () => {
test("example", () => {
expect(part2(exampleInput, 6, 12)).toBe("6,1");
});
test("puzzle", () => {
expect(part2(puzzleInput, 70, 1024)).toBe("52,28");
});
});
223 changes: 223 additions & 0 deletions 2024/18/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,223 @@
import { timePart1, timePart2 } from "../../utils/time-part";

type Coordinate = { x: number; y: number };

const parseInput = (input: string) => {
const coords = input.split("\n").map((coord) => {
const [x, y] = coord.split(",").map(Number);

return { x, y } satisfies Coordinate;
});

return coords;
};

const createGrid = (size: number) =>
[...Array(size).keys()].map(() => [...Array(size).keys()].map(() => "."));

type Grid = ReturnType<typeof createGrid>;

const populateGrid = (grid: Grid, obstacles: Coordinate[]) => {
const newGrid = JSON.parse(JSON.stringify(grid)) as typeof grid;

for (let i = 0; i < obstacles.length; i++) {
const coord = obstacles[i];
newGrid[coord.y][coord.x] = "#";
}

return newGrid;
};

const printGrid = (grid: Grid) => {
console.log(grid.map((row) => row.join("")).join("\n"));
};

const DIRECTIONS = {
UP: { x: 0, y: -1 },
RIGHT: { x: 1, y: 0 },
DOWN: { x: 0, y: 1 },
LEFT: { x: -1, y: 0 },
};

type Direction = keyof typeof DIRECTIONS;

const move = ({ x, y }: Coordinate, dirKey: Direction) => {
const direction = DIRECTIONS[dirKey];

return { x: x + direction.x, y: y + direction.y };
};

const coordToKey = ({ x, y }: Coordinate) => `${x},${y}`;

class BinaryMinHeap {
private heap: Array<{
position: Coordinate;
count: number;
visited: Coordinate[];
}>;

constructor() {
this.heap = [];
}

getParentIndex(index: number) {
return Math.floor((index - 1) / 2);
}

getLeftChildIndex(index: number) {
return 2 * index + 1;
}

getRightChildIndex(index: number) {
return 2 * index + 2;
}

swap(index1: number, index2: number) {
[this.heap[index1], this.heap[index2]] = [
this.heap[index2],
this.heap[index1],
];
}

// Insert a new element into the heap
insert(element: (typeof this.heap)[number]) {
this.heap.push(element);
this.bubbleUp();
}

bubbleUp() {
let index = this.heap.length - 1;

while (
index > 0 &&
this.heap[index].count < this.heap[this.getParentIndex(index)].count
) {
this.swap(index, this.getParentIndex(index));
index = this.getParentIndex(index);
}
}

// Remove and return the smallest element
extractMin() {
if (this.heap.length === 1) return this.heap.pop()!;

const min = this.heap[0];
this.heap[0] = this.heap.pop()!;
this.bubbleDown();
return min;
}

bubbleDown() {
let index = 0;

while (this.getLeftChildIndex(index) < this.heap.length) {
let smallerChildIndex = this.getLeftChildIndex(index);

if (
this.getRightChildIndex(index) < this.heap.length &&
this.heap[this.getRightChildIndex(index)].count <
this.heap[this.getLeftChildIndex(index)].count
) {
smallerChildIndex = this.getRightChildIndex(index);
}

if (this.heap[index].count <= this.heap[smallerChildIndex].count) {
break;
}

this.swap(index, smallerChildIndex);
index = smallerChildIndex;
}
}

isEmpty() {
return this.heap.length === 0;
}
}

const getLowestStepCount = ({
grid,
gridSize,
}: {
grid: Grid;
gridSize: number;
}) => {
const start = { x: 0, y: 0 };
const end = { x: gridSize, y: gridSize };
const visited = new Map<string, number>();
let lowestStepCount = Number.MAX_SAFE_INTEGER;

const queue = new BinaryMinHeap();
queue.insert({ position: start, count: 0, visited: [start] });

while (!queue.isEmpty()) {
const entry = queue.extractMin()!;
const key = coordToKey(entry.position);

if (visited.has(key) && visited.get(key)! <= entry.count) {
continue;
}

visited.set(key, entry.count);

if (entry.position.x === end.x && entry.position.y === end.y) {
if (entry.count < lowestStepCount) {
lowestStepCount = entry.count;
}

continue;
}

for (const direction of Object.keys(DIRECTIONS) as Direction[]) {
const newPosition = move(entry.position, direction);
const nextCount = entry.count + 1;

if (
newPosition.x >= 0 &&
newPosition.x <= gridSize &&
newPosition.y >= 0 &&
newPosition.y <= gridSize &&
grid[newPosition.y][newPosition.x] !== "#" &&
nextCount < lowestStepCount
) {
queue.insert({
position: newPosition,
count: entry.count + 1,
visited: [...entry.visited, newPosition],
});
}
}
}

return lowestStepCount;
};

export const part1 = timePart1(
(input: string, gridSize: number, byteCount: number) => {
const coords = parseInput(input);
const grid = populateGrid(
createGrid(gridSize + 1),
coords.slice(0, byteCount)
);

return getLowestStepCount({ grid, gridSize });
}
);

export const part2 = timePart2(
(input: string, gridSize: number, byteCount: number) => {
const coords = parseInput(input);
const baseGrid = createGrid(gridSize + 1);

// @todo: optimize
for (let i = byteCount + 1; i < coords.length; i++) {
const grid = populateGrid(baseGrid, coords.slice(0, i + 1));

const lowestStepCount = getLowestStepCount({ grid, gridSize });

if (lowestStepCount === Number.MAX_SAFE_INTEGER) {
return coordToKey(coords[i]);
}
}
}
);
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

| Day | Part 1 | Part 2 |
| :----------------------------------------: | :----: | :----: |
| [18](https://adventofcode.com/2024/day/18) |||
| [17](https://adventofcode.com/2024/day/17) |||
| [16](https://adventofcode.com/2024/day/16) |||
| [15](https://adventofcode.com/2024/day/15) |||
Expand Down

0 comments on commit e2d2bec

Please sign in to comment.