5th December, 2024

Make the printing queue ordered again

473 words
3 min.
Post preview image
Advent of Code 2024: Day 5

Finally, a bit of a trickier task today 💪

We have a list of page ordering rules like

47|53
97|13
97|61

and list of page numbers to print.

75,47,61,53,29
97,61,53,29,13
75,29,13

I have an incurable idiosyncrasy for printers and print queues, but fortunately in this case it's purely a mental / coding effort - no need to deal with actual printers failing at their job 😝

For part 1, all we need to do for each set of page numbers is to verify whether, based on the rules, the pages are in the correct order. I solved this with low effort by simply considering only the rules containing the page numbers for the list under consideration (obviously, no need to check against rule 1|2 if the printing queue is [3, 4, 5]).

let isQueueValid = true;

// Consider only the rules affecting the current queue
const rulesToCheck = rules.filter(
    (rule) => queue.includes(rule.a) && queue.includes(rule.b)
);

for (const ruleToCheck of rulesToCheck) {
    const aIndex = queue.indexOf(ruleToCheck.a); // Get the indices of the numbers in the list corresponding to the rule
    const bIndex = queue.indexOf(ruleToCheck.b);

    // If the first number of the rule comes after the second number, then the queue is not ordered correctly
    if (aIndex > bIndex) {
        isQueueValid = false;
        break;
    }
}

Part 2 was, as I anticipated, a bit more challenging: now we have to considered all incorrectly-ordered queues and sort them according to the various rules. It took my a bit of digging and remembering Data Structures & Algorithms classes from university, but in the end I thought it would be best to treat this as a topological sorting problem: from a directed graph built from the list of rules (each number is a node, and each pair is a directed edge between the two nodes), we can then run the graph to get the correct orders of the numbers.

I recommend referring to Wikipedia for the detailed explanation and the excellent tutorial on GeeksForGeeks.

Here's my implementation of it, anyway:

function topologicalSort(numbers: Queue, rules: QueueRule[]) {
    const adjList = new Map();
    const inDegree = new Map();

    numbers.forEach((num) => {
        adjList.set(num, []);
        inDegree.set(num, 0);
    });

    rules.forEach((rule) => {
        adjList.get(rule.a).push(rule.b);
        inDegree.set(rule.b, (inDegree.get(rule.b) ?? 0) + 1);
    });

    const queue: number[] = [];
    const result: number[] = [];

    inDegree.forEach((degree, node) => {
        if (degree === 0) {
            queue.push(node);
        }
    });

    while (queue.length > 0) {
        const node = queue.shift();
        if (node) result.push(node);

        adjList.get(node).forEach((neighbor: number) => {
            inDegree.set(neighbor, inDegree.get(neighbor) - 1);

            if (inDegree.get(neighbor) === 0) {
                queue.push(neighbor);
            }
        });
    }

    if (result.length === numbers.length) {
        return result;
    } else {
        throw new Error('The graph has a cycle, no valid ordering exists.');
    }
}

Criss-Crossing XMAS Clues
Save the Guard from that Loop