|
| 1 | +const shapes = [ |
| 2 | + Object.assign([[0, 0], [1, 0], [2, 0], [3, 0]], { width: 4 }), |
| 3 | + Object.assign([[1, 0], [0, 1], [1, 1], [2, 1], [1, 2]], { width: 3 }), |
| 4 | + Object.assign([[0, 0], [1, 0], [2, 0], [2, 1], [2, 2]], { width: 3 }), |
| 5 | + Object.assign([[0, 0], [0, 1], [0, 2], [0, 3]], { width: 1 }), |
| 6 | + Object.assign([[0, 0], [1, 0], [0, 1], [1, 1]], { width: 2 }), |
| 7 | +]; |
| 8 | + |
| 9 | +const pushCharToDirection: Record<string, number> = { "<": -1, ">": 1 }; |
| 10 | + |
| 11 | +function stringify(chamber: boolean[][]) { |
| 12 | + let result = ""; |
| 13 | + for (let i = chamber.length - 1; i >= 0; i--) { |
| 14 | + for (const item of chamber[i]) result += item ? "#" : "."; |
| 15 | + result += "\n"; |
| 16 | + } |
| 17 | + return result; |
| 18 | +} |
| 19 | + |
| 20 | +export default function solve(input: string, { rocks = 1000000000000 } = {}) { |
| 21 | + const chamber: boolean[][] = []; |
| 22 | + let atFirstRepeat: |
| 23 | + | { rock: number; height: number; secondRepeatRegExp: RegExp } |
| 24 | + | undefined; |
| 25 | + let skippedHeight = 0; |
| 26 | + for (let rock = 1, i = 0, j = 0; rock <= rocks; rock++, i++) { |
| 27 | + const shape = shapes[i % shapes.length]; |
| 28 | + for (let x = 2, y = chamber.length + 3; y >= 0; y--) { |
| 29 | + const dx = pushCharToDirection[input[j++ % input.length]]; |
| 30 | + if ( |
| 31 | + x + dx >= 0 && x + dx + shape.width <= 7 && |
| 32 | + !shape.some(([rx, ry]) => chamber[y + ry]?.[x + rx + dx]) |
| 33 | + ) x += dx; |
| 34 | + if (y === 0 || shape.some(([rx, ry]) => chamber[y + ry - 1]?.[x + rx])) { |
| 35 | + for (const [rx, ry] of shape) { |
| 36 | + chamber[y + ry] ??= Array.from({ length: 7 }, () => false); |
| 37 | + chamber[y + ry][x + rx] = true; |
| 38 | + } |
| 39 | + break; |
| 40 | + } |
| 41 | + } |
| 42 | + if (skippedHeight) continue; |
| 43 | + if (rock < 10000) continue; |
| 44 | + if (!atFirstRepeat) { |
| 45 | + const match = /((?:[.#]{7}\n){40,}?)(\1+)/m.exec(stringify(chamber)); |
| 46 | + if (match === null) continue; |
| 47 | + const [, lines, repeats] = match; |
| 48 | + const times = repeats.length / lines.length; |
| 49 | + const secondRepeatRegExp = new RegExp(`(?:${lines}){${times + 2}}`, "m"); |
| 50 | + atFirstRepeat = { rock, height: chamber.length, secondRepeatRegExp }; |
| 51 | + } else if (atFirstRepeat.secondRepeatRegExp.test(stringify(chamber))) { |
| 52 | + const repeatedRocks = rock - atFirstRepeat.rock; |
| 53 | + const repeatedHeight = chamber.length - atFirstRepeat.height; |
| 54 | + const toRepeat = Math.floor((rocks - rock) / repeatedRocks); |
| 55 | + rock += toRepeat * repeatedRocks; |
| 56 | + skippedHeight = toRepeat * repeatedHeight; |
| 57 | + } |
| 58 | + } |
| 59 | + return chamber.length + skippedHeight; |
| 60 | +} |
0 commit comments