From d9163882ce8ef5b04914b6d918774c5f5d4239c7 Mon Sep 17 00:00:00 2001 From: letelete Date: Tue, 17 Dec 2024 00:15:03 +0100 Subject: [PATCH] =?UTF-8?q?Add=20day=2016=20of=20year=202024=20?= =?UTF-8?q?=F0=9F=A7=9D=E2=80=8D=E2=99=80=EF=B8=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 2024/days/day-16/README.md | 28 +++++ 2024/days/day-16/in.sample.1.txt | 15 +++ 2024/days/day-16/in.sample.2.txt | 17 +++ 2024/days/day-16/main.js | 173 +++++++++++++++++++++++++++++++ 4 files changed, 233 insertions(+) create mode 100644 2024/days/day-16/README.md create mode 100644 2024/days/day-16/in.sample.1.txt create mode 100644 2024/days/day-16/in.sample.2.txt create mode 100644 2024/days/day-16/main.js diff --git a/2024/days/day-16/README.md b/2024/days/day-16/README.md new file mode 100644 index 0000000..fab715c --- /dev/null +++ b/2024/days/day-16/README.md @@ -0,0 +1,28 @@ +# Benchmark + +``` +Platform: darwin arm64 +CPU: Apple M3 Pro 11 Cores +Memory: 18.00 GB +``` + +## Sample 1 + +| part | time (~) | μs | +| ---- | -------- | ------------------- | +| 1 | 0.41ms | 0.40595799999999826 | +| 2 | 1.96ms | 1.9566669999999995 | + +## Sample 2 + +| part | time (~) | μs | +| ---- | -------- | ------------------ | +| 1 | 0.27ms | 0.2736249999999991 | +| 2 | 0.56ms | 0.5599999999999987 | + +## Answer + +| part | time (~) | μs | +| ---- | -------- | ----------- | +| 1 | 10.97ms | 10.974833 | +| 2 | 8.328s | 8327.917625 | diff --git a/2024/days/day-16/in.sample.1.txt b/2024/days/day-16/in.sample.1.txt new file mode 100644 index 0000000..2c21676 --- /dev/null +++ b/2024/days/day-16/in.sample.1.txt @@ -0,0 +1,15 @@ +############### +#.......#....E# +#.#.###.#.###.# +#.....#.#...#.# +#.###.#####.#.# +#.#.#.......#.# +#.#.#####.###.# +#...........#.# +###.#.#####.#.# +#...#.....#.#.# +#.#.#.###.#.#.# +#.....#...#.#.# +#.###.#.#.#.#.# +#S..#.....#...# +############### diff --git a/2024/days/day-16/in.sample.2.txt b/2024/days/day-16/in.sample.2.txt new file mode 100644 index 0000000..bc61c57 --- /dev/null +++ b/2024/days/day-16/in.sample.2.txt @@ -0,0 +1,17 @@ +################# +#...#...#...#..E# +#.#.#.#.#.#.#.#.# +#.#.#.#...#...#.# +#.#.#.#.###.#.#.# +#...#.#.#.....#.# +#.#.#.#.#.#####.# +#.#...#.#.#.....# +#.#.#####.#.###.# +#.#.#.......#...# +#.#.###.#####.### +#.#.#...#.....#.# +#.#.#.#####.###.# +#.#.#.........#.# +#.#.#.#########.# +#S#.............# +################# diff --git a/2024/days/day-16/main.js b/2024/days/day-16/main.js new file mode 100644 index 0000000..0388044 --- /dev/null +++ b/2024/days/day-16/main.js @@ -0,0 +1,173 @@ +const fs = require('fs'); + +function parse(source) { + return source + .trim() + .split('\n') + .map((e) => e.split('')); +} + +Array.prototype.sum = function () { + return this.reduce((sum, value) => sum + value, 0); +}; + +Array.prototype.product = function () { + return this.reduce((col, value) => col * value, 1); +}; + +Array.prototype.equals = function (arr) { + return ( + this.length === arr.length && this.every((e, i) => Object.is(e, arr[i])) + ); +}; + +const CHAR_WALL = '#'; +const CHAR_START = 'S'; +const CHAR_END = 'E'; +const DIR_EAST = 2; + +const dirs = { + orthogonal: [ + [0, -1], + [-1, 0], + [0, 1], + [1, 0], + ], +}; + +function mod(a, b) { + return ((a % b) + b) % b; +} + +const clockwise = (dir) => { + return mod(dir + 1, dirs.orthogonal.length); +}; + +const counterclockwise = (dir) => { + return mod(dir - 1, dirs.orthogonal.length); +}; + +function inRange(grid, row, col) { + return row >= 0 && row < grid.length && col >= 0 && col < grid[0].length; +} + +function findTile(grid, char) { + const row = grid.findIndex((row) => row.includes(char)); + const col = grid[row].findIndex((col) => col === char); + + return [row, col]; +} + +const findMinScore = (grid, startRow, startCol, startDir, endRow, endCol) => { + const visited = new Set(); + const distance = Array.from({ length: grid.length }, () => + new Array(grid[0].length).fill(Infinity) + ); + distance[startRow][startCol] = 0; + + const q = [[0, startRow, startCol, startDir]]; + + while (q.length) { + q.sort(([acost], [bcost]) => bcost - acost); + + const [cost, row, col, dir] = q.pop(); + visited.add(`${row},${col}`); + + if (row === endRow && col === endCol) { + break; + } + + [ + [0, dir], + [1000, clockwise(dir)], + [1000, counterclockwise(dir)], + ].forEach(([weight, dir]) => { + const [drow, dcol] = dirs.orthogonal[dir]; + if ( + inRange(grid, row + drow, col + dcol) && + grid[row + drow][col + dcol] !== CHAR_WALL && + !visited.has(`${row + drow},${col + dcol}`) + ) { + const nextCost = cost + weight + 1; + + if (nextCost < distance[row + drow][col + dcol]) { + distance[row + drow][col + dcol] = nextCost; + q.push([nextCost, row + drow, col + dcol, dir]); + } + } + }); + } + + return distance; +}; + +const findMinScoreRoutes = (grid, startRow, startCol, endRow, endCol) => { + const routes = []; + const visited = new Map(); + + const q = [[[startRow, startCol], [[startRow, startCol]], 0, 0]]; + while (q.length > 0) { + const [[row, col], history, currScore, currDir] = q.shift(); + + if (row === endRow && col === endCol) { + routes.push([history, currScore]); + continue; + } + + const key = `${row},${col},${currDir}`; + if (visited.has(key) && visited.get(key) < currScore) { + continue; + } + + visited.set(key, currScore); + + dirs.orthogonal.forEach(([drow, dcol], dir) => { + const nextRow = row + drow; + const nextCol = col + dcol; + + if ( + grid[nextRow] && + grid[nextRow][nextCol] !== CHAR_WALL && + !history.some(([hrow, hcol]) => hrow === nextRow && hcol === nextCol) + ) { + if (dir === currDir) { + q.push([ + [nextRow, nextCol], + [...history, [nextRow, nextCol]], + currScore + 1, + dir, + ]); + } else { + q.push([[row, col], history, currScore + 1000, dir]); + } + } + }); + } + + const minScore = Math.min(...routes.map(([_, score]) => score)); + return routes.filter(([_, score]) => score === minScore); +}; + +function part1(data) { + const [startRow, startCol] = findTile(data, CHAR_START); + const [endRow, endCol] = findTile(data, CHAR_END); + + return findMinScore(data, startRow, startCol, DIR_EAST, endRow, endCol)[ + endRow + ][endCol]; +} + +function part2(data) { + const [startRow, startCol] = findTile(data, CHAR_START); + const [endRow, endCol] = findTile(data, CHAR_END); + + const uniqueTiles = new Set( + findMinScoreRoutes(data, startRow, startCol, endRow, endCol) + .flatMap(([route]) => route) + .map(([row, col]) => `${row},${col}`) + ); + + return uniqueTiles.size; +} + +module.exports = { parse, part1, part2 };