diff --git a/24/input b/24/input new file mode 100644 index 0000000..b001985 --- /dev/null +++ b/24/input @@ -0,0 +1,22 @@ +#.###################################################################################################################################################### +#>.^v.>^v>><>>^>>v>v^>.<>^.>v^v>..<<><>>>v.><^<.>^.<^>^v^>>v<^>><.^<^.v><><^^<^>>^<<<>.>.>^^^<^<^>.>^v.>.>^>^v<<^^<>><>>>v^.>^.^>v.>.v<<>><>vv.vv<>vvvv^>^>v^vvv>.<^>v<>>.<><<><>v<<><^><<.v.vv<<><.<<<><^<><^<<<<.>v<^v^>>>><.v>^>.>^<^>>vv<# +#>>>>^.>.^v>v<.^<^vv^^^<>.>>.>><<.v<^><.<<<><.^v^^v<>^<^v.^>^v<><>>>^v<>><>v<^v>>>^vv<^vvv>^><<><^v>>^># +#><<>^>v>.<^^v>v^<<<>^^v>^.v<<>><>v.v>v^.v>>>^<^><.>>v<<<>vv^.>^>^<><^.>.v^^^><>^vv^v>v^<>><>>v^^<^v>>>v.<<>.v<><^^v<.^>v>.<^^># +#<.v^vv^vv>>^>>^.vvv^^><<>v><>v.^>vvv^<>>v><.>^.>.^<<<.^vvv>^..^>>.v^v>^v^v<<<>v^<<^><.v>v# +#<^v^><>v^<.>vv>.^<<<^<^.^^<.^vv^>v^^.^<^>^.^<><><^<<>>v<><>v^^v<.vvv>^>>^<^><>>^vv<^>><^.<^v^v>>>^v<^<><>.^^>.v><<># +#<.><^v>^<.>>vv<^v^v.^>v<.v>.^>v^^.<>.<.vvv<^>v^.^<^v>^.>.v<.v>><<<^vv^.^<<^<^v.><>v.^^.^><<.<<^vv.<^^^^>^<.<><<>>^>v>.^^v.<<# +#<<^.>v<><>.^>>^v<<^>vv<^v>><.<^^.^^^>^.><^>v^v<<<<>>^>v>^v<^<>..v<.<>v^>v^^v..<>^<><>^v<<<.v>...>^^v>.>v^.^>vv>^v<>v><<^.>^^..<<^<# +#>>vvvv>v>>.v^<^v<>.>^<.<<^<>.<..>.^.>^^>.^.^^>v.v^>v<^>^v.v^^^vv><^>^>>>v>>>..v.<^<.<<<>>><^<>^<.>^^^<><^<.<^>^vv>vv># +#<><>v^..>v>^^^^<.v>v>..<>><^><<^v.v.<><<>>^^>>v<<>^^<^^^>v.<<>v^>^v>>v>vv^<^v<^v^.><>vv>>>.>>v^><^^v.^>^v^># +#>v><>><<^.>v^.v<<<^vv<.v^>v<.>vv>v>>^<^<<><>v>v^^v<^.>.^^^>^^v.^.>^>>>^>v<^>v^<v^>>v.^.^<<.vv>^^^<^v<# +#>^>>^..vv<^<<^<.^.<<>.>>>><^<>^>^v>>v.<.^^^>><><.>^v^>^<>^<^^.<^>^^^^^^vv<>vv^v^v>^v^<>^><<<.^>># +#<.>>>>.>^<>>.^^vv>>^<<<^v><^^>^^^^^^>><^>v^<<>>v<^.>vvvv>>^<.^>.v<^^<>.vv.v^v<>>>><^v^>>>.v.^^v<v^^>^v>^v<><.v><^v.# +#><<^v><<<.v<^^><^v<^v.<<<.^^.v>vvvv>^>><^^v<>.<>><<.v^>..^<>.><^>^>^v^^>v<^>vvv.>^^>^^<<>v>v^>^^^<^v>.v>^<><^<.v<^><<>^.^>>><v^v^v<^v>>^^^><^<# +#<^>v<>.>>^.v>^<>.v.^^v>^<..v.^>^>.v>>>^^>v><>vv.>>^v>>^v<<.v>v^>v><<<^.<<<<>vv>^>>^<>^^<^^<<<>^^<># +#>>.>>.^>..<^<^.<<>..^^<..v<.><^^<>^vv.v<<<>^>^v^>v<>>>>>>v^.>>><.<>^^^^v<<^>>.><<.><.v^^..>>>.v>># +#>v^<>>v^v.vv^>v^vvvvv<>.v^.v<.><>^>>^<v><^.>>^><>vv<<^>^>^.vv<<>^^>^<<>v>.<<^.<>><<^<<.^<^<<<.^>.<.vv>v^v<^>vvv>^v<<<v^.^<><>^<>^>v^>^><.vv>>.<^..^^^<^<.^^<^^v^<^.<<<<^v.<<>.^>><^>^v>><^v^<<^>>v<<^.v<<^vv>>v>^.v^^^<>.v.<><# +#..vv<^vv>.v.v>^v^>>vv^.^>v.v^^<<>^^<>^<^^>.<^v^>^v<^^^>v>><><<><.<<><^v^^>>v><>^v^.v>^^v<^><><^v<^<><<>v<<><>v<.># +#>>.<^^>><<<^>v<^^>>^<<>><<.<^^^<^^^<^<vv<<^v>.^<^^>><>^^^.>v>v^<^>>v><<^<^^^>v><<>^^v><>v..<.^^>..vv.>>^<>>>.>vv.>>^v>^v<.# +######################################################################################################################################################.# diff --git a/24/solution.mjs b/24/solution.mjs new file mode 100644 index 0000000..e66f7c8 --- /dev/null +++ b/24/solution.mjs @@ -0,0 +1,162 @@ +import { readFileSync } from 'node:fs'; + +const input = readFileSync('input', 'utf-8'); + +// Direction +const D = { + R: 0, + D: 1, + L: 2, + U: 3 +}; + +class Blizzard { + constructor(width, height, x, y, direction) { + this.width = width; + this.height = height; + this.x = x; + this.y = y; + this.direction = direction; + } + + posAtTime(t) { + switch (this.direction) { + case D.R: return [(this.x + t) % this.width, this.y]; + case D.D: return [this.x, (this.y + t) % this.height]; + case D.L: return [ + this.width - 1 - (this.width - 1 - this.x + t) % this.width, + this.y + ]; + case D.U: return [ + this.x, + this.height - 1 - (this.height - 1 - this.y + t) % this.height, + ]; + } + } +} + +const initialState = input + .split('\n') + .filter(line => !!line.trim() && !/#.*#.*#/.test(line)) + .map(line => line.replace(/^#(.*)#$/, '$1')); + +const height = initialState.length; +const width = initialState[0].length; + +const blizzards = initialState + .flatMap((line, y) => line + .split('') + .flatMap((c, x) => { + let direction; + switch (c) { + case '>': direction = D.R; break; + case 'v': direction = D.D; break; + case '<': direction = D.L; break; + case '^': direction = D.U; break; + case '.': return []; + default: throw new Error(`Invalid character: ${c}`); + } + return [new Blizzard(width, height, x, y, direction)]; + }) + ); + +function lcm(a, b) { + const larger = Math.max(a, b); + const smaller = Math.min(a, b); + + for (let m = 1; ; m++) { + const v = larger * m; + if (v % smaller === 0) { + return v; + } + } +} + +class Frame { + constructor(blizzards, width, height, t) { + this.width = width; + this.height = height; + this.t = t; + this.data = new Array(height).fill().map(_ => '.'.repeat(width)); + for (const blizzard of blizzards) { + const [x, y] = blizzard.posAtTime(t); + this.data[y] = ( + this.data[y].slice(0, x) + + '#' + + this.data[y].slice(x + 1) + ); + } + } + + getPos(x, y) { + // Start + if (x === 0 && y === -1) { + return '.'; + } + if (x === width - 1 && y === this.height) { + return '.'; + } + return this.data[y]?.[x] ?? '#'; + } + + movesFromPos(x, y) { + return [ + [x, y], + [x + 1, y], + [x, y + 1], + [x - 1, y], + [x, y - 1] + ].filter(pos => + this.getPos(...pos) !== '#' + ); + } + + print() { + console.log(this.data.join('\n')); + } +} + +const nFrames = lcm(width, height); +const frames = new Array(nFrames) + .fill() + .map((_, t) => new Frame(blizzards, width, height, t)); + +function fastestRoute(start, end, timeOffset = 0) { + let positions = [start]; + for (let t = timeOffset + 1; ; t++) { + const frame = frames[t % nFrames]; + + const newPositions = []; + + for (const [x, y] of positions) { + const moves = frame.movesFromPos(x, y); + if (moves.find(move => move[0] === end[0] && move[1] === end[1])) { + return t; + } + newPositions.push(...frame.movesFromPos(x, y)); + } + + // Remove duplicates and assign to positions + positions = newPositions.filter((pos1, i, arr) => + arr.findLastIndex( + pos2 => pos1[0] === pos2[0] && pos1[1] === pos2[1] + ) === i + ); + } +} + +const start = [0, -1]; +const end = [width - 1, height]; + +const fastestTime = fastestRoute(start, end); +console.log('The fastest time to navigate the blizzards is:', fastestTime); + +const fastestTimeWithSnacks = fastestRoute( + start, + end, + fastestRoute(end, start, fastestTime) +); +console.log( + 'The fastest time with a return trip for snacks is:', + fastestTimeWithSnacks +);