CodePen Template
             
               .---_ 
              / / /\|
             / / | \ *
            /  /  \ \
           / /  / \  \ 
         ./~~~~~~~~~~~\.
        ( .",^. -". '.~ )
         '~~~~~~~~~~~~~' 
            
          

Advent of Code 2024 - Solutions

Did you already solve the Pi Day Coding Quest?

Lessons from Advent of Code 2023

Solutions 2023

Note: These solutions are made on JavaScript to run directly in the Chrome Dev Tools Console. Just go to the correspond Advent of Code Day input and open Dev Tools (right click and Inspect or Ctrl/Cmd + Shift + C) and paste the code. If you don't know Advent of Code, visit its website.

If you have any question regard to my solutions, feel free of contacting me: email, Instagram, X (Twitter)

--- Day 25: Code Chronicle ---

Go to page

Part 1

const code = $('*').textContent.trim().split('\n\n');
const h = code[0].split('\n').length-2;
const locks = [];
const keys = [];
code.forEach(line => {
    line = line.split('\n');
    const heights = [];
    if(line[0][0] === '#') {
        for (let x = 0; x < line[0].length; x++) {
            let i = 0;
            while (line[i][x] === '#') {
                i++;
            }
            heights.push(i-1);
        }
        locks.push(heights);
    } else if(line[0][0] === '.') {
        for (let x = 0; x < line[0].length; x++) {
            let i = line.length-1;
            let counter = -1;
            while (line[i][x] === '#') {
                i--;
                counter++;
            }
            heights.push(counter);
        }
        keys.push(heights);
    }
});
let fit = 0;
for (let lock of locks) {
    for (let key of keys) {
        let overlap = false;
        for (let i = 0; i < lock.length; i++) {
            if (lock[i] + key[i] > h) {
                overlap = true;
                break;
            }
        }
        if (overlap) {
            overlap = false;
        } else {
            fit++;
        }
    }
}
console.log('Part 1 ->',fit);Language:JavaScript

--- Day 24: Crossed Wires ---

Go to page

Part 1

let [data, instructions] = $0.textContent.trim().split('\n\n');
data = data.split('\n');
instructions = instructions.split('\n');
const dataMap = new Map();
data.forEach(d => {
    const [p1,p2] = d.split(': ');
    dataMap.set(p1, parseInt(p2));
});

while (instructions.length > 0) {
    instructions.forEach((ins,i) => {
        const [p1,p2,p3,p4,p5] = ins.split(' ');
        if (dataMap.get(p1) !== undefined && dataMap.get(p3) !== undefined) {
            if (p2 === 'AND') {
                dataMap.set(p5, dataMap.get(p1) && dataMap.get(p3));
            } else if (p2 === 'OR') {
                dataMap.set(p5, dataMap.get(p1) || dataMap.get(p3));
            } else {
                dataMap.set(p5, dataMap.get(p1) ^ dataMap.get(p3));
            }
            instructions.splice(i,1);
        }
    });
}

const zKeys = Array.from(dataMap.keys())
    .filter(key => key.startsWith('z'))
    .sort((a, b) => b.localeCompare(a));

let bits = '';
for (const key of zKeys) {
    bits += dataMap.get(key);
}
const decimal = parseInt(bits, 2);
console.log('Part 1 ->', decimal);Language:JavaScript

--- Day 23: LAN Party ---

Go to page

Part 1 & 2

const code = $('*').textContent.trim().split('\n');
const connections = new Map();
code.forEach(line => {
    const [com1, com2] = line.split('-');
        if (!connections.has(com1)) connections.set(com1, new Set());
        if (!connections.has(com2)) connections.set(com2, new Set());
        connections.get(com1).add(com2);
        connections.get(com2).add(com1);
});

const triples = new Set();
const computers = Array.from(connections.keys());
for (let i = 0; i < computers.length; i++) {
    const com1 = computers[i];
    const ns = Array.from(connections.get(com1));
    for (let j = 0; j < ns.length; j++) {
        for (let k = j+1; k < ns.length; k++) {
            const com2 = ns[j];
            const com3 = ns[k];
            if(connections.get(com2).has(com3)) {
                const triple = [com1, com2, com3].sort().join(',');
                triples.add(triple);
            }
        }
    }
}
const r1 = Array.from(triples).filter(triple => 
    triple.split(',').some(comp => comp.startsWith('t'))
).length;
console.log('Part 1 ->', r1);

function findMaxClique(connections) {
    let maxClique = [];
    function bronKerbosch(R, P, X) {
        if (P.size === 0 && X.size === 0) {
            if (R.size > maxClique.length) {
                maxClique = Array.from(R);
            }
            return;
        }
        const pivot = Array.from(P.size ? P : X)[0];
        const pivotNeighbors = connections.get(pivot);
        
        for (const v of P) {
            if (!pivotNeighbors.has(v)) {
                const vNeighbors = connections.get(v);
                bronKerbosch(
                    new Set([...R, v]),
                    new Set([...P].filter(n => vNeighbors.has(n))),
                    new Set([...X].filter(n => vNeighbors.has(n)))
                );
                P.delete(v);
                X.add(v);
            }
        }
    }
    bronKerbosch(
        new Set(),
        new Set(connections.keys()),
        new Set()
    );
    return maxClique.sort().join(',');
}
console.log('Part 2 ->', findMaxClique(connections));Language:JavaScript

--- Day 22: Monkey Market ---

Go to page

Part 1

const code = $('*').textContent.trim().split('\n').map(Number);
let r1 = 0;
const seqTotal = {};
code.forEach(line => {
    let num = line;
    const buyer = [num%10];
    for (let i = 0; i < 2000; i++) {
        num = (num ^ (num * 64)) % 16777216;
        if (num < 0) num += 16777216;
        num = (num ^ Math.trunc(num / 32)) % 16777216;
        if (num < 0) num += 16777216;
        num = (num ^ (num * 2048)) % 16777216;
        if (num < 0) num += 16777216;
        buyer.push(num%10);
    }
    r1 += num;
    const v = new Set();
    for (let j = 0; j < buyer.length - 4; j++) {
        let [a, b, c, d, e] = buyer.slice(j, j + 5);
        const seq = [b - a, c - b, d - c, e - d];
        const seqKey = JSON.stringify(seq);
        if (v.has(seqKey)) continue;
        v.add(seqKey);
        seqTotal[seqKey] ??= 0;
        seqTotal[seqKey] += e;
    }
});

console.log('Part 1 ->', r1);
console.log('Part 2 ->', Math.max(...Object.values(seqTotal)));Language:JavaScript

--- Day 20: Race Condition ---

Go to page

Part 1 & 2

I learnt this solution from this video

const map = $('*').textContent.trim().split('\n');
const rows = map.length;
const cols = map[0].length;
const DIRS = [[0,-1],[1,0],[0,1],[-1,0]];
let pos = {x:0, y:0};
let counter = 0;
outer:for (let i = 0; i < rows; i++) {
    for (let j = 0; j < cols; j++) {
        if (map[i][j] === 'S') {
            pos.x = j;
            pos.y = i;
            break outer;
        }
    }
}

let dists = Array.from({ length: rows }, () => Array(cols).fill(-1));
dists[pos.y][pos.x] = 0;

while (map[pos.y][pos.x] !== 'E') {
    for (let [x,y] of DIRS) {
        const nx = pos.x + x;
        const ny = pos.y + y;
        if (ny < 0 || nx < 0 || ny >= rows || nx >= cols) continue;
        if (map[ny][nx] === "#") continue;
        if (dists[ny][nx] !== -1) continue;
        dists[ny][nx] = dists[pos.y][pos.x] + 1;
        pos.x = nx;
        pos.y = ny;
    }
}

for (let i = 0; i < rows; i++) {
    for (let j = 0; j < cols; j++) {
        if (map[i][j] === '#') continue;
        for (let [nx,ny] of [[j,i+2],[j+1,i+1],[j+2,i],[j+1,i-1]]) {
        if (nx < 0 || ny < 0 || nx >= cols || ny >= rows) continue;
            if (map[ny][nx] === '#') continue;
            if (Math.abs(dists[i][j] - dists[ny][nx]) >= 102) counter++;
        }
    }
}

console.log('Part 1 ->', counter);

counter = 0;
for (let i = 0; i < rows; i++) {
    for (let j = 0; j < cols; j++) {
        if (map[i][j] === '#') continue;
        for (let r = 2; r < 21; r++) {
            for (let dy = 0; dy < r+1; dy++) {
                const dx = r - dy;
                const dirset = new Set([
                    `${i + dy},${j + dx}`,
                    `${i + dy},${j - dx}`,
                    `${i - dy},${j + dx}`,
                    `${i - dy},${j - dx}`
                ]);
                for (const dir of dirset) {
                    const [ny, nx] = dir.split(',').map(Number);
                    if (nx < 0 || ny < 0 || nx >= cols || ny >= rows) continue;
                    if (map[ny][nx] === '#') continue;
                    if ((dists[i][j] - dists[ny][nx]) >= 100+r) counter++;
                }
            }
        }
    }
}

console.log('Part 2 ->', counter);Language:JavaScript

--- Day 19: RAM Run ---

Go to page

Part 1 & 2

let [patterns, designs] = $('*').textContent.trim().split('\n\n');
patterns = patterns.split(', ');
designs = designs.split('\n');
patterns.sort((a, b) => b.length - a.length);

function isPatternPossible(design, memo) {
    if (design === '') return true;
    if (memo.has(design)) {
        return memo.get(design);
    }
    for (const pattern of patterns) {
        if (pattern.length > design.length) continue;
        if (pattern[0] !== design[0]) continue;
        if (design.startsWith(pattern)) {
            const remaining = design.slice(pattern.length);
            if (isPatternPossible(remaining, memo)) {
                memo.set(design, true);
                return true;
            }
        }
    }
    memo.set(design, false);
    return false;
}

let r1 = 0;
let r2 = 0;
for (const design of designs) {
    const memo = new Map();
    const countMemo = new Map();
    r1 += isPatternPossible(design, memo) ? 1 : 0;
    r2 += patternCount(design, countMemo);
}
console.log('Part 1 ->', r1);
console.log('Part 2 ->', r2);

function patternCount(design, memo) {
    if (design === '') return 1;
    if (memo.has(design)) {
        return memo.get(design);
    }
    let count = 0;
    for (const pattern of patterns) {
        if (pattern.length > design.length) continue;
        if (pattern[0] !== design[0]) continue;
        if (design.startsWith(pattern)) {
            const remaining = design.slice(pattern.length);
            count += patternCount(remaining, memo);
        }
    }
    memo.set(design, count);
    return count;
}Language:JavaScript

--- Day 18: RAM Run ---

Go to page

Part 1 & 2

const code = $('*').textContent.trim().split('\n');
const map = Array.from({ length: 71 }, () => Array(71).fill('.'));
const rows = map.length;
const cols = map[0].length;
const DIRS = [[0,-1],[1,0],[0,1],[-1,0]];
let bytes = 1024;

for (let i = 0; i < bytes; i++) {
    const [x,y] = code[i].split(',').map(Number);
    map[y][x] = '#';
}

function bfs() {
    const start = {x:0, y:0, g:0};
    const v = new Set();
    const q = [start];
    v.add(`${start.x}-${start.y}`);
    while (q.length > 0) {
        const el = q.shift();
        if (el.x === 70 && el.y === 70) {
            return el.g;
        }
        for (let [x,y] of DIRS) {
            const nx = el.x+x;
            const ny = el.y+y;
            if (nx >= 0 && ny >= 0 && nx < cols && ny < rows && map[ny][nx] !== '#') {
                if (!v.has(`${nx}-${ny}`)) {
                    q.push({x:nx,y:ny,g:el.g+1});
                    v.add(`${nx}-${ny}`)
                }
            }
        }
    }
    return -1;
}
console.log('Part 1 ->',bfs());

while (bfs() > 0) {
    bytes++;
    const [x,y] = code[bytes].split(',').map(Number);
    map[y][x] = '#';
}
console.log('Part 2 ->',code[bytes]);Language:JavaScript

--- Day 17: Chronospatial Computer ---

Go to page

Part 1

let [regs, program] = $0.textContent.trim().split('\n\n');
const registers = {};
let pointer = 0;
program = program.split(' ')[1].split(',').map(Number);
regs.split('\n').forEach(reg => {
    registers[reg.slice(9, 10)] = Number(reg.slice(12));
});

function getValue(operand) {
    if (operand <= 3) return operand;
    else if (operand === 4) return registers.A;
    else if (operand === 5) return registers.B;
    else if (operand === 6) return registers.C;
    else if (operand === 7) return registers.A;
    throw new Error("Invalid combo operand 7");
}

function exec(opcode, operand) {
    switch (opcode) {
        case 0:
            registers.A = Math.floor(registers.A / 2**getValue(operand));
            break;
            
        case 1:
            registers.B ^= operand;
            break;

        case 2:
            registers.B = getValue(operand) % 8;
            break;

        case 3:
            if (registers.A !== 0) {
                pointer = getValue(operand);
                return true;
            }
            break;
            
        case 4:
            registers.B ^= registers.C;
            break;

        case 5:
            r1.push(getValue(operand)%8);
            break;

        case 6:
            registers.B = Math.floor(registers.A / 2**getValue(operand));
            break;

        case 7:
            registers.C = Math.floor(registers.A / 2**getValue(operand));
            break;
    }
}
const r1 = [];
while (pointer < program.length) {
    const opcode = program[pointer];
    const operand = program[pointer+1];
    const jump = exec(opcode, operand);
    if (!jump) pointer += 2;
}
console.log('Part 1 ->',r1.join(','));Language:JavaScript

--- Day 16: Reindeer Maze ---

Go to page

Part 1 & 2

ℹ️ Run the code and go to grab a drink... because it takes around 5 minutes to drop answers.

class PriorityQueue {
constructor() {
    this.values = [];
}

enqueue(node, priority) {
    this.values.push({node, priority});
    this._bubbleUp();
}

dequeue() {
    if (this.values.length === 0) return null;
    
    const min = this.values[0];
    const end = this.values.pop();
    
    if (this.values.length > 0) {
        this.values[0] = end;
        this._sinkDown();
    }
    
    return min;
}

size() {
    return this.values.length;
}

_bubbleUp() {
    let idx = this.values.length - 1;
    const element = this.values[idx];
    
    while (idx > 0) {
        const parentIdx = Math.floor((idx - 1) / 2);
        const parent = this.values[parentIdx];
        
        if (element.priority >= parent.priority) break;
        
        this.values[parentIdx] = element;
        this.values[idx] = parent;
        idx = parentIdx;
    }
}

_sinkDown() {
    let idx = 0;
    const length = this.values.length;
    const element = this.values[0];
    
    while (true) {
        const leftChildIdx = 2 * idx + 1;
        const rightChildIdx = 2 * idx + 2;
        let leftChild, rightChild;
        let swap = null;
        
        if (leftChildIdx < length) {
            leftChild = this.values[leftChildIdx];
            if (leftChild.priority < element.priority) {
                swap = leftChildIdx;
            }
        }
        
        if (rightChildIdx < length) {
            rightChild = this.values[rightChildIdx];
            if (
                (swap === null && rightChild.priority < element.priority) || 
                (swap !== null && rightChild.priority < leftChild.priority)
            ) {
                swap = rightChildIdx;
            }
        }
        
        if (swap === null) break;
        
        this.values[idx] = this.values[swap];
        this.values[swap] = element;
        idx = swap;
    }
}
}

const map = $('*').textContent.trim().split('\n');
let start = { x: 0, y: 0, c: 0, dx: 1, dy: 0 };

map.forEach((line, y) => {
[...line].forEach((char, x) => {
    if (map[y][x] === 'S') {
        start.x = x;
        start.y = y;
    }
});
});

let q = new PriorityQueue();
q.enqueue(start, 0);
let v = new Set();
v.add(`${start.x}-${start.y}-${start.dx}-${start.dy}`);
let minCost = Infinity;
let optimalPaths = [];
let visitedPlaces = new Set();

while (q.size() > 0) {
const el = q.dequeue();
v.add(`${el.node.x}-${el.node.y}-${el.node.dx}-${el.node.dy}`);

if (map[el.node.y][el.node.x] === "E") {
    if (el.node.c < minCost) {
        minCost = el.node.c;
        optimalPaths = [{ path: el.node.path || [`${el.node.x}-${el.node.y}`], cost: el.node.c }];
    } else if (el.node.c === minCost) {
        optimalPaths.push({ path: el.node.path || [`${el.node.x}-${el.node.y}`], cost: el.node.c });
    }
    continue;
}

for (let [nx, ny, nc, ndx, ndy] of [
    [el.node.x + el.node.dx, el.node.y + el.node.dy, el.node.c + 1, el.node.dx, el.node.dy],
    [el.node.x, el.node.y, el.node.c + 1000, el.node.dy, -el.node.dx],
    [el.node.x, el.node.y, el.node.c + 1000, -el.node.dy, el.node.dx]
]) {
    if (map[ny][nx] === "#") continue;
    if (v.has(`${nx}-${ny}-${ndx}-${ndy}`)) continue;
    let newPath = (el.node.path || [`${el.node.x}-${el.node.y}`]).concat(`${nx}-${ny}`);
    q.enqueue({ x: nx, y: ny, c: nc, dx: ndx, dy: ndy, path: newPath }, nc);
}
}

optimalPaths.forEach(optPath => {
optPath.path.forEach(cell => visitedPlaces.add(cell));
});

console.log('Part 1 ->', minCost);
console.log('Part 2 ->', visitedPlaces.size);Language:JavaScript

--- Day 15: Warehouse Woes ---

Go to page

Part 1 & 2

let [map, ins] = $('*').textContent.trim().split('\n\n');
map = map.split('\n').map(line => [...line]);
const MOVES = {
    '^': [0, -1, 'v'],
    '>': [1, 0, 'h'],
    'v': [0, 1, 'v'],
    '<': [-1, 0, 'h']
};
const expansion = {
    '#': '##',
    'O': '[]',
    '.': '..',
    '@': '@.'
};
let robot = { x: 0, y: 0 };

map.forEach((line, y) => {
    line.forEach((char, x) => {
        if (char === "@") {
            robot.x = x;
            robot.y = y;
        }
    });
});

const expandedMap = [];
map.forEach(line => {
    const row = [];
    line.forEach(char => {
        row.push(expansion[char][0])
        row.push(expansion[char][1])
    });
    expandedMap.push(row);
});

ins = ins.split('').filter(char => char.trim() !== ''); 
let toMove = [[robot.x, robot.y]];

ins.forEach(m => {
    const move = MOVES[m];
    const nx = robot.x + move[0];
    const ny = robot.y + move[1];

    if (map[ny][nx] === '.') {
        map[robot.y][robot.x] = '.';
        robot.x = nx;
        robot.y = ny;
        map[ny][nx] = '@';
    } else if (map[ny][nx] === 'O') {
        toMove.push([nx, ny]);
        let x = nx + move[0];
        let y = ny + move[1];
        while (map[y][x] === 'O') {
            toMove.push([x, y]);
            x += move[0];
            y += move[1];
        }
        if (map[y][x] === '.') {
            let mx, my;
            for (let i = toMove.length - 1; i >= 0; i--) {
                mx = toMove[i][0];
                my = toMove[i][1];
                map[y][x] = map[my][mx];
                x = mx;
                y = my;
            }
            map[robot.y][robot.x] = '.';
            robot.x = nx;
            robot.y = ny;
        }
        toMove = [[robot.x, robot.y]];
    }
});

let r1 = 0;
map.forEach((line, y) => {
    line.forEach((char, x) => {
        if (char === "O") {
            r1 += 100 * y + x;
        }
    });
});
console.log('Part 1 ->', r1);

expandedMap.forEach((line, y) => {
    line.forEach((char, x) => {
        if (char === "@") {
            robot.x = x;
            robot.y = y;
        }
    });
});

toMove = [[robot.x, robot.y]];
let v;
ins.forEach(m => {
    const move = MOVES[m];
    const nx = robot.x + move[0];
    const ny = robot.y + move[1];
    if (expandedMap[ny][nx] === '.') {
        expandedMap[robot.y][robot.x] = '.';
        robot.x = nx;
        robot.y = ny;
        expandedMap[ny][nx] = '@';
    } else if (expandedMap[ny][nx] === ']' && move[2] === 'h') {
        toMove = [[robot.x, robot.y]];
        v = new Set();
        toMove.push([nx, ny]);
        toMove.push([nx-1, ny]);
        v.add(`${nx-1},${ny}`);
        let x = nx + move[0];
        let y = ny + move[1];
        while (expandedMap[y][x] === '[' || expandedMap[y][x] === ']') {
            if (!v.has(`${x},${y}`)) {
                toMove.push([x, y]);
                v.add(`${x},${y}`)
            }
            x += move[0];
            y += move[1];
        }
        if (expandedMap[y][x] === '.') {
            let mx, my;
            for (let i = toMove.length - 1; i >= 0; i--) {
                mx = toMove[i][0];
                my = toMove[i][1];
                expandedMap[y][x] = expandedMap[my][mx];
                x = mx;
                y = my;
            }
            expandedMap[robot.y][robot.x] = '.';
            robot.x = nx;
            robot.y = ny;
        }
        toMove = [[robot.x, robot.y]];
    } else if (expandedMap[ny][nx] === '[' && move[2] === 'h') {
        toMove = [[robot.x, robot.y]];
        v = new Set();
        toMove.push([nx, ny]);
        toMove.push([nx+1, ny]);
        v.add(`${nx+1},${ny}`);
        let x = nx + move[0];
        let y = ny + move[1];
        while (expandedMap[y][x] === '[' || expandedMap[y][x] === ']') {
            if (!v.has(`${x},${y}`)) {
                toMove.push([x, y]);
                v.add(`${x},${y}`)
            }
            x += move[0];
            y += move[1];
        }
        if (expandedMap[y][x] === '.') {
            let mx, my;
            for (let i = toMove.length - 1; i >= 0; i--) {
                mx = toMove[i][0];
                my = toMove[i][1];
                expandedMap[y][x] = expandedMap[my][mx];
                x = mx;
                y = my;
            }
            expandedMap[robot.y][robot.x] = '.';
            robot.x = nx;
            robot.y = ny;
        }
        toMove = [[robot.x, robot.y]];
    } else if ((expandedMap[ny][nx] === ']' || expandedMap[ny][nx] === '[') && move[2] === 'v') {
    toMove = [[robot.x, robot.y]];
    v = new Set();
    
    if (expandedMap[ny][nx] === ']') {
        toMove.push([nx-1, ny]);
        v.add(`${nx-1},${ny}`);
        toMove.push([nx, ny]);
        v.add(`${nx},${ny}`);
        let parts = [[nx-1,ny],[nx,ny]];
        checkBoxes(parts, m);
    } else {
        toMove.push([nx, ny]);
        v.add(`${nx},${ny}`);
        toMove.push([nx+1, ny]);
        v.add(`${nx+1},${ny}`);
        let parts = [[nx,ny],[nx+1,ny]];
        checkBoxes(parts, m);
    }
    if (toMove.length > 0) {
        toMove = orderCoords(toMove);
        let mx, my;
        let tx, ty;
        for (let i = toMove.length - 1; i >= 0; i--) {
            mx = toMove[i][0];
            my = toMove[i][1];
            tx = mx + MOVES[m][0];
            ty = my + MOVES[m][1];
            expandedMap[ty][tx] = expandedMap[my][mx];
            expandedMap[my][mx] = '.';
            tx = mx;
            ty = my;
        }
        robot.x = tx + MOVES[m][0];
        robot.y = ty + MOVES[m][1];
    }
    toMove = [[robot.x, robot.y]];
}
});

let r2 = 0;
expandedMap.forEach((line, y) => {
    line.forEach((char, x) => {
        if (char === "[") {
            r2 += 100 * y + x;
        }
    });
});
console.log('Part 2 ->', r2);

function checkBoxes(parts, m) {
    for (let part of parts) {
        const x = part[0] + MOVES[m][0];
        const y = part[1] + MOVES[m][1];
        if (expandedMap[y][x] === '#') {
            toMove = [];
            return false;
        }
        else if (expandedMap[y][x] === '[') {
            if (!v.has(`${x},${y}`)) {
                toMove.push([x,y]);
                v.add(`${x},${y}`);
                toMove.push([x+1,y]);
                v.add(`${x+1},${y}`);
            }
            if (!checkBoxes([[x,y],[x+1,y]], m)) {
                return false;
            }
        } else if (expandedMap[y][x] === ']') {
            if (!v.has(`${x},${y}`)) {
                toMove.push([x-1,y]);
                v.add(`${x-1},${y}`);
                toMove.push([x,y]);
                v.add(`${x},${y}`);
            }
            if (!checkBoxes([[x-1,y],[x,y]], m)) {
                return false;
            }
        }
    }
    return true;
}

function orderCoords(coords) {
    const sortDirection = coords[1][1] < coords[0][1] ? 'desc' : 'asc';
    coords.sort((a, b) => {
    if (sortDirection === 'desc') {
    return b[1] - a[1] || a[0] - b[0];
    } else {
    return a[1] - b[1] || a[0] - b[0];
    }
});
    return coords;
}Language:JavaScript

--- Day 14: Restroom Redoubt ---

Go to page

Part 1 & 2

const code = $0.textContent.trim();
const w = 101;
const h = 103;
const robots = parseRobotData(code);
function parseRobotData(input) {
    const lines = input.trim().split('\n');
    const robots = {};
    lines.forEach((line, index) => {
    const regex = /[p|v]=(-?\d+),(-?\d+)/g;
    const matches = [...line.matchAll(regex)];
    const position = matches[0].slice(1).map(Number);
    const velocity = matches[1].slice(1).map(Number);
    robots[index] = {
        p: position,
        v: velocity
    };
    });
    return robots;
}
const midX = Math.floor(w/2);
const midY = Math.floor(h/2);
let r1 = 0;
let minSF = Infinity;
let bestIteration = null;
for (let s = 1; s <= w * h; s++) {
    const result = [];
    Object.keys(robots).forEach(id => {
        const [px, py] = robots[id].p;
        const [vx, vy] = robots[id].v;
        robots[id].p[0] = (px + vx + w) % w;
        robots[id].p[1] = (py + vy + h) % h;
        result.push([robots[id].p[0],robots[id].p[1]]);
    });
    let q1 = 0; let q2 = 0; let q3 = 0; let q4 = 0;
    for (let [cx, cy] of result) {
        if (cx < midX && cy < midY) {
            q1++;
        } else if (cx > midX && cy < midY) {
            q2++;
        } else if (cx < midX && cy > midY) {
            q3++;
        } else if (cx > midX && cy > midY) {
            q4++;
        }        
    }
    const sf = q1 * q2 * q3 * q4;
    if (sf < minSF) {
        minSF = sf;
        bestIteration = s;
    }
    if (s === 100) r1 = sf;
}
console.log('Part 1 ->', r1);
console.log('Part 2 ->', bestIteration);Language:JavaScript

--- Day 13: Claw Contraption ---

Go to page

Part 1 & 2

ℹ️ The math for part 2, I found it in this video.

const pattern = /Button A: X\+(\d+), Y\+(\d+)\nButton B: X\+(\d+), Y\+(\d+)\nPrize: X=(\d+), Y=(\d+)/g;
const code = parseCoordinates($0.textContent.trim());

function parseCoordinates(text) {
    const results = [];
    let match;
    while ((match = pattern.exec(text)) !== null) {
        results.push({
            A: {
                x: parseInt(match[1]),
                y: parseInt(match[2])
            },
            B: {
                x: parseInt(match[3]),
                y: parseInt(match[4])
            },
            prize: {
                x: parseInt(match[5]),
                y: parseInt(match[6])
            }
        });
    }
    return results;
}
let r1 = 0;
code.forEach(({A, B, prize}) => {
    let minTotal = Infinity;
    for (let buttonA = 0; buttonA < 100; buttonA++) {
        for (let buttonB = 0; buttonB < 100; buttonB++) {
            const rx = buttonA * A.x + buttonB * B.x;
            const ry = buttonA * A.y + buttonB * B.y;
            if (rx === prize.x && ry === prize.y) {
                const total = buttonA * 3 + buttonB;
                if (total < minTotal) {
                    minTotal = total;
                }
            }
        }
    }
    if (minTotal !== Infinity) {
        r1 += minTotal;
    }
});
console.log('Part 1 ->', r1);

let r2 = 0;
code.forEach(({A, B, prize}) => {
    prize.x += 10000000000000;
    prize.y += 10000000000000;
    const ra = (prize.x * B.y - prize.y * B.x) / (A.x * B.y - A.y * B.x);
    const rb = (prize.x - A.x * ra) / B.x;
    if (ra % 1 === 0 && rb % 1 === 0) {
        r2 += parseInt(ra * 3 + rb);
    }
});
console.log('Part 2 ->', r2);Language:JavaScript

--- Day 12: Plutonian Pebbles ---

Go to page

Part 1 & 2

const code = $('*').textContent.trim().split('\n');
const rows = code.length;
const cols = code[0].length;
const DIRS = [[0,-1],[1,0],[0,1],[-1,0]];
const regions = [];
const visited = Array(rows).fill().map(() => Array(cols).fill(false));

function floodFill(r, c, char, region) {
    if (r < 0 || c < 0 || r >= rows || c >= cols || visited[r][c] || code[r][c] !== char) { return; }
    visited[r][c] = true;
    region.push([r,c]);
    for (let [x,y] of DIRS) {
        floodFill(r+y, c+x, char, region);
    }
}

function getPerimeter(area) {
    const coords = new Set(area.map(([r,c])=>`${r}-${c}`));
    let perimeter = 0;
    for (let [r,c] of area) {
        for (let [x,y] of DIRS) {
            const nr = r+y;
            const nc = c+x;
            if (!coords.has(`${nr}-${nc}`)) perimeter++;
        }
    }
    return perimeter;
}

for (let r = 0; r < rows; r++) {
    for (let c = 0; c < cols; c++) {
        if (!visited[r][c]) {
            const char = code[r][c];
            const region = [];
            floodFill(r, c, char, region);
            if (region.length > 0) {
                regions.push(region);
            }
        }
    }
}

let r1 = 0;
for (let region of regions) {
    r1 += region.length * getPerimeter(region);
}
console.log('Part 1 ->', r1);


function calculateRegionSides(region) {
    const sortedCoords = region.slice().sort((a, b) => 
        a[0] === b[0] ? a[1] - b[1] : a[0] - b[0]
    );
    const horizontalSides = new Set();
    const verticalSides = new Set();
    const coordMap = new Set(region.map(coord => coord.join(',')));
    
    for (const [x, y] of sortedCoords) {
        if (!coordMap.has(`${x},${y-1}`)) {
            horizontalSides.add(`h:${x},${y}-top`);
        }
        if (!coordMap.has(`${x},${y+1}`)) {
            horizontalSides.add(`h:${x},${y}-bottom`);
        }
        if (!coordMap.has(`${x-1},${y}`)) {
            verticalSides.add(`v:${x},${y}-left`);
        }
        if (!coordMap.has(`${x+1},${y}`)) {
            verticalSides.add(`v:${x},${y}-right`);
        }
    }
    
    const mergedH = new Set();
    const processedH = new Set();
    for (const side of horizontalSides) {
        if (processedH.has(side)) continue;
        const [, x, y, type] = side.match(/h:(\d+),(\d+)-(\w+)/);
        let currentX = parseInt(x);
        const currentY = parseInt(y);
        while (horizontalSides.has(`h:${currentX+1},${currentY}-${type}`)) {
            processedH.add(`h:${currentX+1},${currentY}-${type}`);
            currentX++;
        }
        mergedH.add(`h:${x}-${currentX},${y}-${type}`);
    }

    const mergedV = new Set();
    const processedV = new Set();
    for (const side of verticalSides) {
        if (processedV.has(side)) continue;
        const [, x, y, type] = side.match(/v:(\d+),(\d+)-(\w+)/);
        const currentX = parseInt(x);
        let currentY = parseInt(y);
        while (verticalSides.has(`v:${currentX},${currentY+1}-${type}`)) {
            processedV.add(`v:${currentX},${currentY+1}-${type}`);
            currentY++;
        }
        mergedV.add(`v:${x},${y}-${currentY}-${type}`);
    }
    return mergedH.size + mergedV.size;
}

let r2 = 0;
for (let region of regions) {
    r2 += region.length * calculateRegionSides(region);
}
console.log('Part 2 ->', r2);Language:JavaScript

--- Day 11: Plutonian Pebbles ---

Go to page

Part 1 & 2

let stones = $('*').textContent.trim().split(' ').map(Number);
let cache = new Map();

function count(stone, steps) {
    const cacheKey = `${stone}-${steps}`;
    if (cache.has(cacheKey)) return cache.get(cacheKey);
    if (steps === 0) return 1;
    if (stone === 0) {
        const result = count(1, steps - 1);
        cache.set(cacheKey, result);
        return result;
    }
    const str = stone.toString();
    const length = str.length;
    let result;
    if (length % 2 === 0) {
        const left = parseInt(str.slice(0, length / 2));
        const right = parseInt(str.slice(length / 2));
        result = count(left, steps - 1) + count(right, steps - 1);
    } else {
        result = count(stone * 2024, steps - 1);
    }
    cache.set(cacheKey, result);
    return result;
}
const r1 = stones.map(stone => count(stone, 25)).reduce((a, b) => a + b, 0);
console.log('Part 1 ->', r1);
cache = new Map();
const r2 = stones.map(stone => count(stone, 75)).reduce((a, b) => a + b, 0);
console.log('Part 2 ->', r2);Language:JavaScript

--- Day 10: Hoof It ---

Go to page

Part 1 & 2

const code = $('*').textContent.trim().split('\n');
const rows = code.length;
const cols = code[0].length;
const th = [];
const ends = [];
const DIR = [ [0,1], [1,0], [0,-1], [-1,0] ];
const grid = code.map(line => line.split('').map(Number));
grid.forEach((row, y) => {
    row.forEach((char, x) => {
        if (char === 0) th.push([x, y]);
        if (char === 9) ends.push([x, y]);
    });
});

function findPaths(sx, sy, ex, ey) {
    const start = {x:sx, y:sy, v:0};
    const q = [start];
    const visited = {};
    visited[`${start.x}-${start.y}`] = 1;
    while (q.length > 0) {
        const el = q.shift();
        if (el.x === ex && el.y === ey) {
            return visited[`${el.x}-${el.y}`];
        }
        for (let [x,y] of DIR) {
            const newX = el.x + x;
            const newY = el.y + y;
            if (newY >= 0 && newY < rows && newX >= 0 && newX < cols) {
                if (grid[newY][newX] === el.v+1) {
                    if (!visited[`${newX}-${newY}`]) {
                q.push({x:newX, y:newY, v:el.v+1});
                visited[`${newX}-${newY}`] = visited[`${el.x}-${el.y}`];
            } else {
                visited[`${newX}-${newY}`] += visited[`${el.x}-${el.y}`];
            }
                }
            }
        }
    }
    return 0;
}

let r1 = 0, r2 = 0;
th.forEach(([hx, hy]) => {
    ends.forEach(([ex, ey]) => {
        const paths = findPaths(hx, hy, ex, ey);
        r1 += paths > 0 ? 1 : 0;
        r2 += paths;
    });
});

console.log('Part 1 ->', r1);
console.log('Part 2 ->', r2);Language:JavaScript

--- Day 9: Disk Fragmenter ---

Go to page

Part 1 & 2

const code = $('*').textContent.trim();
const files = {};
let blocks = [];
let free = [];
let fid = 0;
let pos = 0;
for (let i = 0; i < code.length; i++) {
    const n = parseInt(code[i]);
    if (i%2 === 0) {
        blocks.push(...Array(n).fill(fid));
        files[fid] = [pos, n];
        fid++;
    } else {
        blocks.push(...Array(n).fill('.'));
        if (n !== 0) {
            free.push([pos, n]);
        }
    }
    pos+=n;
}
let counter = blocks.length-1;
let i = blocks.findIndex(block => block === '.');
while (i < counter) {
    if (blocks[i] === '.') {
        while (blocks[counter] === '.') {
            counter--;
        }
        blocks[i] = blocks[counter];
        blocks[counter] = '.';
    }
    i++;
}
let r1 = blocks.reduce((sum, block, pos) => {
        if (block !== '.') {
            return sum + (pos * parseInt(block));
        }
        return sum;
    }, 0);
console.log('Part 1 ->', r1);

while (fid > 0) {
    fid--;
    const [pos, size] = files[fid];
    for (let i = 0; i < free.length; i++) {
        const [start, length] = free[i];
        if (start >= pos) {
            free = free.slice(0, i);
            break;
        }
        if (size <= length) {
            files[fid] = [start, size];
            if (size === length) {
                free.splice(i, 1);
            } else {
                free[i] = [start + size, length - size];
            }
            break;
        }
    }

}
let total = 0;
for (const [fid, [pos, size]] of Object.entries(files)) {
    for (let x = pos; x < pos + size; x++) {
        total += fid * x;
    }
}
console.log('Part 2 ->',total);Language:JavaScript

--- Day 8: Resonant Collinearity ---

Go to page

Part 1 & 2

ℹ️ I learnt how to solve this day challenge with this great video.

const code = $('*').textContent.trim().split('\n');
const rows = code.length;
const cols = code[0].length;
const antennas = {};
code.forEach((line, y)=>{
    [...line].forEach((char, x)=>{
    if (char !== '.') {
        if (antennas[char] === undefined) {
        antennas[char] = [];
    }
    antennas[char].push([x,y]);
    }
    });
});
let antinodes = new Set();
Object.values(antennas).forEach(arr => {
    for (let i = 0; i < arr.length; i++) {
    for (let j=i+1; j < arr.length; j++) {
        const [x1, y1] = arr[i];
        const [x2, y2] = arr[j];
        antinodes.add([2*x1-x2, 2*y1-y2]);
        antinodes.add([2*x2-x1, 2*y2-y1]);
    }
    }
});
const r1 = new Set();
antinodes.forEach(([x,y]) => {
    if (x >= 0 && x < cols && y >= 0 && y < rows) {
        r1.add(`${x}-${y}`);
    }
});
console.log('Part 1 ->',r1.size);

antinodes = new Set();
Object.values(antennas).forEach(arr => {
    for (let i = 0; i < arr.length; i++) {
    for (let j = 0; j < arr.length; j++) {
        if (i === j) continue;
        const [x1, y1] = arr[i];
        const [x2, y2] = arr[j];
        const dx = x2 - x1;
        const dy = y2 - y1;
        let x = x1;
        let y = y1;
        while (0 <= y && y < rows && 0 <= x && x < cols) {
            antinodes.add([x,y]);
            x += dx;
            y += dy;
        }
    }
    }
});
const r2 = new Set();
antinodes.forEach(([x,y]) => {
    if (x >= 0 && x < cols && y >= 0 && y < rows) {
        r2.add(`${x}-${y}`);
    }
});
console.log('Part 2 ->',r2.size);Language:JavaScript

--- Day 7: Bridge Repair ---

Go to page

Part 1

const code = $('*').textContent.trim().split('\n');
let sum = 0;

code.forEach(line => {
    const [test, equation] = line.split(': ');
    const combinations = generateExpressions(equation.split(' ').map(Number));
    for (let comb of combinations) {
        const r = evaluate(comb);
        if (r === parseInt(test)) {
            sum += r;
            break;
        }
    }
});

function generateExpressions(numbers) {
    const operators = ["+", "*"];
    const results = [];
    function helper(currentExpression, index) {
        if (index === numbers.length) {
            results.push(currentExpression.join(' '));
            return;
        }
        for (const operator of operators) {
            const nextExpression = [...currentExpression, operator, numbers[index]];
            helper(nextExpression, index + 1);
        }
    }
    helper([numbers[0]], 1);
    return results;
}

function evaluate(expression) {
    const tokens = expression.split(' ');
    let result = parseFloat(tokens[0]);
    for (let i = 1; i < tokens.length; i += 2) {
        const operator = tokens[i];
        const nextNumber = parseFloat(tokens[i + 1]);
        if (operator === '+') {
            result += nextNumber;
        } else if (operator === '*') {
            result *= nextNumber;
        }
    }
    return result;
}

console.log('Part 1 ->', sum);Language:JavaScript

Part 2

⚠️ Currently take around 1-2 minutes to drop a result

const code = $('*').textContent.trim().split('\n');
let sum = 0;

code.forEach(line => {
    const [test, equation] = line.split(': ');
    const combinations = generateExpressions(equation.split(' ').map(Number));
    for (let comb of combinations) {
        const r = evaluate(comb);
        if (r === parseInt(test)) {
            sum += r;
            break;
        }
    }
});

function generateExpressions(numbers) {
    const operators = ["+", "*", "||"];
    const results = [];
    function helper(currentExpression, index) {
        if (index === numbers.length) {
            results.push(currentExpression.join(' '));
            return;
        }
        for (const operator of operators) {
            const nextExpression = [...currentExpression, operator, numbers[index]];
            helper(nextExpression, index + 1);
        }
    }
    helper([numbers[0]], 1);
    return results;
}

function evaluate(expression) {
    const tokens = expression.split(' ');
    let result = parseFloat(tokens[0]);
    for (let i = 1; i < tokens.length; i += 2) {
        const operator = tokens[i];
        const nextNumber = parseFloat(tokens[i + 1]);
        if (operator === '+') {
            result += nextNumber;
        } else if (operator === '*') {
            result *= nextNumber;
        }
        else if (operator === '||') {
            result += nextNumber.toString();
            result = parseInt(result);
        }
    }
    return result;
}

console.log('Part 2 ->', sum);Language:JavaScript

--- Day 6: Guard Gallivant ---

Go to page

Part 1 & 2

⚠️Part 2 currently take around 20 seconds to drop a result

const code = $('*').textContent.trim().split('\n');
const rows = code.length;
const cols = code[0].length;
const dirs = [[0,-1], [1,0], [0,1], [-1,0]];
let pos = {x:0, y:0, dir: 0};
let startPos = {x:0, y:0, dir:0};
let positions = new Set();
let loopPositions = new Set();
outer: for (let i = 0; i < rows; i++) {
    for (let j = 0; j < cols; j++) {
        if ("^>v<".includes(code[i][j])) {
            startPos = { x: j, y: i, dir: "^>v<".indexOf(code[i][j]) };
            pos = { x: j, y: i, dir: "^>v<".indexOf(code[i][j]) };
            break outer;
        }
    }
}
positions.add(`${pos.x}-${pos.y}`);

while (true) {
    const newx = pos.x + dirs[pos.dir][0];
    const newy = pos.y + dirs[pos.dir][1];
    if (newx < 0 || newx >= cols || newy < 0 || newy >= rows) {
        break;
    }
    if (code[newy][newx] === '#') {
        pos.dir = (pos.dir + 1)%4;
    } else {
        positions.add(`${newx}-${newy}`);
        pos.x = newx;
        pos.y = newy;
    }
}
console.log('Part 1 ->', positions.size);

function simulation(map, start) {
    let pos = {...start};
    let visited = new Set();

    while(true) {
        const state = `${pos.x}-${pos.y}-${pos.dir}`;
        if (visited.has(state)) {
            return true;
        }
        visited.add(state);
        const newx = pos.x + dirs[pos.dir][0];
        const newy = pos.y + dirs[pos.dir][1];
        if (newx < 0 || newx >= map[0].length || newy < 0 || newy >= map.length) {
            return false;
        }
        if (map[newy][newx] === '#') {
            pos.dir = (pos.dir + 1)%4;
        } else {
            pos.x = newx;
            pos.y = newy;
        }
    }
    return false;
    
}

for (let i = 0; i < rows; i++) {
    for (let j = 0; j < cols; j++) {
        if (code[i][j] === '.') {
            const modMap = code.map(row => row.split(''));
            modMap[i][j] = "#";

            if (simulation(modMap, startPos)) {
                loopPositions.add(`${j}-${i}`);
            }
        }
    }
    
}
console.log('Part 2 ->', loopPositions.size);Language:JavaScript

--- Day 5: Print Queue ---

Go to page

Part 1 & 2

let [Rules, Updates] = $('*').textContent.trim().split('\n\n');

const rules = Rules.split('\n').map(rule => {
    const [before, after] = rule.split('|');
    return [parseInt(before), parseInt(after)];
});

const updates = Updates.split('\n').map(update => 
    update.split(',').map(num => parseInt(num))
);

function isValid(rules, update) {

    const rulesToUse = rules.filter(([before, after]) => update.includes(before) && update.includes(after));
    
    for (let [before, after] of rulesToUse) {
        if (update.indexOf(before) > update.indexOf(after)) {
            return false;
        }
    }
    return true;
}

const disorderedUpdates = [];
let sum = 0;
for (let update of updates) {
    if (isValid(rules, update)) { 
        sum += update[Math.floor(update.length / 2)];
    } else {
        disorderedUpdates.push(update);
    }
}
console.log('Part 1 ->', sum);

function buildGraph(numbers, rules) {
    const graph = new Map();
    const inDegree = new Map();
    
    numbers.forEach(n => {
        graph.set(n, []);
        inDegree.set(n, 0);
    });
    
    rules.filter(([b, a]) => numbers.includes(b) && numbers.includes(a))
            .forEach(([b, a]) => {
            graph.get(b).push(a);
            inDegree.set(a, inDegree.get(a) + 1);
            });
    
    return { graph, inDegree };
}

function topologicalSort(numbers, rules) {
    const { graph, inDegree } = buildGraph(numbers, rules);
    const result = [];
    const queue = [];
    
    numbers.forEach(n => {
        if (inDegree.get(n) === 0) queue.push(n);
    });
    
    while (queue.length) {
        const current = queue.shift();
        result.push(current);
        
        for (const next of graph.get(current)) {
            inDegree.set(next, inDegree.get(next) - 1);
            if (inDegree.get(next) === 0) {
                queue.push(next);
            }
        }
    }
    
    return result;
}

let sum2 = 0;
for (let update of disorderedUpdates) {
    const sorted = topologicalSort(update, rules);
    sum2 += sorted[Math.floor(sorted.length / 2)];
}
console.log('Part 2 ->', sum2);Language:JavaScript

--- Day 4: Ceres Search ---

Go to page

Part 1 & 2

const code = $("*").innerText.split('\n').slice(0,-1);
let counter = 0;
let counter2 = 0;
code.forEach(line => {
    searchWord(line);
});
vertical(code);
diagonal(code);
console.log('Part 1 ->',counter);

findXMAS(code);
console.log('Part 2 ->',counter2);

function vertical(grid) {
    const rows = grid.length;
    const cols = grid[0].length;

    // Loop through each column
    for (let col = 0; col < cols; col++) {
        let columnString = '';
        for (let row = 0; row < rows; row++) {
            columnString += grid[row][col];
        }

        for (let i = 0; i <= columnString.length - 4; i++) {
            const word = columnString.slice(i, i + 4);
            if (word === "XMAS" || word === "SAMX") {
                counter++;
            }
        }
    }
}

function diagonal(grid) {
    const rows = grid.length;
    const cols = grid[0].length;

    for (let start = 0; start < rows + cols - 1; start++) {
        let diagonalString = "";
        for (let i = 0; i < rows; i++) {
            let j = start - i;
            if (j >= 0 && j < cols) {
                diagonalString += grid[i][j];
            }
        }
        searchWord(diagonalString);
    }

    for (let start = -rows + 1; start < cols; start++) {
        let diagonalString = "";
        for (let i = 0; i < rows; i++) {
            let j = i + start;
            if (j >= 0 && j < cols) {
                diagonalString += grid[i][j];
            }
        }
        searchWord(diagonalString);
    }
}

function searchWord(line) {
    for (let i = 0; i <= line.length - 4; i++) {
        const word = line.slice(i, i + 4);
        if (word === "XMAS" || word === "SAMX") {
            counter++;
        }
    }
}

function findXMAS(grid) {
    const rows = grid.length;
    const cols = grid[0].length;

    for (let i = 1; i < rows - 1; i++) {
        for (let j = 1; j < cols - 1; j++) {
            const topLeft = grid[i - 1][j - 1];
            const topRight = grid[i - 1][j + 1];
            const center = grid[i][j];
            const bottomLeft = grid[i + 1][j - 1];
            const bottomRight = grid[i + 1][j + 1];

            if (
                (topLeft === 'M' && topRight === 'S' && center === 'A' && bottomLeft === 'M' && bottomRight === 'S') ||
                (topLeft === 'S' && topRight === 'M' && center === 'A' && bottomLeft === 'S' && bottomRight === 'M') ||
                (topLeft === 'S' && topRight === 'S' && center === 'A' && bottomLeft === 'M' && bottomRight === 'M') ||
                (topLeft === 'M' && topRight === 'M' && center === 'A' && bottomLeft === 'S' && bottomRight === 'S')
            ) {
                counter2++;
            }
        }
    }
}Language:JavaScript

--- Day 3: Mull It Over ---

Go to page

Part 1 & 2

const code = $('*').textContent.split('\n').slice(0,-1);
let counter = 0;
let counter2 = 0

code.forEach(line => {
    const report = line.split(' ').map(Number);
    counter += checkReport(report) ? 1 : 0;
    counter2 += safeReport(report) ? 1 : 0;
});

console.log('Part 1 ->', counter);
console.log('Part 2 ->', counter2);

function checkReport(levels) {
    if (levels.length < 2) return true;
    const firstDiff = levels[1] - levels[0];
    const dir = firstDiff > 0;
    if (Math.abs(firstDiff) > 3 || Math.abs(firstDiff) === 0) return false;

    for (let i = 1; i < levels.length; i++) {
        const diff = levels[i] - levels[i-1];
        if (dir && diff <= 0) return false;
        if (!dir && diff >= 0) return false;
        if (Math.abs(diff) > 3 || Math.abs(diff) === 0) return false;
    }
    return true;
}

function safeReport(levels) {
    if (checkReport(levels)) return true;

    for (let i = 0; i < levels.length; i++) {
        const changed = [...levels.slice(0,i), ...levels.slice(i+1)];
        if (checkReport(changed)) {
            return true;
        }
    }
    return false;
}Language:JavaScript

--- Day 2: Red-Nosed Reports ---

Go to page

Part 1 & 2

const code = $('*').textContent.split('\n').slice(0,-1);
let counter = 0;
let counter2 = 0

code.forEach(line => {
    const report = line.split(' ').map(Number);
    counter += checkReport(report) ? 1 : 0;
    counter2 += safeReport(report) ? 1 : 0;
});

console.log('Part 1 ->', counter);
console.log('Part 2 ->', counter2);

function checkReport(levels) {
    if (levels.length < 2) return true;
    const firstDiff = levels[1] - levels[0];
    const dir = firstDiff > 0;
    if (Math.abs(firstDiff) > 3 || Math.abs(firstDiff) === 0) return false;

    for (let i = 1; i < levels.length; i++) {
        const diff = levels[i] - levels[i-1];
        if (dir && diff <= 0) return false;
        if (!dir && diff >= 0) return false;
        if (Math.abs(diff) > 3 || Math.abs(diff) === 0) return false;
    }
    return true;
}

function safeReport(levels) {
    if (checkReport(levels)) return true;

    for (let i = 0; i < levels.length; i++) {
        const changed = [...levels.slice(0,i), ...levels.slice(i+1)];
        if (checkReport(changed)) {
            return true;
        }
    }
    return false;
}Language:JavaScript

--- Day 1: Historian Hysteria ---

Go to page

Part 1 & 2

console.time("Execution time");
const code = $('*').textContent.split('\n').slice(0,-1);
const row1 = [];
const row2 = [];
code.forEach(line => {
const [n1, n2] = line.split('   ').map(Number);
row1.push(n1);
row2.push(n2);
});
row1.sort((a,b)=>a-b);
row2.sort((a,b)=>a-b);
let cache = new Map();
let result1 = row1.reduce((sum, value, i) => sum + Math.abs(value - row2[i]), 0);
let result2 = 0;

const rowCount = new Map();
row2.forEach(value => {
rowCount.set(value, (rowCount.get(value) || 0) + 1);
});
row1.forEach(value => {
const count = rowCount.get(value) || 0;
result2 += value * count;
});
console.log('Part 1 ->', result1);
console.log('Part 2 ->', result2);
console.timeEnd("Execution time");Language:JavaScript