Advent of Code - Day 14 - Part 2

https://adventofcode.com/2023/day/14
mail@pastecode.io avatar
unknown
javascript
7 months ago
4.0 kB
90
Indexable
Never
const fs = require('fs');
const path = require('path');
const { DateTime } = require('luxon');

const startTime = DateTime.now();

const inputPath = path.join(__dirname, 'data', 'input.txt');

const lines = fs
    .readFileSync(inputPath, { encoding: 'utf8' })
    .trim()
    .split('\n')
    .map((line) => line.trim().split(''));

const FINAL_CYCLE = 1_000_000_000;

const directions = ['n', 'w', 's', 'e'];

let rockRows = [];

let uniqueSnapshots = [];
let minRunsRequired = FINAL_CYCLE;
let run = 0;
for (; run < minRunsRequired; ++run) {
    rockRows.length = 0;
    for (const direction of directions) {
        for (let i = 0; direction === 'n' || direction === 's' ? i < lines[0].length : i < lines.length; ++i) {
            let firstDot = -1;
            for (
                let j =
                    direction === 'n' || direction === 'w'
                        ? 0
                        : direction === 's'
                        ? lines.length - 1
                        : lines[0].length - 1;
                direction === 'n' ? j < lines.length : direction === 'w' ? j < lines[0].length : j >= 0;
                direction === 'n' || direction === 'w' ? ++j : --j
            ) {
                function get(outer, inner) {
                    if (direction === 'n' || direction === 's') return lines[inner][outer];
                    return lines[outer][inner];
                }
                function getY(outer, inner) {
                    if (direction === 'n' || direction === 's') return inner;
                    return outer;
                }
                function set(outer, inner, val) {
                    if (direction === 'n' || direction === 's') {
                        lines[inner][outer] = val;
                    } else {
                        lines[outer][inner] = val;
                    }
                }
                function addRockWeight(weight) {
                    if (direction === directions[directions.length - 1]) {
                        rockRows.push(weight);
                    }
                }

                if (get(i, j) === '.') {
                    if (firstDot === -1) {
                        firstDot = j;
                    } else {
                        continue;
                    }
                } else if (get(i, j) === '#') {
                    firstDot = -1;
                } else if (get(i, j) === 'O') {
                    if (firstDot > -1) {
                        set(i, firstDot, 'O');
                        set(i, j, '.');
                        j = firstDot;
                        addRockWeight(lines.length - getY(i, firstDot));
                        firstDot = -1;
                    } else {
                        addRockWeight(lines.length - getY(i, j));
                        continue;
                    }
                }
            }
        }
    }

    // Try to find a quicker minimum runs required
    if (minRunsRequired === FINAL_CYCLE) {
        const snapshot = lines.map((row) => row.reduce((p, c) => p + c)).reduce((p, c) => p + '\n' + c);
        const index = uniqueSnapshots.indexOf(snapshot);
        if (index !== -1) {
            const duplicateItems = uniqueSnapshots.length - index;
            const itemsIntoDupSet = (FINAL_CYCLE - uniqueSnapshots.length) % duplicateItems;
            minRunsRequired =
                uniqueSnapshots.length + // Elements before duplicates start
                itemsIntoDupSet + // How far into the set of duplicate numbers we will be on the final cycle
                (itemsIntoDupSet === 0 ? itemsIntoDupSet : 0); // Since we needed an additional run before recognising duplicates, we'll need to pad an extra duplicate set if the final cycle ends at the end of the duplicate set
        } else {
            uniqueSnapshots.push(snapshot);
        }
    }
}
console.log('final run', run);

console.log(rockRows.reduce((p, c) => p + c, 0));

console.log(DateTime.now().diff(startTime).valueOf() + ' millis');
Leave a Comment