Home

AdventJs 2022

Article created on 1 dic 2022 and updated on 7 dic 2022

AdventJs 2022

AdventJs is another coding challenge for this Christmas. This time coming from Midudev

I will be posting the solutions in this same post and in a Github repo

Day 1

function wrapping(gifts) {
    return gifts.map((gift) => {
        let w = "*".repeat(gift.length + 2)
        return `${w}\n*${gift}*\n${w}`
    })
}

Day 2

function countHours(year, holidays) {
    return holidays
        .map((day) => new Date(`${year}/${day}`).getDay())
        .filter((day) => day > 0 && day < 6)
        .map((day) => 2)
        .reduce((acc, cur) => acc + cur, 0)
}

Day 3

function distributeGifts(packOfGifts, reindeers) {
    return Math.floor(
        reindeers.reduce((acc, cur) => {
            return acc + cur.length * 2
        }, 0) /
            packOfGifts.reduce((acc, cur) => {
                return acc + cur.length
            }, 0)
    )
}

Day 4

function fitsInOneBox(boxes) {
    return boxes
        .sort((a, b) => a.l - b.l)
        .every((box, i) => {
            if (i === 0) return true
            const prevBox = boxes[i - 1]
            return box.l > prevBox.l && box.w > prevBox.w && box.h > prevBox.h
        })
}

Day 5

function getMaxGifts(giftsCities, maxGifts, maxCities) {
    if (maxCities === 0) return 0
    const allGifts = giftsCities.reduce((acc, cur) => acc + cur, 0)
    return giftsCities.reduce((acc, giftsCity, idx) => {
        if (giftsCity > maxGifts) return acc
        const giftsCitiesClone = [...giftsCities]
        giftsCitiesClone.splice(idx, 1)
        const res =
            getMaxGifts(giftsCitiesClone, maxGifts - giftsCity, maxCities - 1) +
            giftsCity
        if (allGifts === res) {
            giftsCities.splice(1)
        }
        return res > acc ? res : acc
    }, 0)
}

Day 6

function createCube(size) {
    let figure = ""
    for (let i = 1; i <= size; i++) {
        figure +=
            " ".repeat(size - i) + `\/\\`.repeat(i) + `_\\`.repeat(size) + "\n"
    }
    for (let i = size; i > 0; i--) {
        figure +=
            " ".repeat(size - i) + `\\\/`.repeat(i) + `_\/`.repeat(size) + "\n"
    }
    return figure.slice(0, -1)
}

Day 7

function getGiftsToRefill(a1, a2, a3) {
    const allGifts = [...new Set([...a1, ...a2, ...a3])]
    return allGifts.filter(
        (gift) => a1.includes(gift) + a2.includes(gift) + a3.includes(gift) < 2
    )
}

Day 8

function checkPart(part) {
    if (part === [...part].reverse().join("")) {
        return true
    }

    for (let i = 0; i < [...part].length; i++) {
        const candidate = [...part]
        candidate.splice(i, 1)
        if (candidate.join("") === candidate.reverse().join("")) {
            return true
        }
    }
    return false
}

Day 9

function countTime(leds) {
    const arr = leds.join("").split("1")
    arr[0] += arr.splice(-1)
    return Math.max(...arr.map((el) => el.length)) * 7
}

Day 10

function checkJump(heights) {
    const peak = Math.max(...heights)
    const peakIndex = heights.indexOf(peak)
    const left = heights.slice(0, peakIndex)
    const right = heights.slice(peakIndex + 1)
    const isGoingUp = left.slice(1).every((l, i) => l >= left[i])
    const isGoingDown = right.slice(1).every((r, i) => r <= right[i])
    return isGoingUp && isGoingDown && left.length !== 0 && right.length !== 0
}

Day 11

function getCompleted(part, total) {
    const getSeconds = (time) => time[0] * 3600 + time[1] * 60 + time[2]
    const getHCF = (a, b) => (b == 0 ? a : getHCF(b, a % b))
    const partTime = part.split(":").map(Number)
    const totalTime = total.split(":").map(Number)
    const partSeconds = getSeconds(partTime)
    const totalSeconds = getSeconds(totalTime)
    const hcf = getHCF(partSeconds, totalSeconds)
    return `${partSeconds / hcf}/${totalSeconds / hcf}`
}

Day 12

function selectSleigh(distance, sleighs) {
    const selection = sleighs.filter((s) => s.consumption * distance <= 20)
    return selection.length > 0 ? selection[selection.length - 1].name : null
}

Day 13

function getFilesToBackup(lastBackup, changes) {
    const result = new Set()
    changes
        .filter((change) => change[1] > lastBackup)
        .map((change) => change[0])
        .sort((a, b) => a - b)
        .forEach((file) => result.add(file))
    return [...result]
}

Day 14

function getOptimalPath(path) {
    let result = path.reduce((acc, value) => {
        let sliceStart = 0
        return acc
            .map((n) => {
                let tmp0 = value
                    .slice(sliceStart, sliceStart + 2)
                    .map((l) => l + n)
                sliceStart += 1
                return tmp0
            })
            .flat()
    })
    return Math.min(...result)
}

Day 15

function decorateTree(base) {
    const rules = {
        PP: "P",
        BB: "B",
        RR: "R",
        PB: "R",
        BP: "R",
        PR: "B",
        RP: "B",
        BR: "P",
        RB: "P"
    }
    const decorate = [base]
    base = base.split(" ")
    let height = base.length - 1
    for (let i = 0; i < height; i++) {
        const newBase = []
        for (let j = 0; j < base.length - 1; j++) {
            newBase.push(rules[base[j] + base[j + 1]])
        }
        base = newBase
        decorate.unshift(newBase.join(" "))
    }
    return decorate
}

Day 16

function fixLetter(letter) {
    return letter
        .trim()
        .replace(/,/g, ", ")
        .replace(/\s+\./g, ".")
        .replace(/\s+/g, " ")
        .replace(/[??]+/g, "?")
        .replace(/(\s+)([,.?!])+/g, (m, s1, s2) => s2)
        .replace(/^([A-z])/, (m, p) => p.toUpperCase())
        .replace(/([?.!])\s+(\w)/g, (m, s1, s2) => `${s1} ${s2.toUpperCase()}`)
        .replace(/santa claus/gi, "Santa Claus")
        .replace(/([A-z])$/, (m, s) => `${s}.`)
}

Day 17

function carryGifts(gifts, maxWeight) {
    if (gifts.some((gift) => gift.length > maxWeight)) {
        return []
    }
    const bags = []
    let tempBag = []
    let tempWeight = 0
    gifts.forEach((gift) => {
        const weight = gift.length
        // if the gift fits in the bag
        if (tempWeight + weight <= maxWeight) {
            tempBag.push(gift)
            tempWeight += weight
        } else {
            // if the gift does not fit in the bag
            bags.push(tempBag.join(" "))
            tempBag = [gift]
            tempWeight = weight
        }
    })
    if (tempBag.length > 0) {
        bags.push(tempBag.join(" "))
    }
    return bags
}

Day 18

function dryNumber(dry, numbers) {
    numbers = Array.from({ length: numbers }, (_, i) => i + 1)
    return numbers.reduce((acc, curr) => {
        if (curr.toString().includes(dry.toString())) {
            acc.push(curr)
        }
        return acc
    }, [])
}

Day 19

function sortToys(toys, positions) {
    let temp = []
    toys.forEach((toy, index) => {
        const position = positions[index]
        temp.push([position, toy])
    })
    return temp.sort((a, b) => a[0] - b[0]).map((el) => el[1])
}

Day 20

function howManyReindeers(reindeerTypes, gifts) {
    reindeerTypes.sort((a, b) => a.weightCapacity - b.weightCapacity)

    return gifts.map((gift) => {
        let cityWeight = gift.weight
        let reindeers = reindeerTypes.filter(
            (reindeer) => reindeer.weightCapacity < gift.weight
        )
        let team = {}
        while (cityWeight != 0) {
            reindeers.map((reindeerType) => {
                if (cityWeight - reindeerType.weightCapacity >= 0) {
                    team[reindeerType.type]
                        ? (team[reindeerType.type] += 1)
                        : (team[reindeerType.type] = 1)
                    cityWeight -= reindeerType.weightCapacity
                }
            })
        }
        return {
            country: gift.country,
            reindeers: reindeers
                .map((y) => {
                    return {
                        type: y.type,
                        num: team[y.type]
                    }
                })
                .reverse()
        }
    })
}

Day 21

function printTable(gifts) {
    const giftColumnWidths = gifts.map((gift) => gift.name.length)
    const quantityColumnWidths = gifts.map(
        (gift) => gift.quantity.toString().length
    )
    const maxGiftCol = Math.max(...giftColumnWidths, "Gift".length)
    const maxQuantityCol = Math.max(...quantityColumnWidths, "Quantity".length)

    const [header, footer] = ["+", "*"].map((char) =>
        char.repeat(maxGiftCol + maxQuantityCol + 7)
    )
    const title = `| Gift${" ".repeat(maxGiftCol - 3)}| Quantity${" ".repeat(
        maxQuantityCol - 7
    )}|`

    const separator = `| ${"-".repeat(maxGiftCol)} | ${"-".repeat(
        maxQuantityCol
    )} |`

    const content = gifts.map(
        (gift) =>
            `| ${gift.name}${" ".repeat(maxGiftCol - gift.name.length)} | ${
                gift.quantity
            }${" ".repeat(maxQuantityCol - gift.quantity.toString().length)} |`
    )

    return [header, title, separator, ...content, footer].join("\n")
}

Day 22

function checkStepNumbers(systemNames, stepNumbers) {
    const systems = {}
    systemNames.forEach((systemName, index) => {
        if (systems[systemName]) {
            systems[systemName].push(stepNumbers[index])
        } else {
            systems[systemName] = [stepNumbers[index]]
        }
    })
    for (const [key, value] of Object.entries(systems)) {
        for (let i = 0; i < value.length - 1; i++) {
            if (value[i] >= value[i + 1]) {
                return false
            }
        }
    }
    return true
}

Day 23

function executeCommands(commands) {
    const reg = [0, 0, 0, 0, 0, 0, 0, 0]

    const getInt = (arg) => {
        if (arg === undefined) {
            return undefined
        }
        if (arg.startsWith("V")) {
            return parseInt(arg.slice(-1))
        } else {
            return parseInt(arg, 10)
        }
    }

    let pointer = 0
    while (pointer < commands.length) {
        const command = commands[pointer]
        const [instruction, args] = command.split(" ")
        const [arg1, arg2] = args.split(",")
        const i1 = getInt(arg1)
        const i2 = getInt(arg2)

        switch (instruction) {
            case "MOV":
                if (arg1.startsWith("V") && arg2.startsWith("V")) {
                    reg[i2] = reg[i1]
                } else {
                    reg[i2] = i1
                }
                break
            case "ADD":
                reg[i1] += reg[i2]
                reg[i1] %= 256
                break
            case "DEC":
                if (reg[i1] === 0) {
                    reg[i1] = 255
                } else {
                    reg[i1] -= 1
                }
                break
            case "INC":
                if (reg[i1] === 255) {
                    reg[i1] = 0
                } else {
                    reg[i1] += 1
                }
                break
            case "JMP":
                if (reg[0] !== 0) {
                    pointer = i1 - 1
                }
                break
            // default:
            //   throw new Error(`Unknown instruction: ${instruction}`)
        }
        pointer += 1
    }

    return reg
}

Day 24

function canExit(maze) {
    // Get the starting position
    let start
    for (let i = 0; i < maze.length; i++) {
        for (let j = 0; j < maze[i].length; j++) {
            if (maze[i][j] === "S") {
                start = [i, j]
                break
            }
        }
    }

    // Initialize the queue with the starting position
    const queue = [start]
    // Initialize a set to store the visited positions
    const visited = new Set()

    // Define the directions for the movements
    const directions = [
        [-1, 0],
        [1, 0],
        [0, -1],
        [0, 1]
    ]

    // Iterate through the queue
    while (queue.length > 0) {
        // Get the first position from the queue
        const [x, y] = queue.shift()
        // If the position is the exit, return true
        if (maze[x][y] === "E") {
            return true
        }
        // Mark the position as visited
        visited.add(`${x},${y}`)
        // Iterate through the directions
        for (const [dx, dy] of directions) {
            // Calculate the next position
            const i = x + dx
            const j = y + dy
            // Check if the next position is valid
            if (
                i >= 0 &&
                i < maze.length &&
                j >= 0 &&
                j < maze[i].length &&
                maze[i][j] !== "W" &&
                !visited.has(`${i},${j}`)
            ) {
                // Add the next position to the queue
                queue.push([i, j])
            }
        }
    }

    // Return false if there is no exit
    return false
}