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

Advent of Code 2023 - Solutions

Lessons from Advent of Code 2023

Solutions 2022

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 1: Trebuchet?! ---

Go to page

Part 1 & 2 -- What is the sum of all of the calibration values?

const data = $0.innerText.split('\n').slice(0, -1);
let sum = 0; 
let str; 
let nums = ""; 
let calibration = [];

data.forEach((item) => { 
    str = item.replace(/\D/g, ''); 
    str = str[0] + str[str.length-1]; 
    sum += parseInt(str);
});
console.log(`The sum of all the calibration values for part 1 is ${sum}`);

// Reset variables 
sum = 0; 
str = "";

data.forEach((item) => { 
    for (let j = 0; j < item.length; j++) { 
    str += item[j]; 
    str = checkNum(str); 
    } 
    str = nums[0] + nums[nums.length-1]; 
    sum += parseInt(str); 
    nums = ""; 
});
console.log(`The sum of all the calibration values for part 2 is ${sum}`);

function checkNum(str) { 
    const numArray = ['one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine'];

    if (!isNaN(str.slice(-1))) {
    nums += str.slice(-1);
    str = "";
    } else {
    numArray.forEach((word, index) => {
    if (str.includes(word)) {
        nums += (index + 1).toString();
        str = str.slice(-1);
    }
    });
    }
    return str;
}Language:JavaScript

--- Day 2: Cube Conundrum ---

Go to page

Part 1 & 2 -- What is the sum of the IDs of possible games? and What is the sum of the power of games?

const data = $0.innerText.trim().split('\n');
const colors = {};
let sumOfIDs = 0;
let sumOfPowers = 0;

function extractNumericValues(str) {
    return str.replace(/\D/g, '');
}

data.forEach((line) => {
    const [keyPart, colorPart] = line.split(':');
    const key = keyPart.trim();
    const colorData = colors[key] || [[], [], []];

    colorPart.split(/[;,]/).forEach((segment) => {
    if (segment.includes('red')) {
        colorData[0].push(extractNumericValues(segment));
    } else if (segment.includes('green')) {
        colorData[1].push(extractNumericValues(segment));
    } else if (segment.includes('blue')) {
        colorData[2].push(extractNumericValues(segment));
    }
    });

    if (!colorData[0].some(value => value > 12) &&
    !colorData[1].some(value => value > 13) &&
    !colorData[2].some(value => value > 14)) {
        sumOfIDs += parseInt(key.replace(/\D/g, ''));
    }

    const power = Math.max(...colorData[0]) * Math.max(...colorData[1]) * Math.max(...colorData[2]);
    sumOfPowers += power;

    colors[key] = colorData;
});

console.log(`The sum of IDs is ${sumOfIDs}`);
console.log(`The sum of powers is ${sumOfPowers}`);Language:JavaScript

--- Day 3: Gear Ratios ---

Go to page

Part 1 & 2

const data = $0.innerText.split('\n').slice(0, -1);
let nums = 0;
let num = "";
let numGear = [];
let sign = false;
let gear = false;
let gearPos = [];
let gears = [];

for (let i = 0; i < data.length; i++) {
    for (let j = 0; j < data[i].length; j++) {
        if (data[i][j] === '*') {
            gears.push([i,j]);
        }
    }
}

function checkGear(r,c) {
    if(data[r][c] === '*') { 
        gear = true;
        gearPos.push([r,c]);
    }
}

for (let i = 0; i < data.length; i++) {
    num = "";
    for (let j = 0; j < data[i].length; j++) {
        if (!isNaN(data[i][j])) {
            num+= data[i][j];
            if(data[i-1] != undefined && data[i-1][j-1] != undefined && isNaN(data[i-1][j-1]) && data[i-1][j-1] != '.') {
                sign = true;
                checkGear(i-1,j-1);
            }
            if (data[i-1] != undefined && isNaN(data[i-1][j]) && data[i-1][j] != '.') {
                sign = true;
                checkGear(i-1,j);
            }
            if (data[i-1] != undefined && data[i-1][j+1] != undefined && isNaN(data[i-1][j+1]) && data[i-1][j+1] != '.') {
                sign = true;
                checkGear(i-1,j+1);
            }
            if(data[i][j-1] != undefined && isNaN(data[i][j-1]) && data[i][j-1] != '.') {
                sign = true;
                checkGear(i,j-1);
            }
            if (data[i][j+1] != undefined && isNaN(data[i][j+1]) && data[i][j+1] != '.') {
                sign = true;
                checkGear(i,j+1);
            }
            if (data[i+1] != undefined && data[i+1][j-1] != undefined && isNaN(data[i+1][j-1]) && data[i+1][j-1] != '.') {
                sign = true;
                checkGear(i+1,j-1);
            }
            if (data[i+1] != undefined && isNaN(data[i+1][j]) && data[i+1][j] != '.') {
                sign = true;
                checkGear(i+1,j);
            }
            if (data[i+1] != undefined && data[i+1][j+1] != undefined && isNaN(data[i+1][j+1]) && data[i+1][j+1] != '.') {
                sign = true;
                checkGear(i+1,j+1);
            }
        }
        if(data[i][j+1] != undefined && data[i][j+1] === '.' && !sign) {
            num = "";
        }
        if(sign && isNaN(data[i][j+1])) {
            if (gear) {
                numGear.push(num + " " + gearPos[gearPos.length-1]);
                gear = false;
            }
            sign = false;
            nums += parseInt(num);
            num = "";
        }
    }
}

console.log(`Part 1 -> ${nums}`);

let sum = 0;
for (let i = 0; i < gears.length; i++) {
let m = 0;
let filteredElements = numGear.filter(el => el.split(' ')[1] === `${gears[i]}`);
if (filteredElements.length === 2) {
//console.log(filteredElements);
m = parseInt(filteredElements[0].split(' ')[0]) * parseInt(filteredElements[1].split(' ')[0]);
}
sum += m; 
}

console.log(`Part 2 -> ${sum}`);Language:JavaScript

--- Day 4: Scratchcards ---

Go to page

Part 1 & 2

const data = $0.innerText.split('\n').slice(0, -1);
const cards = {};
let matchNumbers = [];
let points = 0;

function findCommonNumbers(arr1, arr2) {
    const set1 = new Set(arr1.map(Number).filter(num => !isNaN(num)));
    const commonNumbers = [...new Set(arr2.map(Number).filter(num => !isNaN(num)))].filter(num => set1.has(num));
    return commonNumbers;
}

data.forEach((line) => {
    let [winning, card] = line.split('|');
    winning = winning.slice(winning.indexOf(':') + 1).trim().split(' ').filter(element => element !== '');
    card = card.trim().split(' ').filter(element => element !== '');
    matchNumbers.push(findCommonNumbers(winning, card).map(Number).filter(num => !isNaN(num)));
    cards[line.split(':')[0].replace(/\s+/g, ' ')] = 1;
});

for (let n = 0; n < matchNumbers.length; n++) {
    if (matchNumbers[n].length > 0) {
        points += Math.pow(2, matchNumbers[n].length-1);
        matchNumbers[n].forEach((_, i) => {
            cards[`Card ${n+1+(i+1)}`] += cards[`Card ${n+1}`];
        });
    }
}
console.log(`Part 1 -> ${points}`);

const total = Object.values(cards).reduce((accumulator, value) => {
    return accumulator + value;
    }, 0);
console.log(`Part 2 -> ${total}`);Language:JavaScript

--- Day 5: If You Give A Seed A Fertilizer ---

Go to page

Part 1 & 2

const data = $0.innerText.split('\n');
let seeds = [];
let map = {};
let counter = 0;
let arrs = ['soil', 'fertilizer', 'water', 'light', 'temperature', 'humidity', 'location'];
data.forEach((line) => {
    if (line.includes('seeds')) {
        seeds = line.split(':')[1].split(' ').slice(1).map(Number);
    } else if (line.includes('-to-')) {
        counter++;
    } else {
        if (line.trim() !== '') {
            const key = arrs[counter - 1];
            map[key] = map[key] || [];
            map[key].push(line.split(' ').map(Number));
        }
    }
});
function convert(line, seed, type) {
    seed = parseInt(seed);
    let dest, source, range = 0;
    const arr = map[type];

    if (arr) {
        dest = parseInt(arr[line][0]);
        source = parseInt(arr[line][1]);
        range = parseInt(arr[line][2]);
    } else {
        console.error(`Type '${type}' not found in the map.`);
        return seed;
    }

    if (seed === source) {
        return dest;
    } else if (seed > source && seed < source + range) {
        let result = seed - source + dest;
        return result;
    } else {
        if (line + 1 >= arr.length) {
        return seed;
    } else {
        return convert(line + 1, seed, type);
        }
    }
}
function convertInvert(line, seed, type) {
    seed = parseInt(seed);
    let dest, source, range = 0;
    const arr = map[type];

    if (arr) {
        dest = parseInt(arr[line][1]);
        source = parseInt(arr[line][0]);
        range = parseInt(arr[line][2]);
    } else {
        console.error(`Type '${type}' not found in the map.`);
        return seed;
    }

    if (seed === source) {
        return dest;
    } else if (seed > source && seed < source + range) {
        let result = seed - source + dest;
        return result;
    } else {
        if (line + 1 >= arr.length) {
        return seed;
    } else {
        return convertInvert(line + 1, seed, type);
    }
    }
}
let minVal = Number.MAX_SAFE_INTEGER;
seeds.forEach((seed) => {
    arrs.forEach((arr) => {
        seed = convert(0, seed, arr);
    });
    seed < minVal ? minVal = seed : null;
});
console.log(`Part 1 -> ${minVal}`);
let minSeed = Number.MAX_SAFE_INTEGER;
let maxSeed = 0;
let seed;
for (let i = 0; i < seeds.length; i += 2) {
    seed = seeds[i];
    let range = seeds[i + 1];
    seed < minSeed ? minSeed = seed : null;
    seed+range > maxSeed ? maxSeed = seed+range : null;
}
for (let j = 1; j < maxSeed+minSeed; j++) {
    seed = j;
    arrs.forEach((arr, i) => {
        seed = convertInvert(0, seed, arrs[arrs.length-1-i]);
    });
    if (seed >= minSeed && seed < maxSeed) {
        console.log(`Part 2 -> ${j}`);
        break;
    }
}Language:JavaScript

--- Day 6: Wait For It ---

Go to page

Part 1 & 2

const data = $0.innerText.split('\n');
            const time = data[0].match(/\d+/g).map(Number);
            const distance = data[1].match(/\d+/g).map(Number);
            let counter = 0;
            let beatRecord = [];

            function calcDist(hold, time) {
            const speed = hold;
            const remainingTime =  time - hold;
            return speed * remainingTime;
            }

            time.forEach((t, i) => {
            for (let j = 0; j < t; j++) {
            const dist = calcDist(j,t)
            dist > distance[i] ? counter++ : null;
            }
            beatRecord.push(counter);
            counter = 0;
            });
            const result = beatRecord.reduce((accumulator, currentValue) => accumulator * currentValue, 1);
            console.log(`Part 1 -> ${result}`);

            const concatenatedTime = time.join('');
            const concatenatedDistance = distance.join('');
            counter = 0;
            for (let j = 0; j < concatenatedTime; j++) {
            const dist = calcDist(j,concatenatedTime);
            dist > concatenatedDistance ? counter++ : null;
            }
            console.log(`Part 2 -> ${counter}`);Language:JavaScript

--- Day 7: Camel Cards ---

Go to page

Part 1

const data = $0.innerText.split('\n').filter(line => line !== '');
            let hands = [];
            let orderedHands = [];
            let rankedHands = [];

            function getType(hand) {
            const charCounts = {};
            for (const char of hand) {
            if (!charCounts[char]) {
            charCounts[char] = 1;
            } else {
            charCounts[char]++;
            }
            }
            const values = Object.values(charCounts);
            if (values.length === 1) {    
            orderedHands.push([hand,6]);
            }
            else if (values.length === 2) {
            if (values.includes(4)) { 
            orderedHands.push([hand,5]);
            } else {
            orderedHands.push([hand,4]);
            }
            }
            else if (values.length === 3) {
            if (values.includes(3)) {
            orderedHands.push([hand,3]);
            } else {
            orderedHands.push([hand,2]);
            }
            }
            else if (values.length === 4) {
            orderedHands.push([hand,1]);
            }
            else if (values.length === 5) {
            orderedHands.push([hand,0]);
            }
            }
            function compareCharacters(char1, char2) {
            const charOrder = ['A', 'K', 'Q', 'J', 'T', '9', '8', '7', '6', '5', '4', '3', '2'];
            const index1 = charOrder.indexOf(char1);
            const index2 = charOrder.indexOf(char2);
            if (index1 > index2) {
            return -1;
            } else if (index1 < index2) {
            return 1;
            } else {
            return 0;
            }
            }
            function compareArraysInGroup(arraysInGroup) {
            return arraysInGroup.sort((a, b) => {
            const charsA = a.split('');
            const charsB = b.split('');
            for (let i = 0; i < Math.min(charsA.length, charsB.length); i++) {
            const charComparison = compareCharacters(charsA[i], charsB[i]);
            if (charComparison !== 0) {
            return charComparison;
            }
            }
            return charsA.length - charsB.length;
            });
            }

            function calculateSum(hand, counter) {
            let value = 0;
            hands.forEach((h) => {
            if (h.includes(hand)) {
            value = h[1] * counter;
            }
            });
            return value;
            }

            data.forEach((line) => { hands.push(line.split(' ')); });
            hands.forEach((hand) => { getType(hand[0]); });
            orderedHands.sort((a, b) => a[1] - b[1] || compareArraysInGroup([a[0], b[0]]));

            const groupedArrays = {};
            orderedHands.forEach(([value, number]) => {
            groupedArrays[number] = groupedArrays[number] || [];
            groupedArrays[number].push(value);
            });

            Object.keys(groupedArrays).forEach((groupNumber) => {
            const arraysInGroup = groupedArrays[groupNumber];
            const sortedArrays = compareArraysInGroup(arraysInGroup);
            rankedHands.push(...sortedArrays);
            });

            let sum = 0;
            rankedHands.forEach((hand, index) => { sum += calculateSum(hand, index + 1); });
            console.log(`Part 1 -> ${sum}`);Language:JavaScript

--- Day 8: Haunted Wasteland ---

Go to page

Part 1 & 2

const data = $0.innerText.split('\n').slice(0,-1);
            let instructions = "";
            let nodes = {};

            data.forEach((line, i)=>{
            i === 0 ? instructions = line : null;
            i > 1 ? nodes[line.slice(0,3)] = line.slice(7,-1).split(', ') : null;
            });

            let counter = -1;
            let steps = 0;
            let node = 'AAA';

            while (node != 'ZZZ') {
            if (counter >= instructions.length-1) {
            counter = 0;
            } else {
            counter++;
            }
            if (instructions[counter] === 'L') {
            node = nodes[node][0];
            }
            else if (instructions[counter] === 'R') {
            node = nodes[node][1];
            }
            steps++;
            }
            console.log(`Part 1 -> ${steps}`);

            function findLCM(numbers) {
            function findGCD(a, b) {
            return b === 0 ? a : findGCD(b, a % b);
            }
            function findLCMOfTwo(a, b) {
            return (a * b) / findGCD(a, b);
            }
            let lcm = numbers[0];
            for (let i = 1; i < numbers.length; i++) {
            lcm = findLCMOfTwo(lcm, numbers[i]);
            }

            return lcm;
            }

            const nodesEndingWithA = Object.keys(nodes).filter(key => key.endsWith('A'));
            let nodeSteps = [];

            nodesEndingWithA.forEach((node)=>{
            counter = -1;
            steps = 0;
            while (node[node.length-1] != 'Z') {
            if (counter >= instructions.length-1) {
            counter = 0;
            } else {
            counter++;
            }
            if (instructions[counter] === 'L') {
            node = nodes[node][0];
            }
            else if (instructions[counter] === 'R') {
            node = nodes[node][1];
            }
            steps++;
            }
            nodeSteps.push(steps);
            });

            const lcm = findLCM(nodeSteps);
            console.log(`Part 2 -> ${lcm}`);Language:JavaScript

--- Day 9: Mirage Maintenance ---

Go to page

Part 1 & 2

const data = $0.innerText.split('\n').slice(0,-1);
            let history = [];
            data.forEach((line,i)=>{
            history.push([line.split(' ')]);
            getNewHistory(history[i], 0);
            });
            function getNewHistory(his,index) {
            const differences = [];
            for (let i = 1; i < his[index].length; i++) {
            differences.push(his[index][i] - his[index][i - 1]);
            }
            his.push(differences);
            const areAllZeros = differences.every(value => value === 0);
            areAllZeros ? null : getNewHistory(his, index+1);
            }
            function extrapolate(his, index) {
            const areAllZeros = his[index+1].every(value => value === 0);
            areAllZeros ? his[index+1].push(0) : null;
            const newValue = parseInt(his[index][his[index].length-1]) + his[index+1][his[index+1].length-1];
            his[index].push(newValue);
            index > 0 ? extrapolate(his, index-1) : null;
            }
            let sum = 0;
            history.forEach((h,i)=>{
            extrapolate(h, h.length-2);
            sum += h[0][h[0].length-1];
            });
            console.log(`Part 1 -> ${sum}`);

            function backExtrapolate(his, index) {
            const areAllZeros = his[index+1].every(value => value === 0);
            areAllZeros ? his[index+1].unshift(0) : null;
            const newValue = parseInt(his[index][0]) - his[index+1][0];
            his[index].unshift(newValue);
            index > 0 ? backExtrapolate(his, index-1) : null;
            }
            sum = 0;
            history.forEach((h,i)=>{
            backExtrapolate(h, h.length-2);
            sum += h[0][0];
            });
            console.log(`Part 2 -> ${sum}`);Language:JavaScript

--- Day 10: Pipe Maze ---

Go to page

Part 1 & 2

const data = $0.innerText.split('\n').slice(0,-1);
            let map = [];
            let start = {value: 'S', x:0, y:0, g:0};
            let queue = [];
            let visited = [];
            let contains = false; 

            data.forEach((line,i)=>{
            for (let j = 0; j < line.length; j++) {
            line[j] === 'S' ? start = {value: 'S', x:j, y:i, g:0} : null;
            }
            map.push(line);
            });

            queue.push(start);
            visited.push(start);

            while (queue.length > 0) {
            let pipe = queue.shift();
            let neighbors = getNeighbors(pipe);
            for (let i = 0; i < neighbors.length; i++) {
            let neighbor = neighbors[i];
            let contains = visited.some( (v) => v.x === neighbor.x && v.y === neighbor.y );
            if (!contains) {
            visited.push(neighbors[i]);
            queue.push(neighbors[i]);
            }
            }
            }

            const result = getObjectWithHighestG(visited);
            console.log(`Part 1 -> ${result.g}`);

            function getNeighbors(tile) {
            let ns = [];
            if (tile.value === '|') {
            ns.push({value: map[tile.y-1][tile.x], x: tile.x, y: tile.y-1, g:tile.g+1});
            ns.push({value: map[tile.y+1][tile.x], x: tile.x, y: tile.y+1, g: tile.g+1});
            }
            else if (tile.value === '-') {
            ns.push({value: map[tile.y][tile.x-1], x: tile.x-1, y: tile.y, g: tile.g+1});
            ns.push({value: map[tile.y][tile.x+1], x: tile.x+1, y: tile.y, g: tile.g+1});
            }
            else if (tile.value === 'L') {
            ns.push({value: map[tile.y-1][tile.x], x: tile.x, y: tile.y-1, g:tile.g+1});
            ns.push({value: map[tile.y][tile.x+1], x: tile.x+1, y: tile.y, g: tile.g+1});
            }
            else if (tile.value === 'J') {
            ns.push({value: map[tile.y-1][tile.x], x: tile.x, y: tile.y-1, g:tile.g+1});
            ns.push({value: map[tile.y][tile.x-1], x: tile.x-1, y: tile.y, g: tile.g+1});
            }
            else if (tile.value === '7') {
            ns.push({value: map[tile.y][tile.x-1], x: tile.x-1, y: tile.y, g: tile.g+1});
            ns.push({value: map[tile.y+1][tile.x], x: tile.x, y: tile.y+1, g: tile.g+1});
            }
            else if (tile.value === 'F') {
            ns.push({value: map[tile.y][tile.x+1], x: tile.x+1, y: tile.y, g: tile.g+1});
            ns.push({value: map[tile.y+1][tile.x], x: tile.x, y: tile.y+1, g: tile.g+1});
            }
            else if (tile.value === 'S') {
            if (map[tile.y-1][tile.x] === '|' || map[tile.y-1][tile.x] === '7' || map[tile.y-1][tile.x] === 'F') {
            ns.push({value: map[tile.y-1][tile.x], x: tile.x, y: tile.y-1, g:tile.g+1});
            }
            if (map[tile.y+1][tile.x] === '|' || map[tile.y+1][tile.x] === 'L' || map[tile.y+1][tile.x] === 'J') {
            ns.push({value: map[tile.y+1][tile.x], x: tile.x, y: tile.y+1, g: tile.g+1});
            }
            if (map[tile.y][tile.x-1] === '-' || map[tile.y][tile.x-1] === 'L' || map[tile.y][tile.x-1] === 'F') {
            ns.push({value: map[tile.y][tile.x-1], x: tile.x-1, y: tile.y, g: tile.g+1});
            }
            if (map[tile.y][tile.x+1] === '-' || map[tile.y][tile.x+1] === 'J' || map[tile.y][tile.x+1] === '7') {
            ns.push({value: map[tile.y][tile.x+1], x: tile.x+1, y: tile.y, g: tile.g+1});
            }
            }
            return ns;
            }
            function getObjectWithHighestG(array) {
            let maxObject = array[0];
            for (let i = 1; i < array.length; i++) {
            if (array[i].g > maxObject.g) {
            maxObject = array[i];
            }
            }
            return maxObject;
            }

            let q = [];
            let w = [];
            let a = [];
            visited.forEach((tile,i)=>{
            i % 2 === 0 ? q.push(tile) : w.unshift(tile);
            })
            a = q.concat(w);
            boundaries = [];
            a.forEach((e)=>{
            if (e.value === 'S' || e.value === 'L' || e.value === 'J' || e.value === '7' || e.value === 'F') {
            boundaries.push(e);
            }
            });
            function getArea(x, y) {
            let area = 0;
            let sum = 0;
            for (let i = 0; i < x.length-1; i++) {
            sum += x[i] * y[i+1] - x[i+1] * y[i]
            }
            area = sum / 2;
            return Math.abs(area);
            }
            let x = [];
            let y = [];

            boundaries.forEach((b)=>{
            x.push(b.x);
            y.push(b.y);
            });
            x.push(boundaries[0].x);
            y.push(boundaries[0].y);

            area = getArea(x,y); 
            console.log(`Part 2 -> ${area + 1 - visited.length / 2}`);Language:JavaScript

--- Day 11: Cosmic Expansion ---

Go to page

Part 1 & 2

const data = $0.innerText.split('\n').slice(0,-1);
            const universe = data.map(row => row.split(''));
            const expansionFactor = 1; // NOTE: change this value to 999999 (one million - 1) to get the answer to the part 2
            let galaxies = [];

            universe.forEach((row, i) => {
            row.forEach((cell, index) => {
            if (cell === '#') {
            galaxies.push([index, i]);
            }
            });
            });

            const emptyRows = universe.reduce((emptyRows, row, index) => {
            if (!row.includes('#')) { emptyRows.push(index); }
            return emptyRows;
            }, []);

            const transposedUniverse = universe[0].map((col, i) => universe.map(row => row[i]));

            const emptyCols = transposedUniverse.reduce((emptyCols, col, index) => {
            if (!col.includes('#')) { emptyCols.push(index); }
            return emptyCols;
            }, []);

            galaxies.forEach((galaxy) => {
            const c = emptyCols.filter(number => number < galaxy[0]);
            galaxy[0] = galaxy[0] + (c.length * expansionFactor);
            const r = emptyRows.filter(number => number < galaxy[1]);
            galaxy[1] = galaxy[1] + (r.length * expansionFactor);
            });

            let sum = 0;
            for (let i = 0; i < galaxies.length; i++) {
            for (let j = i + 1; j < galaxies.length; j++) {
            const d = Math.abs(galaxies[i][0] - galaxies[j][0]) + Math.abs(galaxies[i][1] - galaxies[j][1]);
            sum += d;
            }
            }
            console.log(`Part ${expansionFactor == 1 ? '1' : '2'} --> ${sum}`);Language:JavaScript

--- Day 12: Hot Springs ---

Go to page

Part 1 & 2

const data = $0.innerText.trim().split("\n");
const reps = 1; // IMPORTANT: change this constant to 5 to get the answer for part 2
let result = 0;

for (let line of data) {
    
    let mapRepresentation = line.split(" ")[0];
    let numericValues = [...line.matchAll(/\d+/g)].map(x => +(x[0]));
    let extendedNumericValues = [0];
    let extendedMap = "";
    
    for (let i = 0; i < reps; i++) {
        extendedMap = extendedMap + (mapRepresentation + "?");
        extendedNumericValues = extendedNumericValues.concat(numericValues);
    }
    
    let pathCounts = [];
    for (let i = 0; i < extendedMap.length; i++) {
        pathCounts[i] = [];
    }
    
    let countPaths = (mapIndex, numericIndex) => {
        if (mapIndex === -1 && numericIndex === 0) { return 1; }
        
        if (pathCounts[mapIndex]) { return pathCounts[mapIndex][numericIndex] ?? 0; }
        
        return 0;
    };
    
    for (let numericIndex = 0; numericIndex < extendedNumericValues.length; numericIndex++) {
        
        for (let mapIndex = 0; mapIndex < extendedMap.length; mapIndex++) {
            let currentCount = 0;
    
            if (extendedMap[mapIndex] !== '#') {
                currentCount += countPaths(mapIndex - 1, numericIndex);
            }
    
            if (numericIndex > 0) {
                let doCount = true;
        
                for (let k = 1; k <= extendedNumericValues[numericIndex]; k++) {
                    if (extendedMap[mapIndex - k] === '.') {
                        doCount = false;
                    }
                    }
        
                if (extendedMap[mapIndex] === '#' || !doCount) {
                    doCount = false;
                }
        
                if (doCount) {
                    currentCount += countPaths(mapIndex - extendedNumericValues[numericIndex] - 1, numericIndex - 1);
                }
            }

            pathCounts[mapIndex][numericIndex] = currentCount;
        }
    }

    result += pathCounts[extendedMap.length - 1][extendedNumericValues.length - 1];
}

console.log(`Part ${reps > 1 ? 2 : 1} ->`, result);Language:JavaScript

--- Day 13: Point of Incidence ---

Go to page

Part 1 & 2

const data = $0.innerText.split('\n').slice(0,-1);
            let mirrors = [];
            let m = [];
            let smudge = false;
            data.forEach((line, i) => {
            if (line != '') {
            m.push(line);
            i === data.length-1 ? mirrors.push(m) : null;
            } else {
            mirrors.push(m);
            m = [];
            }
            });

            function transpose(matrix) {
            const numRows = matrix.length;
            const numCols = matrix[0].length;
            const transposedMatrix = [];
            for (let col = 0; col < numCols; col++) {
            const currentColumn = [];
            for (let row = 0; row < numRows; row++) {
            currentColumn.push(matrix[row][col]);
            }
            transposedMatrix.push(currentColumn.join(''));
            }
            return transposedMatrix;
            }

            function findReflection(mirror, i, j, counter) {
            if (mirror[i] === mirror[j]) {
            if (i === 0) {
            if (j === i+1) {
                return 1;
            } else {
                return counter+1;
            }
            } else if (j === mirror.length-1) {
            if (i === j-1) {
                return i+1;
            } else {
                return mirror.length - (counter + 1);
            }
            } else {
            return findReflection(mirror, i - 1, j + 1, counter + 1);
            }
            }
            else {
            if(counter > 0) {
            return findReflection(mirror, j-counter, (j-counter) + 1, 0);
            } else {
            if (j === mirror.length-1) {
                return 0;
            } else {
                return findReflection(mirror, i + 1, j + 1, counter);
            }
            }
            }
            } 


            let r = 0; let c = 0; let sum = 0;
            mirrors.forEach(mirror => {
            r = findReflection(mirror, 0, 1, 0);
            if (r === 0) {
            smudge = false;
            let transposedMirror = transpose(mirror);
            c = findReflection(transposedMirror, 0, 1, 0);
            }
            smudge = false;
            sum += c + (r * 100);
            r = 0;
            c = 0;
            });
            let result = sum;
            console.log(`Part 1 -> ${result}`);

            function findSingleDifference(str1, str2) {
            let differingPosition = -1;
            let differencesCount = 0;
            for (let i = 0; i < str1.length; i++) {
            if (str1[i] !== str2[i]) {
            differencesCount++;
            differingPosition = i;
            }
            if (differencesCount > 1) {
            differingPosition = -1;
            break;
            }
            }
            return differingPosition;
            }

            function toggleCharacter(str, index) {
            const char = str[index];
            const toggledChar = char === '#' ? '.' : '#';
            return str.substring(0, index) + toggledChar + str.substring(index + 1);
            }

            function findReflectionWithSmudge(mirror, i, j, counter) {

            const d = findSingleDifference(mirror[i], mirror[j]);
            if (d !== -1 && !smudge) {
            smudge = true;
            }
            if (mirror[i] === mirror[j] || (d !== -1 && smudge)) {
            if (i === 0) {
            if (j === i+1) {
                if (smudge) {
                    return 1;    
                } else {
                    return findReflectionWithSmudge(mirror, i + 1, j + 1, counter);
                }
            } else {
                if (smudge) {
                    return counter+1;    
                } else {
                    return findReflectionWithSmudge(mirror, j-counter, (j-counter) + 1, 0);
                }
            }
            } else if (j === mirror.length-1) {
            if (i === j-1) {
                if (smudge) {
                    return i+1;
                } else {
                    return 0;
                }
                
            } else {
                if (smudge) {
                    return mirror.length - (counter + 1); 
                } else {
                    return findReflectionWithSmudge(mirror, j-counter, (j-counter) + 1, 0);
                }
                
            }
            } else {
            return findReflectionWithSmudge(mirror, i - 1, j + 1, counter + 1);
            }
            }
            else {
            smudge = false;
            if(counter > 0) {
            return findReflectionWithSmudge(mirror, j-counter, (j-counter) + 1, 0);
            } else {
            if (j === mirror.length-1) {
                return 0;
            } else {
                return findReflectionWithSmudge(mirror, i + 1, j + 1, counter);
            }
            }
            }
            }

            r = 0; c = 0; sum = 0;
            mirrors.forEach(mirror => {
            r = findReflectionWithSmudge(mirror, 0, 1, 0);
            if (r === 0) {
            smudge = false;
            let transposedMirror = transpose(mirror);
            c = findReflectionWithSmudge(transposedMirror, 0, 1, 0);
            }
            smudge = false;
            sum += c + (r * 100);
            r = 0;
            c = 0;
            });
            result = sum;
            console.log(`Part 2 -> ${result}`);Language:JavaScript

--- Day 14: Parabolic Reflector Dish ---

Go to page

Part 1 & 2

const lines = $0.innerText.split('\n').slice(0, -1);
            const numRows = lines.length;
            const numCols = lines[0].length;
            const mapSet = new Set();
            const mapIndices = new Map();
            const map = lines.map(line => line.split(''));
            const EMPTY = '.';
            const OROCK = 'O';
            const REPS = 1; // change this constant to 1000000000 (one billion) to get part 2 of the challenge

            function tiltNorth(i, j) {
            while (i > 0 && map[i - 1][j] === EMPTY) {
            map[i][j] = EMPTY;
            map[--i][j] = OROCK;
            }
            }

            function tiltWest(i, j) {
            while (j > 0 && map[i][j - 1] === EMPTY) {
            map[i][j] = EMPTY;
            map[i][--j] = OROCK;
            }
            }

            function tiltSouth(i, j) {
            while (i < numRows - 1 && map[i + 1][j] === EMPTY) {
            map[i][j] = EMPTY;
            map[++i][j] = OROCK;
            }
            }

            function tiltEast(i, j) {
            while (j < numCols && map[i][j + 1] === EMPTY) {
            map[i][j] = EMPTY;
            map[i][++j] = OROCK;
            }
            }

            // Cycles loop
            for (let r = 1; r <= REPS; r++) {
            const currentMapStr = JSON.stringify(map);
            if (mapSet.has(currentMapStr)) {
            const repeatedIndex = mapIndices.get(currentMapStr);
            cycleStartIndex = repeatedIndex;
            cycleLength = r - repeatedIndex;
            break;
            }
            mapSet.add(currentMapStr);
            mapIndices.set(currentMapStr, r);


            for (let i = 1; i < numRows; i++) {
            for (let j = 0; j < numCols; j++) {
            if (map[i][j] === OROCK) {
                tiltNorth(i, j);
            }
            }
            }

            if (REPS === 1) {
            break;
            }

            for (let i = 0; i < numRows; i++) {
            for (let j = 0; j < numCols; j++) {
            if (map[i][j] === OROCK) {
                tiltWest(i, j);
            }
            }
            }

            for (let i = numRows - 2; i >= 0; i--) {
            for (let j = 0; j < numCols; j++) {
            if (map[i][j] === OROCK) {
                tiltSouth(i, j);
            }
            }
            }

            for (let i = 0; i < numRows; i++) {
            for (let j = numCols - 2; j >= 0; j--) {
            if (map[i][j] === OROCK) {
                tiltEast(i, j);
            }
            }
            }
            }

            const finalReps = cycleStartIndex + (REPS - cycleStartIndex) % cycleLength;

            for (let r = cycleStartIndex; r <= finalReps; r++) {

            for (let i = 1; i < numRows; i++) {
            for (let j = 0; j < numCols; j++) {
            if (map[i][j] === OROCK) {
                tiltNorth(i, j);
            }
            }
            }

            for (let i = 0; i < numRows; i++) {
            for (let j = 0; j < numCols; j++) {
            if (map[i][j] === OROCK) {
                tiltWest(i, j);
            }
            }
            }

            for (let i = numRows - 2; i >= 0; i--) {
            for (let j = 0; j < numCols; j++) {
            if (map[i][j] === OROCK) {
                tiltSouth(i, j);
            }
            }
            }

            for (let i = 0; i < numRows; i++) {
            for (let j = numCols - 2; j >= 0; j--) {
            if (map[i][j] === OROCK) {
                tiltEast(i, j);
            }
            }
            }
            }

            const sum = map.reduce((total, line, i) => {
            return total + line.filter(l => l === OROCK).length * (map.length - i);
            }, 0);

            console.log(`Part ${REPS > 1 ? 2 : 1} -> ${sum}`);Language:JavaScript

--- Day 15: Lens Library ---

Go to page

Part 1 & 2

const data = $0.innerText.split(',');
            data[data.length-1] = data[data.length-1].slice(0,-1);
            let currentVal = 0;
            let sum = 0;

            function hash(ascii) {
            currentVal = currentVal + ascii;
            currentVal *= 17;
            currentVal = currentVal % 256;
            return currentVal;
            }

            data.forEach(d => {
            let val = 0;
            for (let i = 0; i < d.length; i++) {
            val = hash(d.charCodeAt(i));
            }
            sum += val;
            currentVal = 0;
            });

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

            currentVal = 0;
            const regex = /([a-zA-Z]+)([=|-]?)([a-zA-Z0-9]*)/;
            const steps = [];
            const boxes = {};

            for (let i = 0; i <= 255; i++) {
            boxes[i] = [];
            }
            data.forEach(d => {
            const match = d.match(regex);
            const result = match.slice(1).filter(Boolean);
            steps.push(result);
            });

            function getBox(label) {
            let val = 0;
            for (let i = 0; i < label.length; i++) {
            val = hash(label.charCodeAt(i));
            }
            currentVal = 0;
            return val;
            }

            for (let i = 0; i < steps.length; i++) {
            let box = getBox(steps[i][0]);
            if (steps[i][1] === '=') {
            if (boxes[box].includes(steps[i][0])) {
            const keyToUpdate = steps[i][0];
            const value = steps[i][2];
            const regex = new RegExp(`\\[${keyToUpdate} \\d+\\]`);
            boxes[box] = boxes[box].replace(regex, `[${keyToUpdate} ${value}]`);
            } else {
            boxes[box] += `[${steps[i][0]} ${steps[i][2]}] `;
            }
            } else {
            const keyToRemove = steps[i][0];
            if (boxes[box].includes(keyToRemove)) {
            const regex = new RegExp(`\\[${keyToRemove} \\d+\\]`, 'g');
            boxes[box] = boxes[box].replace(regex, '');
            }

            }
            }

            sum = 0;
            const pattern = /\w+ \d+/g;
            for (let j = 0; j < Object.keys(boxes).length; j++) {

            if (typeof(boxes[j]) === 'string' && boxes[j].trim().length > 0) {
            const arr = boxes[j].match(pattern);
            arr.forEach((el, i) => {
            const calc = (1 + j) * (i+1) * parseInt(el.split(' ')[1]);
            sum += calc;
            });
            }
            }
            console.log('Part 2 ->',sum);Language:JavaScript

--- Day 16: The Floor Will Be Lava ---

Go to page

Part 1 & 2

const data = $0.innerText.split('\n').slice(0,-1);
            let bean = {x:0, y:0, xdir: 1, ydir: 0};
            let explored = [];
            let max = 0;

            explored.push([bean.x, bean.y]);
            cave(bean.x, bean.y, bean.xdir, bean.ydir, [...data]);

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

            data.forEach((line,i) => {
            if (i === 0) {
            explored = [];
            cave(0, i, 1, 0, [...data]);
            explored.length > max ? max = explored.length : null;
            for (let j = 0; j < line.length; j++) {
            explored = [];
            cave(j, i, 0, 1, [...data]);
            explored.length > max ? max = explored.length : null;
            }
            explored = [];
            cave(line.length-1, i, -1, 0, [...data]);
            explored.length > max ? max = explored.length : null;
            } else if (i === data.length-1) {
            explored = [];
            cave(0, i, 1, 0, [...data]);
            explored.length > max ? max = explored.length : null;
            for (let j = 0; j < line.length; j++) {
            explored = [];
            cave(j, i, 0, -1, [...data]);
            explored.length > max ? max = explored.length : null;
            }
            explored = [];
            cave(line.length-1, i, -1, 0, [...data]);
            explored.length > max ? max = explored.length : null;
            } else {
            explored = [];
            cave(0, i, 1, 0, [...data]);
            explored.length > max ? max = explored.length : null;
            explored = [];
            cave(line.length-1, i, -1, 0, [...data]);
            explored.length > max ? max = explored.length : null;
            }

            })

            function cave(x, y, xdir, ydir, currentData) {
            let contain = false;

            if (x < 0 || x > currentData[0].length - 1 || y < 0 || y > currentData.length - 1) {
            return ;
            }

            for (let coors of explored) {
            if (coors[0] === x && coors[1] === y) {
            contain = true;
            }
            }
            if (contain) {
            contain = false;
            } else {
            explored.push([x, y]);
            }

            if (currentData[y][x] === '\\') {

            if (xdir === 1) {
            cave(x, y+1, 0, 1, currentData);    
            } else if (xdir === -1) {
            cave(x, y-1, 0, -1, currentData);    
            } else if (ydir === 1) {
            cave(x+1, y, 1, 0, currentData);    
            } else if (ydir === -1) {
            cave(x-1, y, -1, 0, currentData);    
            }

            }
            else if (currentData[y][x] === '/') {
            if (xdir === 1) {
            cave(x, y-1, 0, -1, currentData);    
            } else if (xdir === -1) {
            cave(x, y+1, 0, 1, currentData);    
            } else if (ydir === 1) {
            cave(x-1, y, -1, 0, currentData);    
            } else if (ydir === -1) {
            cave(x+1, y, 1, 0, currentData);    
            }
            }
            else if (currentData[y][x] === '|') {
            currentData[y] = currentData[y].substring(0, x) + 'L' + currentData[y].substring(x + 1);

            if (xdir === 1 || xdir === -1) {
            cave(x, y-1, 0, -1, currentData);
            cave(x, y+1, 0, 1, currentData);
            }
            else if (ydir === 1) {
            cave(x, y+1, 0, 1, currentData);
            } else if (ydir === -1) {
            cave(x, y-1, 0, -1, currentData);
            }
            }
            else if (currentData[y][x] === '-') {
            currentData[y] = currentData[y].substring(0, x) + 'L' + currentData[y].substring(x + 1);

            if (xdir === 1) {
            cave(x+1, y, 1, 0, currentData);
            } else if (xdir === -1) {
            cave(x-1, y, -1, 0, currentData);
            } else {
            cave(x+1, y, 1, 0, currentData);
            cave(x-1, y, -1, 0, currentData);
            }
            }
            else if (currentData[y][x] === 'L') {
            return ;
            }
            else {
            if (xdir === 1) {
            cave(x+1, y, 1, 0, currentData);
            } else if (xdir === -1) {
            cave(x-1, y, -1, 0, currentData);
            } else if (ydir === 1) {
            cave(x, y+1, 0, 1, currentData);
            } else if (ydir === -1) {
            cave(x, y-1, 0, -1, currentData);
            }
            }

            }

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

--- Day 17: Clumsy Crucible ---

Go to page

Part 1 & 2

// NOTE: This code is currently really slow! It takes around 15 seconds to drop an answer for part 1 and around 1 minute for part 2! Hopefully I will improve it in the future. For now, it is what I have; I hope it can be useful for you, dear reader. =) 
            // Iván R.

            const part = 1; // IMPORTANT: change this constant to 2 to get the answer for part 2
            const grid = $0.innerText.trim().split('\n').map(line => line.split('').map(Number));
            const seen = new Set();
            const pq = [[0, 0, 0, 0, 0, 0]];

            while (pq.length > 0) {
            const [hl, r, c, dr, dc, n] = pq.shift();

            if (r === grid.length - 1 && c === grid[0].length - 1 && n >= (part > 1 ? 4 : 0)) {
            console.log(`Part ${part} ->`,hl);
            break;
            }

            if (seen.has(`${r}-${c}-${dr}-${dc}-${n}`)) {
            continue;
            }

            seen.add(`${r}-${c}-${dr}-${dc}-${n}`);

            if (n < (part > 1 ? 10 : 3) && (dr !== 0 || dc !== 0)) {
            const nr = r + dr;
            const nc = c + dc;
            if (nr >= 0 && nr < grid.length && nc >= 0 && nc < grid[0].length) {
            pq.push([hl + grid[nr][nc], nr, nc, dr, dc, n + 1]);
            pq.sort((a, b) => a[0] - b[0]);
            }
            }

            if (part > 1) {
            if (n >= 4 || (dr === 0 && dc === 0)) {
            const directions = [[0, 1], [1, 0], [0, -1], [-1, 0]];
            for (const [ndr, ndc] of directions) {
                if ((ndr !== dr || ndc !== dc) && (ndr !== -dr || ndc !== -dc)) {
                    const nr = r + ndr;
                    const nc = c + ndc;
                    if (nr >= 0 && nr < grid.length && nc >= 0 && nc < grid[0].length) {
                        pq.push([hl + grid[nr][nc], nr, nc, ndr, ndc, 1]);
                        pq.sort((a, b) => a[0] - b[0]);
                    }
                }
            }
            }
            } else {
            const directions = [[0, 1], [1, 0], [0, -1], [-1, 0]];
            for (const [ndr, ndc] of directions) {
            if ((ndr !== dr || ndc !== dc) && (ndr !== -dr || ndc !== -dc)) {
                const nr = r + ndr;
                const nc = c + ndc;
                if (nr >= 0 && nr < grid.length && nc >= 0 && nc < grid[0].length) {
                    pq.push([hl + grid[nr][nc], nr, nc, ndr, ndc, 1]);
                    pq.sort((a, b) => a[0] - b[0]);
                }
            }
            }
            }
            }Language:JavaScript

--- Day 18: Lavaduct Lagoon ---

Go to page

Part 1 & 2

const data = $0.innerText.split('\n').slice(0, -1);
            let x = [];
            let y = [];
            let X = 0;
            let Y = 0;
            let len = 0;

            data.forEach(instruction => {
            const [dir, steps] = instruction.split(' ');
            const numSteps = parseInt(steps, 10);
            len += numSteps;
            switch(dir) {
            case 'U':
            Y-=numSteps;
            break;
            
            case 'R':
            X+=numSteps;
            break;

            case 'D':
            Y+=numSteps;
            break;
            
            case 'L':
            X-=numSteps;
            break;
            }
            x.push(X);
            y.push(Y);
            });

            function getArea(x, y) {
            let area = 0;
            let sum = 0;
            for (let i = 0; i < x.length-1; i++) {
            sum += x[i] * y[i+1] - x[i+1] * y[i]
            }
            area = sum / 2;
            return Math.abs(area);
            }

            let area = getArea(x,y);
            console.log('Part 1 ->',len + (area + 1 - len / 2));

            function hexToDec(hex) {
            if (!/^[0-9A-Fa-f]+$/.test(hex)) {
            throw new Error('No valid hex!');
            }
            return parseInt(hex, 16);
            }


            const instructions = [];
            data.forEach( value => {
            let val = value.slice(value.indexOf('(')+2, -2);
            val = hexToDec(val);
            let ins = '';
            switch (value.slice(-2, -1)) {
            case '0':
            ins = 'R';
            break;
            case '1':
            ins = 'D';
            break;
            case '2':
            ins = 'L';
            break;
            case '3':
            ins = 'U';
            break; 
            }
            instructions.push([ins, val]);
            });

            X = 0; Y = 0; len = 0; x = []; y = [];

            instructions.forEach(instruction => {
            const dir = instruction[0];
            const steps = instruction[1];
            const numSteps = parseInt(steps, 10);
            len += numSteps;
            switch(dir) {
            case 'U':
            Y-=numSteps;
            break;
            
            case 'R':
            X+=numSteps;
            break;

            case 'D':
            Y+=numSteps;
            break;
            
            case 'L':
            X-=numSteps;
            break;
            }
            x.push(X);
            y.push(Y);
            });

            area = getArea(x,y);
            console.log('Part 2 ->',len + (area + 1 - len / 2));Language:JavaScript

--- Day 19: Aplenty ---

Go to page

Part 1 & 2

const workflows = $0.innerText.trim().split(/\r?\n\r?\n/)[0].split('\n');
const parts = $0.innerText.trim().split(/\r?\n\r?\n/)[1].split('\n');
const workflow = {};

function compare(p, w, index) { 
    if (w[index].cond === null) {
        if (w[index].next === 'A' || w[index].next === 'R') {
            return w[index].next;
        }
        return compare(p, workflow[w[index].next], 0);
    }
    const v = p[w[index].cat];
    if (w[index].cond === '<') {
        if (v < w[index].val) {
            if (w[index].next === 'A' || w[index].next === 'R') {
                return w[index].next;
            }
            return compare(p, workflow[w[index].next], 0);
        } else {
            return compare(p, w, index+1);
        }
    } 
    else if (w[index].cond === '>') {
        if (v > w[index].val) {
            if (w[index].next === 'A' || w[index].next === 'R') {
                return w[index].next;
            }
            return compare(p, workflow[w[index].next], 0);
        } else {
            return compare(p, w, index+1);
        }
    }
}

const parsePart = (line) => {
    return line.split(",").reduce((part, e) => {
    const m = e.match(/(x|m|a|s)=(\d+)/);
    part[m[1]] = parseInt(m[2]);
    return part;
    }, {});
};
const part = parts.map(parsePart);

for (let work of workflows) {
    const w = work.match(/(\w+){(.*)}/);
    const label = w[1];
    const rule = w[2].split(',');
    const rules = [];
    for (let r of rule) {
        const s = r.match(/(a|m|s|x)(<|>)(\d+):(\w+)/);
        if (s) {
            rules.push({cat: s[1], cond: s[2], val: parseInt(s[3]), next: s[4]});
        } else {
            rules.push({cond: null, next: r});
        }
    }

    workflow[label] = rules;
}

let current = 'in';
let total = 0;
part.forEach(p => {
    const val = compare(p, workflow[current], 0);
    if (val === 'A') {
        let sum = 0;
        for (let key in p) {
            if (p.hasOwnProperty(key)) {
                sum += p[key];
            }
        }
        total += sum;
    }
});

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

const start = { label: 'in', xmin: 1, xmax: 4000, mmin: 1, mmax: 4000, amin: 1, amax: 4000, smin: 1, smax: 4000 };
const queue = [start];
total = 0;

while(queue.length) {
    const part = queue.shift();
    
    if (part.label === 'R') { continue; }
    if (part.label === 'A') {
        const pieces = (
            (1 + part.xmax - part.xmin) *
            (1 + part.mmax - part.mmin) *
            (1 + part.amax - part.amin) *
            (1 + part.smax - part.smin));
        total += pieces;
        continue;
        }
    
    workflow[part.label].forEach(rule => {
        if (rule.cond != null) {
            const v = rule.cat;
            const op = rule.cond;
            const val = rule.val;
            const limit = val;
            const next = { ...part, label: rule.next};
            
            if (op === '<') {
                next[`${v}max`] = Math.min(next[`${v}max`], limit - 1);
                queue.push(next);
                part[`${v}min`] = Math.max(part[`${v}min`], next[`${v}max`] + 1);
            } 
            else if (op === '>') {
                next[`${v}min`] = Math.max(next[`${v}min`], limit + 1);
                queue.push(next);
                part[`${v}max`] = Math.min(part[`${v}max`], next[`${v}min`] - 1);
            }
        } 
        else {
            queue.push({ ...part, label: rule.next })
        }
    });
}
console.log('Part 2 ->', total);Language:JavaScript

--- Day 20: Pulse Propagation ---

Go to page

Part 1 & 2

function parse(input) {
    const modules = {};
    const inputs = {};
    
    input.split("\n").slice(0,-1).forEach((line) => {
        const {
        groups: { prefix, key, output },
        } = line.match(/(?<prefix>[%&]?)(?<key>.+) -> (?<output>.+)/);
    
        modules[key] = { prefix, name: key, dest: output.split(", "), pulse: false, sender: ''};
    
        output.split(", ").forEach((outputKey) => {
        inputs[outputKey] ??= {};
        inputs[outputKey][key] = false;
        });
    });
    
    return [modules, inputs];
    }
    let [modules, inputs] = parse($0.innerText);
    
    let cycles = {};
    let seen = {};
    for (let [name, module] of Object.entries(modules)) {
        if (module.dest.includes('gq')) {
            seen[name] = 0;
        }
    }
    
    const pushes = 1000;
    let lowCounter = 0;
    let highCounter = 0;
    let q = [];
    let start = 'broadcaster'; 
    let reachedRX = false;
    let counter = 1;
    let bool = false;
    
    q.push(modules[start]);
    
    while (bool === false) {
        lowCounter++;
        while (q.length > 0) {
            let module = q.shift();
            handlePulse(module, module.pulse, module.dest.length, module.sender);
        }
        counter++;
        if (counter === pushes) {
            let result = lowCounter * highCounter;
            console.log('Part 1 ->',result);
        }
        q = [];
        q.push(modules[start]);
    }
    
    
    function handlePulse(r, p, s, sender) {
        
        if (r.name === 'broadcaster') {
            for (let i = 0; i < s; i++) {
                q.push({prefix: modules[r.dest[i]].prefix, name: r.dest[i], pulse: p, dest: modules[r.dest[i]].dest, sender: r.name});
                p === false ? lowCounter++ : highCounter++;
            }
        }
        else if (r.prefix === '%') {
            if (p === false) {
                modules[r.name].pulse = !modules[r.name].pulse;
                for (let i = 0; i < s; i++) {            
                    q.push({prefix: modules[r.dest[i]].prefix, name: r.dest[i], pulse: modules[r.name].pulse, dest: modules[r.dest[i]].dest, sender: r.name});
                    modules[r.name].pulse === false ? lowCounter++ : highCounter++;
                }
                
            }
        } 
        else if (r.prefix === '&') {
            
            inputs[r.name][r.sender] = p;
            const set = new Set (Object.values(inputs[r.name]));
            if (set.size === 1 && set.has(true)) {
                
                for (let i = 0; i < s; i++) {
                    if (r.dest.includes('rx')) {
                        lowCounter++;
                    } else {
                        q.push({prefix: modules[r.dest[i]].prefix, name: r.dest[i], pulse: false, dest: modules[r.dest[i]].dest, sender: r.name});
                        lowCounter++;
                    }
                }
            } 
            else {
    
                if (r.dest.includes('gq')) {
                    seen[r.name] += 1;
        
                    if (cycles[r.name] === undefined) {
                        cycles[r.name] = counter;
                    }
    
                    if (Object.values(seen).every(value => value)) {
                        bool = true;
                        let x = 1;
                        for (let cycle_length of Object.values(cycles)) {
                            x = (x * cycle_length) / GCD(x, cycle_length);
                        }
                        console.log('Part 2 ->',x);
                    }
        
                }
                
                for (let i = 0; i < s; i++) {
                    if (r.dest.includes('rx')) {
                        highCounter++;
                    } else {
                        q.push({prefix: modules[r.dest[i]].prefix, name: r.dest[i], pulse: true, dest: modules[r.dest[i]].dest, sender: r.name});
                        highCounter++;
                    }
                }
            }
        }
    }
    
    function GCD(a, b) {
        return b === 0 ? a : GCD(b, a % b);
    }Language:JavaScript

--- Day 21: Step Counter ---

Go to page

Part 1 & 2 (note: part 2 takes a few seconds to drop an answer!)

const data = $0.innerText.split('\n').slice(0,-1);
const start = [];

data.forEach((line, i) => {
    for (let j = 0; j < line.length; j++) {
        if (data[i][j] === 'S') {
            start.push(i, j);
            break;
        }
    }
});

function fill(sy, sx, steps) {
    const directions = [[0, 1], [1, 0], [0, -1], [-1, 0]];
    let visited = new Set();
    let plots = [[sy,sx]];
    
    let counter = 0;
    while (counter < steps) {
    
        let toExplore = plots;
        plots = [];
        visited = new Set();
        for (let [x,y] of toExplore) {
            //console.log(x,y);
            for (let [xdir,ydir] of directions) {
                let newx = x + xdir;
                let newy = y + ydir;
                if (data[newy] !== undefined && data[newy][newx] !== undefined) {
                    if (data[newy][newx] !== '#' && !visited.has(`${newx},${newy}`)) {
                        visited.add(`${newx},${newy}`);
                        plots.push([newx,newy]);
                    }
                    
                }
            }
        }   
        counter++;
    }
    return plots.length;
}

console.log('Part 1 ->',fill(start[0], start[1], 64));

const size = data.length;
const steps = 26501365;
const gridWidth = Math.floor(steps / size - 1);
const odd = Math.pow(Math.floor(gridWidth / 2) * 2 + 1, 2);
const even = Math.pow(Math.floor((gridWidth + 1) / 2) * 2, 2);
const oddPoints = fill(start[0], start[1], size * 2 + 1);
const evenPoints = fill(start[0], start[1], size * 2);

const cornerT = fill(size - 1, start[1], size - 1);
const cornerR = fill(start[0], 0, size - 1);
const cornerB = fill(0, start[1], size - 1);
const cornerL = fill(start[0], size - 1, size - 1);

const smallTR = fill(size - 1, 0, Math.floor(size / 2) - 1);
const smallTL = fill(size - 1, size - 1, Math.floor(size / 2) - 1);
const smallBR = fill(0, 0, Math.floor(size / 2) - 1);
const smallBL = fill(0, size - 1, Math.floor(size / 2) - 1);

const largeTR = fill(size - 1, 0, Math.floor(size * 3 / 2) - 1);
const largeTL = fill(size - 1, size - 1, Math.floor(size * 3 / 2) - 1);
const largeBR = fill(0, 0, Math.floor(size * 3 / 2) - 1);
const largeBL = fill(0, size - 1, Math.floor(size * 3 / 2) - 1);


console.log('Part 2 ->',
    odd * oddPoints +
    even * evenPoints +
    cornerT + cornerR + cornerB + cornerL +
    (gridWidth + 1) * (smallTR + smallTL + smallBR + smallBL) +
    gridWidth * (largeTR + largeTL + largeBR + largeBL)
);Language:JavaScript

--- Day 22: Sand Slabs ---

Go to page

Part 1 & 2

const input = $0.innerText.trim().split('\n');
const bricks = input.map(line => line.replace("~", ",").split(",").map(Number));

bricks.sort((a, b) => a[2] - b[2]);

function overlaps(a, b) {
    return Math.max(a[0], b[0]) <= Math.min(a[3], b[3]) && Math.max(a[1], b[1]) <= Math.min(a[4], b[4]);
}

for (let index = 0; index < bricks.length; index++) {
    let max_z = 1;
    for (let i = 0; i < index; i++) {
        const check = bricks[i];
        if (overlaps(bricks[index], check)) {
            max_z = Math.max(max_z, check[5] + 1);
        }
    }
    bricks[index][5] -= bricks[index][2] - max_z;
    bricks[index][2] = max_z;
}

bricks.sort((a, b) => a[2] - b[2]);

const k_supports_v = new Array(bricks.length).fill(null).map(() => new Array());
const v_supports_k = new Array(bricks.length).fill(null).map(() => new Array());

for (let j = 0; j < bricks.length; j++) {
    const upper = bricks[j];
    for (let i = 0; i < j; i++) {
        const lower = bricks[i];
        if (overlaps(lower, upper) && upper[2] === lower[5] + 1) {
            k_supports_v[i].push(j);
            v_supports_k[j].push(i);
        }
    }
}

let total = 0;

k_supports_v.forEach(bricks => {
    bricks.every(j => v_supports_k[j].length > 1) ? total++ : null;
});

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

let sum = 0;
for (let i = 0; i < bricks.length; i++) {
    total = 0;
    let set = new Set();
    set.add(i);

    let sublist = k_supports_v.slice(i);
    sublist.forEach(bricks => {
        for (let brick of bricks) {
            if (v_supports_k[brick].every( b=> set.has(b) )) {
                if (!set.has(brick)) {
                    set.add(brick);
                    total++;
                }
            }
        }
    });
    sum += total;
}

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

--- Day 23: A Long Walk ---

Go to page

Part 1 (part 2 below)

const data = $0.innerText.split('\n').slice(0, -1);
const start = [0, data[0].indexOf('.')];
const end = [data.length-1, data[data.length-1].indexOf('.')];
const points = [start, end];

data.forEach((line, i) => {

    for (let j = 0; j < line.length; j++) {
        if (line[j] === "#") { continue }
        let neighbors = 0;
        const dirs = [[i - 1, j], [i + 1, j], [i, j + 1], [i, j - 1]];
    
        for (let [y, x] of dirs) {
            
            if (y >= 0 && y < data.length && x >= 0 && x < data[0].length && data[y][x] != "#") {
                neighbors++;
            }
        }
        if (neighbors > 2) {
            points.push([i, j]);
        }
    }
    
});

const dirs = {
    "^": [[-1,0]],
    "v": [[1,0]],
    ">": [[0, 1]],
    "<": [[0, -1]],
    ".": [[-1,0], [1,0], [0,1], [0,-1]]
};

let graph = {};
for (let pt of points) {
    graph[pt] = [];
}

for (let [sr, sc] of points) {

    let stack = [];
    stack.push([0, sr, sc]);
    let seen = new Set();
    seen.add(`${sr},${sc}`);

    while (stack.length > 0) {
        let [n, r, c] = stack.pop();
        let contain = points.some(v => v[0] === r && v[1] === c);
        if (n != 0 && contain) {
            graph[`${sr},${sc}`].push([r,c, n]);
            continue;
        }
        for (let [dr, dc] of dirs[data[r][c]]) {
            let nr = r + dr;
            let nc = c + dc;

            if (nr >= 0 && nr < data.length && nc >=0 && nc < data[0].length && data[nr][nc] != "#" && !seen.has(`${nr},${nc}`)) {
                stack.push([n+1, nr, nc]);
                seen.add(`${nr},${nc}`);
            }
        }
    }
    
}

let seen = new Set();
function dfs(pt) {

    if (pt[0] === end[0] && pt[1] === end[1]) {
        return 0;
    }
    
    let max = Number.NEGATIVE_INFINITY;

    seen.add(`${pt[0]},${pt[1]}`);
    for (let nx of graph[`${pt[0]},${pt[1]}`]) {
        max = Math.max(max, dfs([nx[0],nx[1]]) + nx[2]);
    }
    seen.delete(`${pt[0]},${pt[1]}`);

    return max;
    
}

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

Part 2

Note: it takes a few seconds to drop the answer!

const data = $0.innerText.split('\n').slice(0, -1);
const start = [0, data[0].indexOf('.')];
const end = [data.length-1, data[data.length-1].indexOf('.')];
const points = [start, end];

data.forEach((line, i) => {

    for (let j = 0; j < line.length; j++) {
        if (line[j] === "#") { continue }
        let neighbors = 0;
        const dirs = [[i - 1, j], [i + 1, j], [i, j + 1], [i, j - 1]];
    
        for (let [y, x] of dirs) {
            
            if (y >= 0 && y < data.length && x >= 0 && x < data[0].length && data[y][x] != "#") {
                neighbors++;
            }
        }
        if (neighbors > 2) {
            points.push([i, j]);
        }
    }
    
});

let graph = {};
for (let pt of points) {
    graph[pt] = [];
}

for (let [sr, sc] of points) {

    let stack = [];
    stack.push([0, sr, sc]);
    let seen = new Set();
    seen.add(`${sr},${sc}`);

    while (stack.length > 0) {
        let [n, r, c] = stack.pop();
        let contain = points.some(v => v[0] === r && v[1] === c);
        if (n != 0 && contain) {
            graph[`${sr},${sc}`].push([r,c, n]);
            continue;
        }

        for (let [dr, dc] of [[-1,0], [1,0], [0,1], [0,-1]]) {
            let nr = r + dr;
            let nc = c + dc;

            if (nr >= 0 && nr < data.length && nc >=0 && nc < data[0].length && data[nr][nc] != "#" && !seen.has(`${nr},${nc}`)) {
                stack.push([n+1, nr, nc]);
                seen.add(`${nr},${nc}`);
            }
        }
    }
    
}

let seen = new Set();
function dfs(pt) {

    if (pt[0] === end[0] && pt[1] === end[1]) {
        return 0;
    }
    
    let max = Number.NEGATIVE_INFINITY;

    seen.add(`${pt[0]},${pt[1]}`);

    for (let nx of graph[`${pt[0]},${pt[1]}`]) {
        if (!seen.has(`${nx[0]},${nx[1]}`)) {
            max = Math.max(max, dfs([nx[0],nx[1]]) + nx[2]);    
        }
        
    }
    seen.delete(`${pt[0]},${pt[1]}`);

    return max;
    
}

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

--- Day 24: Never Tell Me The Odds ---

Go to page

Part 1

class Hailstone {
constructor(sx, sy, sz, vx, vy, vz) {
    this.sx = sx;
    this.sy = sy;
    this.sz = sz;
    this.vx = vx;
    this.vy = vy;
    this.vz = vz;

    this.a = vy;
    this.b = -vx;
    this.c = vy * sx - vx * sy;
}

toString() {
    return `Hailstone{a=${this.a}, b=${this.b}, c=${this.c}}`;
}
}

const lines = $0.innerText.split('\n').slice(0,-1);
const hailstones = lines.map(line => new Hailstone(...line.replace('@', ',').split(',').map(Number)));
const limits = [200000000000000, 400000000000000];
let occurrences = 0;

for (let i = 0; i < hailstones.length; i++) {
const hs1 = hailstones[i];
for (let j = 0; j < i; j++) {
    const hs2 = hailstones[j];
    const [a1, b1, c1] = [hs1.a, hs1.b, hs1.c];
    const [a2, b2, c2] = [hs2.a, hs2.b, hs2.c];
    if (a1 * b2 === b1 * a2) {
        continue;
    }
    const x = (c1 * b2 - c2 * b1) / (a1 * b2 - a2 * b1);
    const y = (c2 * a1 - c1 * a2) / (a1 * b2 - a2 * b1);
    if (limits[0] <= x && x <= limits[1] && limits[0] <= y && y <= limits[1]) {
        if ((x - hs1.sx) * hs1.vx >= 0 && (y - hs1.sy) * hs1.vy >= 0
        && (x - hs2.sx) * hs2.vx >= 0 && (y - hs2.sy) * hs2.vy >= 0) {
            occurrences++;
        }
    }
}
}

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

Part 2 - I couldn't solve this part of the day by myself. Here you will find an implementation of another code found in the megathread solutions.

const data = $0.innerText.split('\n').slice(0,-1);
const hailstones = [];
const velocitiesX = {};
const velocitiesY = {};
const velocitiesZ = {};

const getRockVelocity = (velocities) => {
    let possibleV = [];
    for (let x = -1000; x <= 1000; x++) {
    possibleV.push(x);
    }

    Object.keys(velocities).forEach((velocity) => {
    if (velocities[velocity].length < 2) {
        return;
    }

    let newPossibleV = [];
    possibleV.forEach((possible) => {
        if ((velocities[velocity][0] - velocities[velocity][1]) % (possible - velocity) === 0) {
        newPossibleV.push(possible);
        }
    });

    possibleV = newPossibleV;
    })

    return possibleV[0];
}

for await (const line of data) {

    const [positions, velocity] = line.split(' @ ');
    const [px, py, pz] = positions.split(', ').map((n) => Number(n));
    const [vx, vy, vz] = velocity.split(', ').map((n) => Number(n));

    if (!velocitiesX[vx]) {
        velocitiesX[vx] = [px]
    } else {
        velocitiesX[vx].push(px);
    }

    if (!velocitiesY[vy]) {
        velocitiesY[vy] = [py]
    } else {
        velocitiesY[vy].push(py);
    }

    if (!velocitiesZ[vz]) {
        velocitiesZ[vz] = [pz]
    } else {
        velocitiesZ[vz].push(pz);
    }

    hailstones.push({ px, py, pz, vx, vy, vz });
    }

    let possibleV = [];
    for (let x = -1000; x <= 1000; x++) {
    possibleV.push(x);
    }

    const rvx = getRockVelocity(velocitiesX);
    const rvy = getRockVelocity(velocitiesY);
    const rvz = getRockVelocity(velocitiesZ);

    const results = {};
    for (let i = 0; i < hailstones.length; i++) {
    for (let j = i+1; j < hailstones.length; j++) {
        const stoneA = hailstones[i];
        const stoneB = hailstones[j];

        const ma = (stoneA.vy-rvy)/(stoneA.vx-rvx)
        const mb = (stoneB.vy-rvy)/(stoneB.vx-rvx)
    
        const ca = stoneA.py - (ma*stoneA.px)
        const cb = stoneB.py - (mb*stoneB.px)
    
        const rpx = parseInt((cb-ca)/(ma-mb))
        const rpy = parseInt(ma*rpx + ca)
    
        const time = Math.round((rpx - stoneA.px)/(stoneA.vx-rvx))
        const rpz = stoneA.pz + (stoneA.vz - rvz)*time

        const result = rpx + rpy + rpz;
        if (!results[result]) {
        results[result] = 1;
        } else {
        results[result]++;
        }
    }
    }

    const result = Object.keys(results).sort((a, b) => results[b] - results[a])[0];

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

--- Day 25: Snowverload ---

Go to page

Part 1

const inputData = $0.innerText;

function parseInputData(inputText) {
    const lines = inputText.trim().split('\n');
    const edges = [];
    const vToId = new Map();

    for (const line of lines) {
        const [v1, v2s] = line.split(': ');

        if (!vToId.has(v1)) {
            vToId.set(v1, vToId.size);
        }

        for (const v2 of v2s.split(' ')) {
            if (!vToId.has(v2)) {
                vToId.set(v2, vToId.size);
            }

            edges.push([vToId.get(v1), vToId.get(v2)]);
        }
    }

    return { edges, vToId };
}

// Find groups in the graph using union-find algorithm
function findGroups(vertexCount, edges, desiredCuts) {
    for (let i = 0; i < edges.length; ++i) {
        const idx = Math.floor(Math.random() * (i + 1));
        [edges[i], edges[idx]] = [edges[idx], edges[i]];
    }

    const groupParents = [-1];
    const vertexGroups = new Uint16Array(vertexCount);
    const groupPromotions = [-1];

    function union(v1, v2) {
        if (!vertexGroups[v1] && !vertexGroups[v2]) {
            // Union operation implementation for new groups
            const group = groupParents.length;
            groupParents.push(group);
            groupPromotions.push(1);
            vertexGroups[v1] = group;
            vertexGroups[v2] = group;
        } else if (!vertexGroups[v1]) {
            // Union operation implementation when v1 is not in a group
            const g = (vertexGroups[v2] = parent(v2));
            ++groupPromotions[g];
            vertexGroups[v1] = g;
        } else if (!vertexGroups[v2]) {
            // Union operation implementation when v2 is not in a group
            const g = (vertexGroups[v1] = parent(v1));
            ++groupPromotions[g];
            vertexGroups[v2] = g;
        } else {
            // Union operation implementation for merging existing groups
            let g1 = parent(v1);
            let g2 = parent(v2);

            if (g1 !== g2) {
                if (groupPromotions[g1] > groupPromotions[g2]) {
                    [g2, g1] = [g1, g2];
                }

                groupPromotions[g2] += groupPromotions[g1] + 1;
                groupParents[g1] = g2;
                vertexGroups[v1] = g2;
                vertexGroups[v2] = g2;
            } else {
                return false;
            }
        }

        return true;
    }

    function parent(v) {
        // Find the parent of a group
        if (vertexGroups[v] === 0) {
            return -1;
        }

        let group = vertexGroups[v];
        while (group !== groupParents[group]) {
            group = groupParents[group];
        }

        return group;
    }

    let edgeIdx = 0;
    while (vertexCount > 2) {
        const [v1, v2] = edges[edgeIdx++];

        if (union(v1, v2)) {
            --vertexCount;
        }
    }

    let removedEdges = 0;
    for (const [v1, v2] of edges) {
        if ((vertexGroups[v1] = parent(v1)) !== (vertexGroups[v2] = parent(v2))) {
            ++removedEdges;
        }
    }

    return removedEdges === desiredCuts ? vertexGroups : null;
}


function main(inputText) {
    const { edges, vToId } = parseInputData(inputText);

    while (true) {
        const groups = findGroups(vToId.size, edges, 3);

        if (groups !== null) {
            const group1Count = groups.filter(x => x === groups[0]).length;
            console.log(group1Count * (vToId.size - group1Count));
            break;
        }
    }
}

main(inputData);Language:JavaScript