Ben Ashton
1 year ago
2 changed files with 184 additions and 0 deletions
@ -0,0 +1,22 @@
|
||||
#.###################################################################################################################################################### |
||||
#>.^v.>^v>><>>^>>v>v^>.<>^.>v^v>..<<><>>>v.><^<.>^.<^>^v^>>v<^>><.^<^.<v>v><><^^<^>>^<<<>.>.>^^^<^<^>.>^v.>.>^>^v<<^^<>><vv<><>>>v^.>^<vvv>.^>v.>.v<v.<# |
||||
#<^^^><<vv<vv^^><>><>vv.vv<>vvvv<vv.>^>^>v^vvv>.<^>v<>>.<><<><>v<v^^<^.v^^><<><^><<.v.vv<<><.<<<<v^^>><^<><^<<<<.>v<^v<v>^>>>><.v>^>.>^<^>>v<v<^vv<v>v<# |
||||
#>>>>^.>.^v>v<.^<^vv^^^<>.>>.>><<.v<v^><^><.<<<><.^v^^v<><v^v><vv<<v<..<<^<.<>^<^v.^>^<v^v.^<v>v<><>>>^v<v.^^<><>><>v<^v>>>^v<v^>v<^vvv><v>^><<><^v>>^># |
||||
#><<>^>v>.<^^v><v.<>v^<<<>^^v>^.v<<>><>v.v>v^.v>>>^<^><.>>v<<<>vv^.>^>^<><^.>.v^^<v<.^>^><>^<v>vv^v>v^<>><<vv>>>v^^<^v>>>v.<<>.v<><^^v<.^>v>.<^^><vv.^># |
||||
#<.v^vv^vv>><v>^>>^.vvv^^<v<.v^>><<>v<v<<^<<<vv.>><>v.^>vvv<v<v>^<>>v><.>^.>.^<<<.^vvv>^..^>>.v<v.^v^<^^<<^<^>^v>^v<vvv.v^^v<<<>^v<<<v><>v^<<^><.v>v<v># |
||||
#<^v^><>v^<.>vv>.^<<<vv^^<><^<^.^^<.^vv^>v^^.^<^>^.^<><><^<<>><vv<v^<>v<><>v^^v<.<v^<v>vvv>^>>^<^><>>^vv<^>><v><^.<<v><v.>^v^v>>>^<vv>v<^<><>.^^>.v><<># |
||||
#<.><^v>^<.>>v<v>v<^v^v.^>v<.v>.<vv>^>v<v<<>^^.<>.<.vvv<^>v^.^<^v>^.>.v<.v>><<<^vv^.^<<^<^v<v^^^>.><>v.^^.^><<.<vvvv<v><<^vv.<^^^^>^<.<><<>>^>v>.^^v.<<# |
||||
#<<^.>v<><>.^>>^v<<^>vv<^v>><.<v^v^<<><v><^^.^^^>^.><^>v^v<<<<>>^>v>^v<^<>..v<.<v<><>v^>v^^v..<>^<><>^v<<v<<><<.v>...>^^v>.>v^.^>vv>^v<>v><<^.>^^..<<^<# |
||||
#><v^<^..>>v<v.<>vvv>v<v^>>>.v^<^v<>.>^<.<<^<>.<..>.^.>^^>.^.^^>v.v^>v<^>^v.<v^>v^^^vv><^>^>>>v><v<>>>..v.<^<.<<<>>><^<<v>>^<.>^^^<><^<.<^>^<v.^>vv>vv># |
||||
#<><>v^..>v>^^^^<.v>v>..<>><^><<^v.v.<v^<v><><<<v>>>^^>>v<<>^^<^^^>v.<<>v^<v><v^<<v>>^v>>v><v^^>vv^<^v<^v^.><>vv>>>.>>v^<v^v>><^^v.^<v.><v^^<<<v^>>^v^># |
||||
#>v><>><<^.>v^.v<<<v><^vv<.v^>v<.>vv>v>>^<^<<><>v>v<v..v<^<.vvv<^>^^v<^.>.^^^>^^v.^.>^>>>^><v<.v<^^^^.v.vv<<vv<.>v<^>v^<<vv^v><v>v^>>v.^.^<<.vv>^^^<^v<# |
||||
#<v^<>>^>>^..vv<^<<<v.v.^vv<.<^^<v^<.>^<.^.<<v^^^<^><>.>>>><^<>^>^v>><v^>v.<.^<v>^^>><><.>^v^>^<>^<^^.<^>^^^^^^vv<>vv^v^v>^v^<v<vv><<vv<^^^.>>^><<<.^>># |
||||
#<.<v^.>>><vv^.<^^^>>>.><v<v>^<>>.^^vv>>^<<<^v><^^<v<>>^^^^<v^<>^^>><^>v^<<>>v<^.>vvvv>>^<.^>.v<^^<<v>>.vv.v^v<>>>><^v^>>>.v.^^v<<v>v^^>^v>^v<><.v><^v.# |
||||
#><<^v><<<.v<^^><^v<^v.<<<.^^.v>vvvv>^>><^^v<>.<>><<.v^>..^<>.><^>^>^v^^>v<<vv>^>vvv.>^^>^^<<>v>v^>^^^<^v>.v>^<><^<.v<^><<>^.^>>><<v^>v^v^v<^v>>^^^><^<# |
||||
#<^>v<>.>>^.v>^<v><>.v.^^v>^<..v.^>^>.v>>><vv^<>^^>v><>vv.<v<v^vv<.^vv<><v>>>^v><v^vvvvv.<vv>>^v<<.v>v^>v><<<^.<<<<>vv>^>>^<>^^<vvv^<vvvvvv><^^<<<>^^<># |
||||
#>>.>>.^>..<^<^.<<v<><>..^^<..v<.><^^<v.v^v><>^vv<v<^v<^>.v<vvvv><<<>^>^v^>v<>><vv^v<>>>>>v<v<>^.>><v<v.>><v<v^^<v.<><.<>^^^^v<<^>>.><<.><.v^^..>>>.v>># |
||||
#>v<v^^<^vvv.>^<>>v^v.vv^>v^vvvvv<>.v^.v<.><>^>>^<<v<^^v>v><^.>>^><>v<vv>v<<^>^>^.<v>vv<<>^^>^<<>v><v>.<<^.<>><<^<<.^<^<<<.^>.<.vv>v^v<^>vvv>^v<<<<v<<<# |
||||
#<^<>v^.^<><>^<>^>v<v.^v..v..vv^.<.^^>^>^><.vv>>.<^..^^^<^<.^^<^^v^<^.<<<<<v<^<<><v>^v.<<>.^>><^>^v>><<v<>^v^<<^>>v<<^.v<<^vv>>v>^.v^^^<><vv^^<<>.v.<><# |
||||
#..vv<v><^vv>.v<v<^v><vvv>.v>^v^>>vv^.^>v.v^^<<>^^<>^<^^>.<^v<v>^>^<v<<v<>v<^^^>v>><><<><.<<><^<v<<>v^^>>v><>^v^.v>^^v<^><><^v<^<vv.vvv><><<>v<<><>v<.># |
||||
#>>.<^^>><<<^>v<^^>>^<<>><<.<^^^<^^^<^<v<^<<^><<v<v<^^>vv<<^v>.^<^^>><>^^^.>v>v^<^>>v><<^<^^^>v><<>^^v><>v..<.^^>..vv.<v>>>^<<v^<v^^v>>>>.>vv.>>^v>^v<.# |
||||
######################################################################################################################################################.# |
@ -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 |
||||
); |
Loading…
Reference in new issue