-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy path15.js
115 lines (93 loc) · 2.85 KB
/
15.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
'use strict';
// Failed at following a tutorial for the pathfinding, so just used someone's code
// From https://github.com/joeyemerson/aoc/blob/main/2021/15-chiton/solution.js and https://github.com/joeyemerson/aoc/blob/main/2021/utils/PriorityQueue.js
function PriorityQueue(compare) {
const _heap = [null];
const _swap = function(a, b) {
const temp = _heap[a];
_heap[a] = _heap[b];
_heap[b] = temp;
};
const _siftUp = function(idx) {
const parent = Math.floor(idx / 2);
while (parent > 0 && compare(_heap[idx], _heap[parent])) {
_swap(idx, parent);
_siftUp(parent);
}
};
const _siftDown = function(idx) {
const leftChild = idx * 2;
const rightChild = idx * 2 + 1;
if (
leftChild < _heap.length &&
compare(_heap[leftChild], _heap[idx]) &&
(rightChild >= _heap.length || compare(_heap[leftChild], _heap[rightChild]))
) {
_swap(idx, leftChild);
_siftDown(leftChild);
} else if (rightChild < _heap.length && compare(_heap[rightChild], _heap[idx])) {
_swap(idx, rightChild);
_siftDown(rightChild);
}
};
const enqueue = function(value) {
_heap.push(value);
_siftUp(_heap.length - 1);
};
const dequeue = function() {
if (isEmpty()) return null;
const top = _heap[1];
const end = _heap.pop();
// Check if we removed the last item
if (!isEmpty()) {
_heap[1] = end;
_siftDown(1);
}
return top;
};
const isEmpty = function() {
return _heap.length === 1;
};
return { enqueue, dequeue, isEmpty };
}
/**
*
* @param {number} height
* @param {number} width
* @param {string[][]} grid
*/
function shortestPath (height, width, grid) {
const tileHeight = grid.length;
const tileWidth = grid[0].length;
const dirs = [[-1, 0], [0, 1], [1, 0], [0, -1]];
const costs = Array.from(Array(height), () => Array(width).fill(Infinity));
const pq = new PriorityQueue((a, b) => a[2] < b[2]);
pq.enqueue([0, 0, 0]); // Start at top-left corner
while (!pq.isEmpty()) {
const [r, c, prevCost] = pq.dequeue();
if (r < 0 || c < 0 || r === height || c === width) continue;
let cost = grid[r % tileHeight][c % tileWidth] + Math.floor(r / tileHeight) + Math.floor(c / tileWidth);
if (cost > 9) cost -= 9;
if (prevCost + cost < costs[r][c]) costs[r][c] = prevCost + cost;
else continue;
if (r === height - 1 && c === width - 1) break; // we found bottom-right corner
for (const [rr, cc] of dirs) {
pq.enqueue([r + rr, c + cc, costs[r][c]]);
}
}
return costs[height - 1][width - 1] - grid[0][0];
}
/**
* @param {string} d
*/
export const part1 = async d => {
const data = d.split('\n').map(e => e.split('').map(e => parseInt(e, 10)));
return shortestPath(data.length, data[0].length, data);
};
/**
* @param {string} d
*/
export const part2 = async d => {
const data = d.split('\n').map(e => e.split('').map(e => parseInt(e, 10)));
return shortestPath(data.length * 5, data[0].length * 5, data);
};