Skip to content

Commit

Permalink
last couple of days
Browse files Browse the repository at this point in the history
  • Loading branch information
CodingAP committed Dec 19, 2023
1 parent 29a18fe commit ba8f82f
Show file tree
Hide file tree
Showing 7 changed files with 476 additions and 82 deletions.
139 changes: 57 additions & 82 deletions aoc/puzzles/2023/day14/solution.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,94 +8,65 @@
* 12/13/2023
*/

const directions = { NORTH: 0, EAST: 1, SOUTH: 2, WEST: 3 };
/**
* helper constants to give name to numbers and slide the rocks
*/
const directions = {
EAST: 0,
SOUTH: 1,
WEST: 2,
NORTH: 3,
MOVEMENTS: [{ x: 1, y: 0 }, { x: 0, y: 1 }, { x: -1, y: 0 }, { x: 0, y: -1 }]
};

/**
* slides the grid towards the direction specified
*
* mutates the grid given
*
* @param {string[][]} grid grid to slide
* @param {number} direction direction to slide it in
*/
const slideGrid = (grid, direction) => {
if (direction == directions.NORTH) {
for (let x = 0; x < grid[0].length; x++) {
for (let y = 0; y < grid.length; y++) {
// try to move it up
if (grid[y][x] == 'O') {
let current = y - 1;
while (current >= 0 && !grid[current][x].match(/[O#]/)) {
// swap
let temp = grid[current + 1][x];
grid[current + 1][x] = grid[current][x];
grid[current][x] = temp;

current--;
}
}
}
}
} else if (direction == directions.SOUTH) {
for (let x = 0; x < grid[0].length; x++) {
for (let y = grid.length - 1; y >= 0; y--) {
// try to move it down
if (grid[y][x] == 'O') {
let current = y + 1;
while (current < grid.length && !grid[current][x].match(/[O#]/)) {
// swap
let temp = grid[current - 1][x];
grid[current - 1][x] = grid[current][x];
grid[current][x] = temp;

current++;
}
}
}
}
} else if (direction == directions.WEST) {
for (let y = 0; y < grid.length; y++) {
for (let x = 0; x < grid[y].length; x++) {
// try to move it right
if (grid[y][x] == 'O') {
let current = x - 1;
while (current >= 0 && !grid[y][current].match(/[O#]/)) {
// swap
let temp = grid[y][current + 1];
grid[y][current + 1] = grid[y][current];
grid[y][current] = temp;

current--;
}
}
}
}
} else if (direction == directions.EAST) {
for (let y = 0; y < grid.length; y++) {
for (let x = grid[y].length - 1; x >= 0; x--) {
// try to move it left
if (grid[y][x] == 'O') {
let current = x + 1;
while (current < grid[y].length && !grid[y][current].match(/[O#]/)) {
// swap
let temp = grid[y][current - 1];
grid[y][current - 1] = grid[y][current];
grid[y][current] = temp;

current++;
}
}
const rowPrimary = (direction == directions.NORTH || direction == directions.SOUTH);
const reversed = (direction == directions.SOUTH || direction == directions.EAST);
console.log(rowPrimary, reversed)
for (let i = 0; i < (rowPrimary ? grid[0].length : grid.length); i++) {
for (let j = (reversed ? ((rowPrimary ? grid.length : grid[0].length) - 1) : 0); (reversed ? (j >= 0) : (j < (rowPrimary ? grid.length : grid[0].length))); j += (reversed ? -1 : 1)) {
// try to move it up
console.log(i, j);
if (grid[i][j] == 'O') {
let current = { x: rowPrimary ? j : i, y: rowPrimary ? i : j };
console.log(current);
// while (current >= 0 && !grid[current][x].match(/[O#]/)) {
// // swap
// let temp = grid[current + 1][x];
// grid[current + 1][x] = grid[current][x];
// grid[current][x] = temp;

// current--;
// }
}
}
}

return grid;
}

const hash = (str, seed = 0) => {
let h1 = 0xdeadbeef ^ seed, h2 = 0x41c6ce57 ^ seed;
for (let i = 0, ch; i < str.length; i++) {
ch = str.charCodeAt(i);
h1 = Math.imul(h1 ^ ch, 2654435761);
h2 = Math.imul(h2 ^ ch, 1597334677);
/**
* hash a string to a number for hash maps
*
* apparently, using Math.imul() on just integers is much faster than using normal multiplication
*
* @param {string} str string to hash
* @returns
*/
const hash = str => {
let h1 = 0xdeadbeef, h2 = 0x41c6ce57;
for (let i = 0; i < str.length; i++) {
h1 = Math.imul(h1 ^ str.charCodeAt(i), 2654435761);
h2 = Math.imul(h2 ^ str.charCodeAt(i), 1597334677);
}
h1 = Math.imul(h1 ^ (h1 >>> 16), 2246822507);
h1 ^= Math.imul(h2 ^ (h2 >>> 13), 3266489909);
h2 = Math.imul(h2 ^ (h2 >>> 16), 2246822507);
h2 ^= Math.imul(h1 ^ (h1 >>> 13), 3266489909);

h1 = Math.imul(h1 ^ (h1 >>> 16), 2246822507) ^ Math.imul(h2 ^ (h2 >>> 13), 3266489909);
h2 = Math.imul(h2 ^ (h2 >>> 16), 2246822507) ^ Math.imul(h1 ^ (h1 >>> 13), 3266489909);
return 4294967296 * (2097151 & h2) + (h1 >>> 0);
};

Expand All @@ -106,9 +77,12 @@ const hash = (str, seed = 0) => {
* @returns {Promise<string | number>} the result of part 1
*/
const part1 = async input => {
// parse input and slide
const grid = slideGrid(input.split(/\n/g).map(line => line.split('')), directions.NORTH);
// parse input
let grid = input.split(/\n/g).map(line => line.split(''));

slideGrid(grid, directions.NORTH);

// count all rocks depending on which row it is on
let sum = 0;
for (let y = grid.length - 1; y >= 0; y--) {
sum += grid[y].filter(char => char == 'O').length * (grid.length - y);
Expand Down Expand Up @@ -151,6 +125,7 @@ const part2 = async input => {
grid = slideGrid(grid, directions.EAST);
}

// count all rocks depending on which row it is on
let sum = 0;
for (let y = grid.length - 1; y >= 0; y--) {
sum += grid[y].filter(char => char == 'O').length * (grid.length - y);
Expand Down
193 changes: 193 additions & 0 deletions aoc/puzzles/2023/day17/solution.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
/**
* aoc/puzzles/2023/day17/solution.js
*
* ~~ Clumsy Crucible ~~
* this is my solution for this advent of code puzzle
*
* by alex prosser
* 12/16/2023
*/

/**
* represents a state of the path finding algorithm
*
* @typedef {Object} PathState
* @property {number} x x position of path
* @property {number} y y position of path
* @property {number} direction direction of the path
* @property {run} run current length of the same direction
*/

/**
* functions to manage a minheap, which will help with finishing dijkstra's algorithm faster
*/
const MinHeap = {
siftDown(arr, i = 0, value = arr[i]) {
if (i < arr.length) {
let key = value[0];
while (true) {
let j = i * 2 + 1;
if (j + 1 < arr.length && arr[j][0] > arr[j + 1][0]) j++;
if (j >= arr.length || key <= arr[j][0]) break;
arr[i] = arr[j];
i = j;
}
arr[i] = value;
}
},
pop(arr) {
return this.exchange(arr, arr.pop());
},
exchange(arr, value) {
if (!arr.length) return value;

let oldValue = arr[0];
this.siftDown(arr, 0, value);
return oldValue;
},
push(arr, value) {
let key = value[0], i = arr.length, j;

while ((j = (i - 1) >> 1) >= 0 && key < arr[j][0]) {
arr[i] = arr[j];
i = j;
}

arr[i] = value;
return arr;
}
};

/**
* helper constants to give name to numbers and move the beam
*/
const directions = {
UP: 0,
RIGHT: 1,
DOWN: 2,
LEFT: 3,
MOVEMENTS: [{ x: 0, y: -1 }, { x: 1, y: 0 }, { x: 0, y: 1 }, { x: -1, y: 0 }]
};

/**
* turns a path state into a string for maps
*
* @param {PathState} state state to convert
* @returns {string}
*/
const toKey = state => `${state.x},${state.y},${state.direction},${state.run}`;

/**
* find the neighbors allowed by the minimum and maximum run length
*
* adheres to rules stated in prose (no 180s, left/right turns happen after minimum, straight stops after maximum)
*
* @param {string[][]} grid grid to traverse
* @param {PathState} state current state to find the neighbors
* @param {number} minRun how many straight paths before allowed to turn
* @param {number} maxRun how many straight paths allowed
* @returns {{ x: number, y: number, direction: number, run: number, heatLoss: number }[]}
*/
const neighbors = (grid, state, minRun, maxRun) => {
let neighbors = [];

// check straight
let straightX = state.x + directions.MOVEMENTS[state.direction].x;
let straightY = state.y + directions.MOVEMENTS[state.direction].y;
if (straightX >= 0 && straightX < grid[0].length && straightY >= 0 && straightY < grid.length && state.run < maxRun) {
neighbors.push({ x: straightX, y: straightY, direction: state.direction, run: state.run + 1, heatLoss: grid[straightY][straightX] });
}

if (state.run >= minRun) {
// check left
let leftDirection = (state.direction + 3) % directions.MOVEMENTS.length;
let leftX = state.x + directions.MOVEMENTS[leftDirection].x;
let leftY = state.y + directions.MOVEMENTS[leftDirection].y;

if (leftX >= 0 && leftX < grid[0].length && leftY >= 0 && leftY < grid.length) {
neighbors.push({ x: leftX, y: leftY, direction: leftDirection, run: 1, heatLoss: grid[leftY][leftX] });
}

// check left
let rightDirection = (state.direction + 1) % directions.MOVEMENTS.length;
let rightX = state.x + directions.MOVEMENTS[rightDirection].x;
let rightY = state.y + directions.MOVEMENTS[rightDirection].y;

if (rightX >= 0 && rightX < grid[0].length && rightY >= 0 && rightY < grid.length) {
neighbors.push({ x: rightX, y: rightY, direction: rightDirection, run: 1, heatLoss: grid[rightY][rightX] });
}
}

return neighbors;
}

/**
* code for part 1 of the advent of code puzzle
*
* @param {string} input
* @returns {Promise<string | number>} the result of part 1
*/
const part1 = async input => {
// parse input
const grid = input.split(/\n/).map(line => line.split('').map(num => parseInt(num)));

// start dijkstra's algorithm
// i used a minheap to make the process of searching paths fast
let start = { x: 0, y: 0, direction: directions.RIGHT, run: 0 };
let queue = [], heatLosses = {};
MinHeap.push(queue, [0, start])
heatLosses[toKey(start)] = 0;

// find shortest path
while (queue.length != 0) {
let [heatLoss, current] = MinHeap.pop(queue);

// the first path to reach the end will be the shortest
if (current.x == grid[0].length - 1 && current.y == grid.length - 1) return heatLoss;

// find all neighbors that can be accessed
for (let neighbor of neighbors(grid, current, 0, 3)) {
let currentHeatLoss = heatLosses[toKey(neighbor)] || Infinity;
if (heatLoss + neighbor.heatLoss < currentHeatLoss) {
heatLosses[toKey(neighbor)] = heatLoss + neighbor.heatLoss;
MinHeap.push(queue, [heatLoss + neighbor.heatLoss, neighbor])
}
}
}
}

/**
* code for part 2 of the advent of code puzzle
*
* @param {string} input
* @returns {Promise<string | number>} the result of part 2
*/
const part2 = async input => {
// parse input
const grid = input.split(/\n/).map(line => line.split('').map(num => parseInt(num)));

// start dijkstra's algorithm
let start = { x: 0, y: 0, direction: directions.RIGHT, run: 0 };
let queue = [], heatLosses = {};
MinHeap.push(queue, [0, start])
heatLosses[toKey(start)] = 0;

// find shortest path
while (queue.length != 0) {
let [heatLoss, current] = MinHeap.pop(queue);

// the first path to reach the end will be the shortest
if (current.x == grid[0].length - 1 && current.y == grid.length - 1) return heatLoss;

// find all neighbors that can be accessed
for (let neighbor of neighbors(grid, current, 4, 10)) {
let currentHeatLoss = heatLosses[toKey(neighbor)] || Infinity;
if (heatLoss + neighbor.heatLoss < currentHeatLoss) {
heatLosses[toKey(neighbor)] = heatLoss + neighbor.heatLoss;
MinHeap.push(queue, [heatLoss + neighbor.heatLoss, neighbor])
}
}
}
}

export { part1, part2 };
Loading

0 comments on commit ba8f82f

Please sign in to comment.