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

Advent of Code 2025 - Solutions

Solutions 2024

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 12: Christmas Tree Farm ---

Go to page

Part 1

// ! VERY DIRTY CODE!!!
// I am ashamed of this code, but it works to get a right answer in this AoC challenge!
// And currently I don't have the time to refactor it... nor the knowledge to do it properly...
const data = $0.textContent.trim().split('\n\n');
const shapes = data.slice(0,6);
const regions = data.slice(6)[0].split('\n');
let canFit = 0;
regions.forEach(region => {
    const [dims, quantity] = region.split(': ');
    const [n1,n2] = dims.split('x').map(Number);
    const size = n1*n2;
    const nums = quantity.split(' ').map(Number);
    const sum = nums.reduce((acc,v)=>acc+v,0);
    const shapeNum = sum * 7;
    if (size >= sum*3*3 || size >= shapeNum) canFit++;
});
console.log('Part 1 ->', canFit);Language:JavaScript

--- Day 11: Reactor ---

Go to page

Part 1

const data = $0.textContent.trim().split('\n');
const g = {};
data.forEach(d => {
    const [n, c] = d.split(': ');
    g[n] = c.split(' ');
});
let m = {};
function dfs(node) {
    if(node === 'out') return 1;
    if(m[node] !== undefined) return m[node];
    let count = 0;
    for(const nxt of g[node] || []) {
        count+= dfs(nxt);
    }
    m[node] = count;
    return count;
}
console.log('Part 1 ->',dfs('you'));

m = {};
function dfs2(node, mask) {
    if(node === 'dac') mask |= 1;
    if(node === 'fft') mask |= 2;
    if(node === 'out') {
        return mask === 3 ? 1 : 0;
    }
    const key = node + '|' + mask;
    if(m[key] !== undefined) return m[key];
    let count = 0;
    for(const nxt of g[node] || []) {
        count+= dfs2(nxt,mask);
    }
    m[key] = count;
    return count;
}
console.log('Part 2 ->',dfs2('svr',0));Language:JavaScript

--- Day 10: Factory ---

Go to page

Part 1 & 2

const data = $0.textContent.trim().split('\n');
let result = 0;
data.forEach(d => {
    let ans = 0;
    const light = d.match(/\[(.*?)\]/)[1];
    let targetBits = 0;
    for (let i = 0; i < light.length; i++) {
        if (light[i] === '#') targetBits |= (1 << i);
    }
    const btns = [...d.matchAll(/\((.*?)\)/g)]
        .map(m => m[1] === "" ? [] : m[1].split(",").map(Number));
    const btnMask = btns.map(list => {
        let mask = 0;
        for(const l of list) mask |= (1 << l);
        return mask;
    });
    const queue = [0];
    const dist = new Map();
    dist.set(0, 0);

    while (queue.length > 0) {
        const cur = queue.shift();
        const d = dist.get(cur);

        for (const mask of btnMask) {
            const nxt = cur ^ mask;
            if (!dist.has(nxt)) {
                dist.set(nxt, d + 1);
                if (nxt === targetBits) ans = d + 1;
                queue.push(nxt);
            }
        }
    }
    result += ans;
});
console.log('Part 1 ->',result);

// I wasn't able to solve the following part by myself
// from here it is vibe coding helped by this post:
// https://www.reddit.com/r/adventofcode/comments/1pk87hl/comment/ntp4njq/
const INF = 1 << 30;
class Part2 {
    constructor(buttons, joltage) {
        this.buttons = buttons;
        this.joltage = joltage;
        this.n = joltage.length;
        this.m = buttons.length;
        this.memo = new Map();
        
        // Precompute button masks
        this.buttonMasks = buttons.map(button => {
            let mask = 0;
            for (const id of button) mask |= 1 << id;
            return mask;
        });
        
        // Precompute all parity states reachable in 1 press
        // Actually, we need all combinations (0 or 1 presses for each button)
        // But we can generate them efficiently
        this.generateAllParityCombinations();
    }
    
    generateAllParityCombinations() {
        const n = this.n;
        const m = this.m;
        
        // Generate all 2^m combinations
        this.parityStates = new Array(1 << n).fill().map(() => []);
        
        for (let mask = 0; mask < (1 << m); mask++) {
            let parityState = 0;
            for (let i = 0; i < m; i++) {
                if (mask & (1 << i)) {
                    parityState ^= this.buttonMasks[i];
                }
            }
            this.parityStates[parityState].push(mask);
        }
    }
    
    solve() {
        return this.recursiveSolve(this.joltage);
    }

    getKey(arr) {
        return arr.join(',');
    }
    
    countBits(n) {
        let count = 0;
        while (n) {
            count += n & 1;
            n >>= 1;
        }
        return count;
    }

    recursiveSolve(current) {
        // Base case: all zeros
        let allZero = true;
        for (const val of current) {
            if (val !== 0) {
                allZero = false;
                break;
            }
        }
        if (allZero) return 0;

        // Check memo
        const key = this.getKey(current);
        if (this.memo.has(key)) {
            return this.memo.get(key);
        }

        // Get parity pattern
        let parityState = 0;
        for (let i = 0; i < this.n; i++) {
            if (current[i] % 2 === 1) {
                parityState |= 1 << i;
            }
        }

        // Get all combinations that achieve this parity
        const combinations = this.parityStates[parityState];
        
        if (combinations.length === 0) {
            this.memo.set(key, INF);
            return INF;
        }

        let best = INF;

        // Try each combination
        for (const mask of combinations) {
            const pressedCount = this.countBits(mask);
            
            // Create new joltage after applying this combination ONCE
            // But careful: each button press affects all its lights ONCE
            const nextJoltage = [...current];
            
            // Subtract 1 for each affected joltage
            for (let i = 0; i < this.m; i++) {
                if (mask & (1 << i)) {
                    for (const id of this.buttons[i]) {
                        nextJoltage[id] -= 1;
                    }
                }
            }
            
            // Check if all joltages are non-negative
            let valid = true;
            for (const val of nextJoltage) {
                if (val < 0) {
                    valid = false;
                    break;
                }
            }
            
            if (!valid) continue;
            
            // Verify that all joltages are now even
            // They should be because we subtracted odd numbers from odd ones
            for (let i = 0; i < this.n; i++) {
                if (nextJoltage[i] % 2 !== 0) {
                    valid = false;
                    break;
                }
            }
            
            if (!valid) continue;
            
            // Halve all joltages and recurse
            const halved = nextJoltage.map(x => x / 2);
            const subResult = this.recursiveSolve(halved);
            
            if (subResult < INF) {
                // Formula: current presses + 2 * recursive result
                const total = pressedCount + 2 * subResult;
                if (total < best) {
                    best = total;
                }
            }
        }

        this.memo.set(key, best);
        return best;
    }
}
let ans2 = 0;
for (const line of data) {
    const comp = line.split(' ');
    const buttons = comp.slice(1, -1).map(c => c.slice(1, -1).split(',').map(Number));
    const joltage = comp[comp.length - 1].slice(1, -1).split(',').map(Number);
    ans2 += new Part2(buttons, joltage).solve();
}
console.log('Part 2 ->', ans2);Language:JavaScript

--- Day 9: Movie Theater ---

Go to page

Part 1 & 2

const data = $0.textContent.trim().split('\n');
const pts = data.map(d => d.split(',').map(Number));
let largest = 0;
for (let i = 0; i < pts.length; i++) {
    const [x1, y1] = pts[i];
    for (let j = i + 1; j < pts.length; j++) {
        const [x2, y2] = pts[j];
        const area = (Math.abs(x2 - x1) + 1) * (Math.abs(y2 - y1) + 1);
        largest = Math.max(largest, area);
    }
}
console.log('Part 1 ->', largest);

const sides = pts.map((p, i) => [
    p, pts[(i + 1) % pts.length]
]);
const inRange = (a1, a2, b1, b2) =>
    !(a1 <= b1 && a1 <= b2 && a2 <= b1 && a2 <= b2) &&
    !(a1 >= b1 && a1 >= b2 && a2 >= b1 && a2 >= b2);
function rectangleCrossesPolygon(x1, y1, x2, y2) {
    return sides.some(([[sx1, sy1], [sx2, sy2]]) =>
        inRange(sy1, sy2, y1, y2) &&
        inRange(sx1, sx2, x1, x2)
    );
}
let largest2 = 0;
for (let i = 0; i < pts.length; i++) {
    const [x1, y1] = pts[i];
    for (let j = i + 1; j < pts.length; j++) {
        const [x2, y2] = pts[j];
        const area = (Math.abs(x2 - x1) + 1) * (Math.abs(y2 - y1) + 1);
        if (area <= largest2) continue;
        if (!rectangleCrossesPolygon(x1, y1, x2, y2)) {
            largest2 = area;
        }
    }
}
console.log('Part 2 ->', largest2);Language:JavaScript

--- Day 8: Playground ---

Go to page

Part 1 & 2

function find(a) {
  while (parent[a] !== a) {
    parent[a] = parent[parent[a]];
    a = parent[a];
  }
  return a;
}
function union(a, b) {
  const ra = find(a);
  const rb = find(b);
  if (ra === rb) return false;
  if (size[ra] < size[rb]) {
    parent[ra] = rb;
    size[rb] += size[ra];
  } else {
    parent[rb] = ra;
    size[ra] += size[rb];
  }
  return true;
}
const data = $0.textContent.trim().split("\n");
const pairs = [];
const limit = 1000;
console.info("Wait a bit for answers...");
data.forEach((d, i) => {
  const [x, y, z] = d.split(",").map(Number);
  for (let j = i + 1; j < data.length; j++) {
    const [x2, y2, z2] = data[j].split(",").map(Number);
    const dx = x - x2;
    const dy = y - y2;
    const dz = z - z2;
    const dist = dx * dx + dy * dy + dz * dz;
    pairs.push([dist, i, j]);
  }
});
pairs.sort((a, b) => a[0] - b[0]);
let parent = Array(data.length)
  .fill(0)
  .map((_, i) => i);
let size = Array(data.length).fill(1);
for (let k = 0; k < limit; k++) {
  const [, i, j] = pairs[k];
  union(i, j);
}
const comp = new Map();
for (let i = 0; i < data.length; i++) {
  const r = find(i);
  comp.set(r, (comp.get(r) || 0) + 1);
}
const sizes = [...comp.values()].sort((a, b) => b - a);
console.log("Part 1 ->", sizes[0] * sizes[1] * sizes[2]);

let components = data.length;
let lastEdge = null;
parent = Array(data.length)
  .fill(0)
  .map((_, i) => i);
size = Array(data.length).fill(1);
for (let k = 0; k < pairs.length; k++) {
  const [, i, j] = pairs[k];
  if (union(i, j)) {
    components--;
    if (components === 1) {
      lastEdge = [i, j];
      break;
    }
  }
}
const [i, j] = lastEdge;
const xi = Number(data[i].split(",")[0]);
const xj = Number(data[j].split(",")[0]);
const result = xi * xj;
console.log("Part 2 ->", result);Language:JavaScript

--- Day 7: Laboratories ---

Go to page

Part 1 & 2

const data = $('*').textContent.trim().split('\n');
let beams = 0;
let active = new Set();
let startRow = 0;
let startCol = 0;
rows:for (let r = 0; r < data.length; r++) {
    for (let c = 0; c < data[r].length; c++) {
        if(data[r][c] === 'S') {
            startRow = r;
            startCol = c;
            active.add(c);
            break rows;
        }
    }
}
for (let i = startRow + 1; i < data.length; i++) {
    let next = new Set();
    for(const a of active) {
        const char = data[i][a];
        if(char === '^') {
            beams++;
            if(a - 1 >= 0) next.add(a - 1);
            if(a + 1 < data[i].length) next.add(a + 1);
        } else {
            next.add(a);
        }
    }
    active = next;
}
console.log('Part 1 ->',beams);

let tl = new Map();
tl.set(startCol, 1);
for (let i = startRow + 1; i < data.length; i++) {
    let next = new Map();
    for(const [col, c] of tl.entries()) {
        const char = data[i][col];
        if(char === '^') {
            if(col - 1 >= 0) {
                next.set(col - 1, (next.get(col - 1) || 0) + c);
            }
            if(col + 1 < data[i].length) {
                next.set(col + 1, (next.get(col + 1) || 0) + c);
            }
        } else {
            next.set(col, (next.get(col) || 0) + c);
        }
    }
    tl = next;
}
let result = 0;
for(const c of tl.values()) result += c;
console.log('Part 2 ->',result);Language:JavaScript

--- Day 6: Trash Compactor ---

Go to page

Part 1 & 2

const data = $('*').textContent.trim().split('\n');
    const rows = data.map(line => line.split(''));
    const separators = [];
    for (let i = 0; i < rows[0].length; i++) {
        let allSpaces = true;
        for (let j = 0; j < rows.length; j++) {
            if(rows[j][i] && rows[j][i] !== ' ') {
                allSpaces = false;
                break;
            }
        }
        if(allSpaces) separators.push(i);
    }
    // boundaries for better slicing
    separators.unshift(-1);
    separators.push(rows[0].length);
    
    let total = 0;
    for (let i = 0; i < separators.length - 1; i++) {
        const sc = separators[i] + 1;
        const ec = separators[i+1];
        const pc = [];
        for (let col = sc; col < ec; col++) {
            const column = [];
            for (let row = 0; row < rows.length; row++) {
                column.push(rows[row][col] || ' ');
            }
            pc.push(column);
        }
        const prs = [];
        for (let row = 0; row < rows.length; row++) {
            const pr = [];
            for (let col = 0; col < pc.length; col++) {
                pr.push(pc[col][row]);
            }
            prs.push(pr.join(''));
        }
        const nums = [];
        let op = null;
        for (let row = 0; row < prs.length - 1; row++) {
            const rowStr = prs[row];
            const matches = rowStr.match(/\d+/g);
            if(matches) {
                const num = parseInt(matches[matches.length - 1], 10);
                if (!isNaN(num)) {
                    nums.push(num);
                }
            }
        }
        const last = prs[prs.length-1];
        last.includes('+') ? op = '+' : op = '*';
        if (nums.length > 0 && op) {
            let result;
            op === '+' ?
                result = nums.reduce((sum,num) => sum + num, 0) :
                result = nums.reduce((prod, num) => prod * num, 1);
            total += result;
        }
    }
    console.log('Part 1 ->',total);
    
    total = 0;
    const problems = [];
    let nums = [];
    const ops = [];
    for (let i = rows[0].length-1; i >= 0; i--) {
        let num = '';
        for (let j = 0; j < rows.length; j++) {
            const el = rows[j][i];
            if(!isNaN(el)) num += el.trim();
            else {
                if (el !== undefined) ops.push(el);
            }
        }
        if (num === '') {
            problems.push(nums);
            nums = [];
        } else nums.push(num);
    }
    problems.push(nums);
    
    for (let i = 0; i < problems.length; i++) {
        const p = problems[i].map(Number);
        const op = ops[i];
        let result;
            op === '+' ?
                result = p.reduce((sum,num) => sum + num, 0) :
                result = p.reduce((prod, num) => prod * num, 1);
            total += result;
    }
    
    console.log('Part 2 ->',total);Language:JavaScript

--- Day 5: Printing Department ---

Go to page

Part 1 & 2

const [freshRanges, available] = $('*').textContent.split('\n\n');
const ranges = freshRanges.trim().split('\n');
const merged = [];
let counter = 0;
let counter2 = 0;
available.trim().split('\n').forEach(a => {
    for (let i = 0; i < ranges.length; i++) {
        const r = ranges[i];
        const [s,e] = r.split('-').map(Number);
        if (Number(a) >= s && Number(a) <= e) {
            counter++;
            break;
        }
    }
});

const sortedRanges = ranges
    .map(r => r.split('-').map(Number))
    .sort((a, b) => a[0] - b[0]);
for (let i = 0; i < sortedRanges.length; i++) {
    const s = sortedRanges[i][0];
    const e = sortedRanges[i][1];
    if(!merged.length) {
        merged.push([s,e]);
        continue;
    }
    const last = merged[merged.length-1];
    if(s <= last[1]+1) {
        last[1] = Math.max(last[1], e);
    } else {
        merged.push([s,e]);
    }
}
for(const [s,e] of merged) {
    counter2 += e - s + 1;
}
console.log('Part 1 ->',counter);
console.log('Part 2 ->',counter2);Language:JavaScript

--- Day 4: Printing Department ---

Go to page

Part 1 & 2

let data = $('*').textContent.trim().split('\n');
const moves = [
    [-1,-1], [-1,0], [-1,1],
    [0,-1], [0,1],
    [1,-1], [1,0], [1,1]
];
let counter = 0;
data.forEach((d,i) => {
    for (let j = 0; j < d.length; j++) {
        const cell = d[j];
        let count = 0;
        if (cell === '@') {
            for(const [y,x] of moves) {
                const nx = j + x;
                const ny = i + y;
                if (data[ny] !== undefined && data[ny][nx] !== undefined) {
                    if (data[ny][nx] === '@') count++;
                }
            }
            if (count < 4) counter++;
        }
        else continue;
    } 
});
console.log('Part 1 ->',counter);

counter = 0;
let pass = 1;
while(pass > 0) {
    pass = 0;
    let grid = [];
    data.forEach((d,i) => {
        let line = [];
        for (let j = 0; j < d.length; j++) {
            let cell = d[j];
            let count = 0;
            if (cell === '@') {
                for(const [y,x] of moves) {
                    const nx = j + x;
                    const ny = i + y;
                    if (data[ny] !== undefined && data[ny][nx] !== undefined) {
                        if (data[ny][nx] === '@') count++;
                    }
                }
                if (count < 4) {
                    pass++;
                    counter++;
                    cell = '.';
                }
            }
            line.push(cell); 
        }
        grid.push(line);
    });
    data = grid;
}
console.log('Part 2 ->',counter);Language:JavaScript

--- Day 3: Lobby ---

Go to page

Part 1 & 2

const data = $('*').textContent.trim().split('\n');
let sum = 0;
data.forEach(d => {
    let battery = '';
    let n = 0;
    let index = 0;
    for(let i = 0; i < d.length-1; i++) {
        const num = Number(d[i]);
        if (n < num) {
            n = num;
            index = i;
        }
    }
    battery += n;
    n = 0;
    for(let j = index+1; j < d.length; j++) {
        const num = Number(d[j]);
        if (n < num) {
            n = num;
        }
    }
    battery += n;
    sum += Number(Number(battery));
});
console.log('Part 1 ->',sum);
sum = 0;
data.forEach(d => {
    let battery = '';
    let index = 0;
    for(let k = 11; k >= 0; k--) {
        let n = 0;
        for(let i = index; i < d.length-k; i++) {
            const num = Number(d[i]);
            if (n < num) {
                n = num;
                index = i+1;
            }
        }
        battery += n;
    }
    sum += Number(Number(battery));
});
console.log('Part 2 ->',sum);Language:JavaScript

--- Day 2: Gift Shop ---

Go to page

Part 1 & 2

const data = $('*').textContent.trim().split('\n');
const IDs = [];
let sum = 0;
let sum2 = 0;
data.forEach(d=> {
    const ranges = d.split(',');
    for (const range of ranges) {
        IDs.push(range);
    }
});

for (const id of IDs) {
    const [first, last] = id.split('-');
    for (let i = Number(first); i <= Number(last); i++) {
        const str = String(i);
        if (str.length % 2 === 0) {
if (str.substring(0,str.length/2) === str.substring(str.length/2)) {
                sum += i;
            }
        }
    }
}
console.log('Part 1 ->',sum);

for (const id of IDs) {
    const [first, last] = id.split('-');
    for (let i = Number(first); i <= Number(last); i++) {
        const str = String(i);
        for(let j = 1; j <= str.length/2; j++) {
            const p = str.substring(0,j);
            const r = str.length / j;
            if(Number.isInteger(r)) {
                if(p.repeat(r) === str) {
                    sum2 += i;
                    break;
                }
            }
        }
    }
}
console.log('Part 2 ->',sum2);Language:JavaScript

--- Day 1: Secret Entrance ---

Go to page

Part 1 & 2

const data = $('*').textContent.split('\n').slice(0,-1);
let pos = 50;
let counter = 0;
let counter2 = 0;
data.forEach(d=> {
    const n = Number(d.slice(1));
    for (let i = 0; i < n; i++) {
        d.startsWith('L') ? pos -- : pos++;
        if (pos < 0 || pos > 99) {
            pos = ((pos % 100) + 100) % 100;
        }
        if (pos === 0) counter2++;
    }
    if (pos === 0) counter++;
});
console.log('Part 1 ->', counter);
console.log('Part 2 ->', counter2);Language:JavaScript