From 757d0c2af8bfa7ac2350183872cf3f07fdc06743 Mon Sep 17 00:00:00 2001 From: Tim Kurvers Date: Fri, 6 Dec 2024 10:16:12 +0100 Subject: [PATCH] feat(js): add solutions for 2024 day 6 --- js/src/2024/04/index.js | 18 ++------- js/src/2024/06/index.js | 72 +++++++++++++++++++++++++++++++++++ js/src/utils/navigation.js | 16 ++++++++ puzzles/2024/06/examples.yaml | 17 +++++++++ 4 files changed, 108 insertions(+), 15 deletions(-) create mode 100644 js/src/2024/06/index.js create mode 100644 puzzles/2024/06/examples.yaml diff --git a/js/src/2024/04/index.js b/js/src/2024/04/index.js index 8e5750c..a15242d 100644 --- a/js/src/2024/04/index.js +++ b/js/src/2024/04/index.js @@ -1,20 +1,8 @@ -import { Grid, solution } from '../../utils/index.js'; +import { ALL_DIRECTIONS, INTERCARDINAL_DIRECTIONS, Grid, solution } from '../../utils/index.js'; const parse = (input) => Grid.from(input); -const DIRECTIONS = [ - [-1, -1], - [0, -1], - [1, -1], - [-1, 0], - [1, 0], - [-1, 1], - [0, 1], - [1, 1], -]; - -// Keep diagonals only -const CROSS_PATTERN_DIRECTIONS = DIRECTIONS.filter(([dx, dy]) => dx !== 0 && dy !== 0); +const CROSS_PATTERN_DIRECTIONS = INTERCARDINAL_DIRECTIONS; // Whether given letters are all encountered when walking the grid from starting point in the dx/dy direction const matches = (letters, { grid, point, dx, dy }) => @@ -27,7 +15,7 @@ const search = (grid, word) => { const letters = word.split(''); let count = 0; for (const point of grid) { - for (const [dx, dy] of DIRECTIONS) { + for (const [dx, dy] of ALL_DIRECTIONS) { count += +matches(letters, { grid, point, dx, dy }); } } diff --git a/js/src/2024/06/index.js b/js/src/2024/06/index.js new file mode 100644 index 0000000..005607d --- /dev/null +++ b/js/src/2024/06/index.js @@ -0,0 +1,72 @@ +import { Grid, Orientation, Rotation, solution, dx, dy, normalizeOrientation } from '../../utils/index.js'; + +const GUARD = '^'; +const OBSTACLE = '#'; + +const parse = (input) => { + const grid = Grid.from(input); + const start = grid.find((point) => point.value === GUARD); + return { grid, start }; +}; + +const explore = (grid, start) => { + let orientation = Orientation.UP; + + // Holds points visited by the guard + const visited = new Set(); + + // Holds points the guard faced (in front) + const faced = new Set(); + + // Used to track whether the guard patrols in a loop + const cyclic = new Set(); + const hash = (point, direction) => `${point.x}:${point.y}:${direction}`; + + let current = start; + let isCyclic = false; + while (current) { + const entry = hash(current, orientation); + if (cyclic.has(entry)) { + isCyclic = true; + break; + } + cyclic.add(entry); + visited.add(current); + + const next = grid.getPoint(current.x + dx(orientation), current.y + dy(orientation)); + if (next) { + faced.add(next); + } + if (next?.value === OBSTACLE) { + orientation = normalizeOrientation(orientation + Rotation.TURN_RIGHT); + } else { + current = next; + } + } + return { visited: Array.from(visited), faced, isCyclic }; +}; + +export const partOne = solution((input) => { + const { grid, start } = parse(input); + return explore(grid, start).visited.length; +}); + +export const partTwo = solution.inefficient((input) => { + const { grid, start } = parse(input); + + const initial = explore(grid, start); + + let count = 0; + for (const candidate of initial.faced) { + const original = candidate.value; + candidate.value = OBSTACLE; + + const result = explore(grid, start); + if (result.isCyclic) { + count++; + } + + candidate.value = original; + } + return count; +}); diff --git a/js/src/utils/navigation.js b/js/src/utils/navigation.js index bc0080c..80366b3 100644 --- a/js/src/utils/navigation.js +++ b/js/src/utils/navigation.js @@ -4,6 +4,22 @@ import { TAU } from './math.js'; const EPSILON = 0.000000001; +export const CARDINAL_DIRECTIONS = [ + [0, -1], + [-1, 0], + [1, 0], + [0, 1], +]; + +export const INTERCARDINAL_DIRECTIONS = [ + [-1, -1], + [1, -1], + [-1, 1], + [1, 1], +]; + +export const ALL_DIRECTIONS = CARDINAL_DIRECTIONS.concat(INTERCARDINAL_DIRECTIONS); + export const Orientation = { // UP / DOWN are flipped to ensure proper vertical movements in grids UP: Math.PI * 1.5, diff --git a/puzzles/2024/06/examples.yaml b/puzzles/2024/06/examples.yaml new file mode 100644 index 0000000..a9e1caa --- /dev/null +++ b/puzzles/2024/06/examples.yaml @@ -0,0 +1,17 @@ +part-one: + - input: &example1 | + ....#..... + .........# + .......... + ..#....... + .......#.. + .......... + .#..^..... + ........#. + #......... + ......#... + answer: 41 + +part-two: + - input: *example1 + answer: 6