Skip to content

Commit

Permalink
Add day 20 of year 2024 🦌
Browse files Browse the repository at this point in the history
  • Loading branch information
letelete committed Dec 21, 2024
1 parent 6d82d29 commit b2b1c6a
Show file tree
Hide file tree
Showing 2 changed files with 56 additions and 87 deletions.
10 changes: 6 additions & 4 deletions 2024/days/day-20/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,12 @@ Memory: 18.00 GB

| part | time (~) | μs |
| ---- | -------- | ------------------ |
| 1 | 4.48ms | 4.4783750000000015 |
| 1 | 3.69ms | 3.6905 |
| 2 | 0.63ms | 0.6251669999999905 |

## Answer

| part | time (~) | μs |
| ---- | -------- | ------------ |
| 1 | 11.930s | 11929.655666 |
| part | time (~) | μs |
| ---- | -------- | ------------------ |
| 1 | 117.15ms | 117.14849999999998 |
| 2 | 151.22ms | 151.219791 |
133 changes: 50 additions & 83 deletions 2024/days/day-20/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ function parse(source) {
.map((e) => e.split(''));
}

function findTile(grid, char) {
function findTilePos(grid, char) {
const row = grid.findIndex((row) => row.includes(char));
const col = grid[row].findIndex((col) => col === char);
return [row, col];
Expand All @@ -27,105 +27,72 @@ const dirs = {
],
};

function manhattanDist(a, b) {
return Math.abs(a[0] - b[0]) + Math.abs(a[1] - b[1]);
}

function inRange(data, row, col) {
return row >= 0 && row < data.length && col >= 0 && col < data[0].length;
}

function hash(row, col) {
return `${row},${col}`;
function getPath(grid, start, [endRow, endCol]) {
const path = [start];
while (([row, col] = path.at(-1)) && (row !== endRow || col !== endCol)) {
const [prevRow, prevCol] = path.at(-2) ?? [-1, -1];
const [nextRow, nextCol] = dirs.orthogonal
.map(([drow, dcol]) => [row + drow, col + dcol])
.find(
([nextRow, nextCol]) =>
inRange(grid, nextRow, nextCol) &&
grid[nextRow][nextCol] !== symbols.wall &&
(nextRow !== prevRow || nextCol !== prevCol)
);
path.push([nextRow, nextCol]);
}
return path;
}

function bfs(grid, start, end, visited, trackPath) {
const q = [[0, start]];
const source = new Map();

while (q.length) {
const [steps, [row, col]] = q.shift();
if (visited.has(hash(row, col))) {
continue;
function cheat(path, at, duration) {
const saved = [];
for (let i = at + 1; i < path.length; ++i) {
const dist = manhattanDist(path[at], path[i]);
const save = i - at - dist;
if (dist <= duration && save > 0) {
saved.push(save);
}
visited.add(hash(row, col));

if (row === end[0] && col === end[1]) {
if (!trackPath) {
return steps;
}
const path = [[row, col]];
let head = source.get(hash(row, col));
while (head) {
path.push(head.split(',').map(Number));
head = source.get(head);
}
return [steps, [...path].reverse()];
}

dirs.orthogonal.forEach(([drow, dcol]) => {
const [nextRow, nextCol] = [row + drow, col + dcol];
if (
inRange(grid, nextRow, nextCol) &&
grid[nextRow][nextCol] !== symbols.wall &&
!visited.has(hash(nextRow, nextCol))
) {
q.push([steps + 1, [nextRow, nextCol]]);
source.set(hash(nextRow, nextCol), hash(row, col));
}
});
}
return saved;
}

return -1;
function getAllCheats(path, maxDuration) {
return path.flatMap((_, at, path) => cheat(path, at, maxDuration));
}

function part1(data) {
const end = findTile(data, symbols.end);
function countOptimalCheats(cheats, minTimeSaved) {
return cheats.reduce(
(sum, timeSaved) => (timeSaved >= minTimeSaved ? sum + 1 : sum),
0
);
}

const [minSteps, path] = bfs(
function part1(data) {
const path = getPath(
data,
findTile(data, symbols.start),
end,
new Set(),
true
findTilePos(data, symbols.start),
findTilePos(data, symbols.end)
);

const visited = new Set();
const saves = new Map();
console.log({ path });
path.forEach(([row, col], steps) => {
visited.add(hash(row, col));
dirs.orthogonal.forEach(([drow, dcol]) => {
const [nextRow, nextCol] = [row + drow, col + dcol];
const nextHash = hash(nextRow, nextCol);
if (
inRange(data, nextRow, nextCol) &&
data[nextRow][nextCol] === symbols.wall &&
!visited.has(nextHash)
) {
const bfsres = bfs(
data,
[nextRow, nextCol],
end,
new Set(...visited),
false
);
if (bfsres !== -1) {
const candidate = steps + bfsres + 1;
if (candidate < minSteps) {
saves.set(candidate, (saves.get(candidate) ?? 0) + 1);
}
}
}
});
});

return [
minSteps,
[...saves.entries()]
.filter(([k]) => minSteps - k >= 100)
.reduce((sum, [_, v]) => sum + v, 0),
];
const cheats = getAllCheats(path, 2);
return countOptimalCheats(cheats, 100);
}

function part2(data) {
return null;
const path = getPath(
data,
findTilePos(data, symbols.start),
findTilePos(data, symbols.end)
);
const cheats = getAllCheats(path, 20);
return countOptimalCheats(cheats, 100);
}

module.exports = { parse, part1, part2 };

0 comments on commit b2b1c6a

Please sign in to comment.