├── 2021 ├── challenge-01.js ├── challenge-02.js ├── challenge-03.js ├── challenge-04.js ├── challenge-05.js ├── challenge-06.js ├── challenge-07.js ├── challenge-08.js ├── challenge-09.js ├── challenge-10.js ├── challenge-11.js ├── challenge-12.js ├── challenge-13.js ├── challenge-14.js ├── challenge-15.js ├── challenge-16.js ├── challenge-17.js ├── challenge-18.js ├── challenge-19.js ├── challenge-20.js ├── challenge-21.js ├── challenge-22.js ├── challenge-23.js ├── challenge-24.js ├── challenge-25.js └── challenge-26.js ├── 2022 ├── .gitignore ├── 2022.png ├── challenge-01 │ ├── README.md │ ├── main.test.ts │ └── main.ts ├── challenge-02 │ ├── README.md │ ├── main.test.ts │ └── main.ts ├── challenge-03 │ ├── README.md │ ├── main.test.ts │ └── main.ts ├── challenge-04 │ ├── README.md │ ├── main.test.ts │ └── main.ts ├── challenge-05 │ ├── README.md │ ├── main.test.ts │ └── main.ts ├── challenge-06 │ ├── README.md │ ├── main.test.ts │ └── main.ts ├── challenge-07 │ ├── README.md │ ├── main.test.ts │ └── main.ts ├── challenge-08 │ ├── README.md │ ├── main.test.ts │ └── main.ts ├── challenge-09 │ ├── README.md │ ├── main.test.ts │ └── main.ts ├── challenge-10 │ ├── README.md │ ├── main.test.ts │ └── main.ts ├── challenge-11 │ ├── README.md │ ├── main.test.ts │ └── main.ts ├── challenge-12 │ ├── README.md │ ├── main.test.ts │ └── main.ts ├── challenge-13 │ ├── README.md │ ├── main.test.ts │ └── main.ts ├── challenge-14 │ ├── README.md │ ├── main.test.ts │ └── main.ts ├── challenge-15 │ ├── README.md │ ├── main.test.ts │ └── main.ts ├── challenge-16 │ ├── README.md │ ├── main.test.ts │ └── main.ts ├── challenge-17 │ ├── README.md │ ├── main.test.ts │ └── main.ts ├── challenge-18 │ ├── README.md │ ├── main.test.ts │ └── main.ts ├── challenge-19 │ ├── README.md │ ├── main.test.ts │ └── main.ts ├── challenge-20 │ ├── README.md │ ├── main.test.ts │ └── main.ts ├── challenge-21 │ ├── README.md │ ├── main.test.ts │ └── main.ts ├── challenge-22 │ ├── README.md │ ├── main.test.ts │ └── main.ts ├── challenge-23 │ ├── README.md │ ├── main.test.ts │ └── main.ts ├── challenge-24 │ ├── README.md │ ├── main.test.ts │ └── main.ts ├── deno.json └── importMap.json ├── 2023 ├── .gitignore ├── 2023.png ├── challenge-01 │ ├── README.md │ ├── main.test.ts │ └── main.ts ├── challenge-02 │ ├── README.md │ ├── main.test.ts │ └── main.ts ├── challenge-03 │ ├── README.md │ ├── main.test.ts │ └── main.ts ├── challenge-04 │ ├── README.md │ ├── main.test.ts │ └── main.ts ├── challenge-05 │ ├── README.md │ ├── main.test.ts │ └── main.ts ├── challenge-06 │ ├── README.md │ ├── main.test.ts │ └── main.ts ├── challenge-07 │ ├── README.md │ ├── main.test.ts │ └── main.ts ├── challenge-08 │ ├── README.md │ ├── main.test.ts │ └── main.ts ├── challenge-09 │ ├── README.md │ ├── main.test.ts │ └── main.ts ├── challenge-10 │ ├── README.md │ ├── main.test.ts │ └── main.ts ├── challenge-11 │ ├── README.md │ ├── main.test.ts │ └── main.ts ├── challenge-12 │ ├── README.md │ ├── main.test.ts │ └── main.ts ├── challenge-13 │ ├── README.md │ ├── main.test.ts │ └── main.ts ├── challenge-14 │ ├── README.md │ ├── main.test.ts │ └── main.ts ├── challenge-15 │ ├── README.md │ ├── main.test.ts │ └── main.ts ├── challenge-16 │ ├── README.md │ ├── main.test.ts │ └── main.ts ├── challenge-17 │ ├── README.md │ ├── main.test.ts │ └── main.ts ├── challenge-18 │ ├── README.md │ ├── main.test.ts │ └── main.ts ├── challenge-19 │ ├── README.md │ ├── main.test.ts │ └── main.ts ├── challenge-20 │ ├── README.md │ ├── main.test.ts │ └── main.ts ├── challenge-21 │ ├── README.md │ ├── main.test.ts │ └── main.ts ├── challenge-22 │ ├── README.md │ ├── main.test.ts │ └── main.ts ├── challenge-23 │ ├── README.md │ ├── main.test.ts │ └── main.ts ├── challenge-24 │ ├── README.md │ ├── main.test.ts │ └── main.ts ├── challenge-25 │ ├── README.md │ ├── main.test.ts │ └── main.ts ├── deno.json └── importMap.json ├── 2024 ├── challenge-01 │ ├── README.md │ ├── main.py │ ├── mod.js │ └── mod.ts ├── challenge-02 │ ├── README.md │ ├── mod.js │ ├── mod.py │ └── mod.ts ├── challenge-03 │ ├── README.md │ ├── mod.js │ ├── mod.py │ └── mod.ts ├── challenge-04 │ ├── README.md │ ├── mod.js │ ├── mod.py │ └── mod.ts ├── challenge-05 │ ├── README.md │ ├── mod.js │ ├── mod.py │ └── mod.ts ├── challenge-06 │ ├── README.md │ ├── mod.js │ ├── mod.py │ └── mod.ts ├── challenge-07 │ ├── README.md │ ├── mod.js │ ├── mod.py │ └── mod.ts ├── challenge-08 │ ├── README.md │ ├── mod.js │ ├── mod.py │ └── mod.ts ├── challenge-09 │ ├── README.md │ ├── mod.js │ ├── mod.py │ └── mod.ts ├── challenge-10 │ ├── README.md │ ├── mod.js │ ├── mod.py │ └── mod.ts ├── challenge-11 │ ├── README.md │ ├── mod.js │ ├── mod.py │ └── mod.ts ├── challenge-12 │ ├── README.md │ ├── mod.js │ ├── mod.py │ └── mod.ts ├── challenge-13 │ ├── README.md │ ├── mod.js │ ├── mod.py │ └── mod.ts ├── challenge-14 │ ├── README.md │ ├── mod.js │ ├── mod.py │ └── mod.ts ├── challenge-15 │ ├── README.md │ ├── mod.js │ ├── mod.py │ └── mod.ts ├── challenge-16 │ ├── README.md │ ├── mod.js │ ├── mod.py │ └── mod.ts ├── challenge-17 │ ├── README.md │ ├── mod.js │ ├── mod.py │ └── mod.ts ├── challenge-18 │ ├── README.md │ ├── mod.js │ ├── mod.py │ └── mod.ts ├── challenge-19 │ ├── README.md │ ├── mod.js │ ├── mod.py │ └── mod.ts ├── challenge-20 │ ├── README.md │ ├── mod.js │ ├── mod.py │ └── mod.ts ├── challenge-21 │ ├── README.md │ ├── mod.js │ ├── mod.py │ └── mod.ts ├── challenge-22 │ ├── README.md │ ├── mod.js │ ├── mod.py │ └── mod.ts ├── challenge-23 │ ├── README.md │ ├── mod.js │ ├── mod.py │ └── mod.ts ├── challenge-24 │ ├── README.md │ ├── mod.js │ ├── mod.py │ └── mod.ts ├── challenge-25 │ ├── README.md │ ├── mod.js │ ├── mod.py │ └── mod.ts └── challenge-26 │ ├── README.md │ ├── mod.js │ ├── mod.py │ └── mod.ts ├── .gitignore ├── .vscode ├── extensions.json └── settings.json └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | # Deno 2 | deno.lock 3 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "ampcpmgp.cognitive-complexity-show", 4 | "denoland.vscode-deno" 5 | ] 6 | } -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "deno.enable": true 3 | } -------------------------------------------------------------------------------- /2021/challenge-01.js: -------------------------------------------------------------------------------- 1 | /** @typedef {{ name: string, color: string }} Oveja */ 2 | 3 | /** 4 | * @param { Oveja[] } ovejas 5 | * @returns { Oveja[] } 6 | */ 7 | export function contarOvejas(ovejas) { 8 | return ovejas.filter( 9 | ({ color, name }) => color === 'rojo' 10 | && name.toLowerCase().includes('a') 11 | && name.toLowerCase().includes('n') 12 | ) 13 | } 14 | 15 | -------------------------------------------------------------------------------- /2021/challenge-02.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {string} letter 3 | * @returns {{ [key: string]: number }} 4 | */ 5 | export default function listGifts(letter) { 6 | return letter.split(" ").reduce( 7 | (prev, curr) => curr.length && curr[0] !== "_" 8 | ? { ...prev, [curr]: (prev[curr] || 0) + 1 } 9 | : prev, 10 | {}, 11 | ) 12 | } 13 | -------------------------------------------------------------------------------- /2021/challenge-03.js: -------------------------------------------------------------------------------- 1 | export default function isValid(letter) { 2 | for (const str of ['{', '}', '[', ']']) if (letter.includes(str)) 3 | return false; 4 | 5 | const containers = {} 6 | const stack = []; 7 | 8 | letter.split('').forEach((char, i) => { 9 | if (char === ')') containers[i] = stack.pop(); 10 | if (char === '(') stack.push(i); 11 | }) 12 | 13 | for (const value in containers) 14 | if (!letter.substring(containers[value] + 1, +value)) 15 | return false; 16 | 17 | return !stack.length 18 | } 19 | -------------------------------------------------------------------------------- /2021/challenge-04.js: -------------------------------------------------------------------------------- 1 | /** @param {number} height */ 2 | /** @returns {string} */ 3 | export default function createXmasTree(height) { 4 | let tree = ''; 5 | 6 | for (let i = 0; i < height; i++) { 7 | tree += "_".repeat(height - i - 1) + "*".repeat(i * 2 + 1) + "_".repeat(height - i - 1) + "\n"; 8 | } 9 | 10 | tree += "_".repeat(height - 1 / 2) + "#".repeat(1) + "_".repeat(height - 1 / 2) + "\n" 11 | tree += "_".repeat(height - 1 / 2) + "#".repeat(1) + "_".repeat(height - 1 / 2) 12 | 13 | return tree 14 | } 15 | -------------------------------------------------------------------------------- /2021/challenge-05.js: -------------------------------------------------------------------------------- 1 | /** @param {Date} date */ 2 | export default (date) => Math.ceil((new Date('Dec 25, 2021') - date) / (1000 * 60 * 60 * 24)); 3 | -------------------------------------------------------------------------------- /2021/challenge-06.js: -------------------------------------------------------------------------------- 1 | export default function sumPairs(numbers, result) { 2 | for (let i = 0; i < numbers.length; i++) 3 | for (let j = i + 1; j < numbers.length; j++) 4 | if (numbers[i] + numbers[j] === result) 5 | return [numbers[i], numbers[j]]; 6 | 7 | return null; 8 | } 9 | -------------------------------------------------------------------------------- /2021/challenge-07.js: -------------------------------------------------------------------------------- 1 | /** @typedef {{ [key: string]: Product | string }} Product */ 2 | 3 | /** 4 | * @param {Product} store 5 | * @param {string} product 6 | * @returns {boolean} 7 | */ 8 | export default function contains(store, product) { 9 | return Object.values(store).some((value) => typeof value === "object" 10 | ? contains(value, product) 11 | : value === product 12 | ) 13 | } 14 | -------------------------------------------------------------------------------- /2021/challenge-08.js: -------------------------------------------------------------------------------- 1 | /** @param {number[]} prices */ 2 | export default function maxProfit(prices) { 3 | let minPrice = prices[0], maxProfit = 0; 4 | 5 | // 1: 18 6 | for (let i = 0; i < prices.length; i++) { 7 | if (prices[i] < minPrice) minPrice = prices[i]; 8 | 9 | if (prices[i] - minPrice > maxProfit) 10 | maxProfit = prices[i] - minPrice; 11 | } 12 | 13 | return maxProfit || - 1; 14 | } 15 | -------------------------------------------------------------------------------- /2021/challenge-09.js: -------------------------------------------------------------------------------- 1 | export default function groupBy(collection, it) { 2 | const content = {}; 3 | 4 | for (let i = 0; i < collection.length; i++) { 5 | const key = typeof it === 'function' 6 | ? it(collection[i]) 7 | : collection[i][it]; 8 | 9 | content[key] 10 | ? content[key].push(collection[i]) 11 | : content[key] = [collection[i]]; 12 | } 13 | 14 | return content 15 | } 16 | -------------------------------------------------------------------------------- /2021/challenge-10.js: -------------------------------------------------------------------------------- 1 | const coins = { 2 | 0: 1, 3 | 1: 2, 4 | 2: 5, 5 | 3: 10, 6 | 4: 20, 7 | 5: 50, 8 | } 9 | 10 | export default function getCoins(change) { 11 | const result = []; 12 | 13 | for (let i = 5; i >= 0; i--) { 14 | let coin = 0; 15 | 16 | while (change >= coins[i]) { 17 | coin++; 18 | change -= coins[i]; 19 | } 20 | 21 | result.push(coin) 22 | } 23 | 24 | return result.reverse() 25 | } 26 | -------------------------------------------------------------------------------- /2021/challenge-11.js: -------------------------------------------------------------------------------- 1 | export default function shouldBuyFidelity(times) { 2 | const price = 12; 3 | let cost = 0; 4 | 5 | for (let i = 1; i < times + 1; i++) { 6 | cost += price * 0.75 ** i; 7 | } 8 | 9 | return times * price > 250 + cost 10 | } 11 | -------------------------------------------------------------------------------- /2021/challenge-12.js: -------------------------------------------------------------------------------- 1 | export default function getMinJump(obstacles) { 2 | let minJump = 1; 3 | 4 | for (let i = 1; i < obstacles.length + 1; i++) 5 | if (obstacles.includes(minJump * i)) 6 | minJump++ && (i = 0); 7 | 8 | return minJump 9 | } 10 | -------------------------------------------------------------------------------- /2021/challenge-13.js: -------------------------------------------------------------------------------- 1 | export default function wrapGifts(gifts) { 2 | if (gifts.length === 0) return []; 3 | 4 | const gift_box = '*'.repeat(2 + gifts[0].length); 5 | const wrappedGifts = [] 6 | 7 | wrappedGifts.push(gift_box, gift_box); 8 | 9 | for (let i = 0; i < gifts.length; i++) wrappedGifts.splice( 10 | wrappedGifts.length - 1, 0, `*${gifts[i]}*` 11 | ) 12 | 13 | return wrappedGifts; 14 | } 15 | -------------------------------------------------------------------------------- /2021/challenge-14.js: -------------------------------------------------------------------------------- 1 | export default function missingReindeer(ids) { 2 | for (let i = 0; i < ids.length + 1; i++) 3 | if (!ids.includes(i)) return i; 4 | 5 | return -1; 6 | } 7 | -------------------------------------------------------------------------------- /2021/challenge-15.js: -------------------------------------------------------------------------------- 1 | export default function checkSledJump(heights) { 2 | for (let i = 0; i < heights.length; i++) { 3 | const first = heights[i], second = heights[i + 1]; 4 | 5 | if (first === second) return false; 6 | 7 | if (first > second) { 8 | for (let j = (i + 1); j < heights.length; j++) { 9 | if (heights[i] < heights[j]) return false; 10 | 11 | i++ 12 | } 13 | 14 | return true 15 | } 16 | 17 | } 18 | 19 | return false; 20 | } 21 | -------------------------------------------------------------------------------- /2021/challenge-16.js: -------------------------------------------------------------------------------- 1 | const symbol_codes = { 2 | '.': 1, 3 | ',': 5, 4 | ':': 10, 5 | ';': 50, 6 | '!': 100 7 | } 8 | 9 | export default function decodeNumber(symbols) { 10 | let result = 0; 11 | 12 | for (let i = 0; i < symbols.length; i++) { 13 | if (!(symbols[i] in symbol_codes)) return NaN; 14 | 15 | const curr = symbol_codes[symbols[i]]; 16 | 17 | curr < (symbol_codes[symbols[i + 1]] || curr) 18 | ? result -= curr 19 | : result += curr; 20 | 21 | } 22 | 23 | return result 24 | } 25 | -------------------------------------------------------------------------------- /2021/challenge-17.js: -------------------------------------------------------------------------------- 1 | export default function countPackages(carriers, carrierID) { 2 | for (const [name, value, sub_carriers] of carriers) { 3 | if (name !== carrierID) continue 4 | 5 | let result = value; 6 | 7 | for (const carrier_id of sub_carriers) 8 | result += countPackages(carriers, carrier_id); 9 | 10 | return result 11 | } 12 | 13 | return -1 14 | } 15 | -------------------------------------------------------------------------------- /2021/challenge-18.js: -------------------------------------------------------------------------------- 1 | export default function fixFiles(files) { 2 | const elements = {} 3 | 4 | files.forEach((str, i) => { 5 | elements[str] ? elements[str]++ : elements[str] = 1 6 | 7 | if (elements[str] > 1) 8 | files[i] = `${str}(${elements[str] - 1})` 9 | 10 | }) 11 | 12 | return files 13 | } 14 | -------------------------------------------------------------------------------- /2021/challenge-19.js: -------------------------------------------------------------------------------- 1 | export default function learn(time, courses) { 2 | let result = [0]; 3 | 4 | for (let i = 0; i < courses.length; i++) { 5 | for (let j = i + 1; j < courses.length; j++) { 6 | if (courses[i] + courses[j] == time) return [i, j] 7 | 8 | if ( 9 | courses[i] + courses[j] > result[0] && 10 | courses[i] + courses[j] < time 11 | ) result = [courses[i] + courses[j], i, j]; 12 | 13 | } 14 | 15 | } 16 | 17 | return result.length > 2 ? result.slice(1) : null; 18 | } 19 | -------------------------------------------------------------------------------- /2021/challenge-20.js: -------------------------------------------------------------------------------- 1 | const dieresis_accent = { 2 | 'á': 'a', 3 | 'é': 'e', 4 | 'í': 'i', 5 | 'ó': 'o', 6 | 'ú': 'u', 7 | 'ü': 'u', 8 | 'ä': 'a', 9 | 'ë': 'e', 10 | 'ï': 'i', 11 | 'ö': 'o', 12 | 'ü': 'u', 13 | } 14 | 15 | export default function pangram(letter) { 16 | let alpha = new Set("abcdefghijklmñnopqrstuvwxyz") 17 | 18 | for (const key of Object.keys(dieresis_accent)) 19 | letter = letter.toLowerCase().replace(key, dieresis_accent[key]) 20 | 21 | for (let c of letter.toLowerCase()) alpha.delete(c) 22 | 23 | return alpha.size == 0 24 | } 25 | -------------------------------------------------------------------------------- /2021/challenge-21.js: -------------------------------------------------------------------------------- 1 | export default function canCarry(capacity, trip) { 2 | let occupied = 0, route = {} 3 | 4 | for (const [amount, filling, _] of trip) 5 | route[filling] = amount 6 | 7 | for (const [amount, _, delivery] of trip) 8 | route[delivery] = route[delivery] 9 | ? route[delivery] - amount 10 | : -amount 11 | 12 | for (const [_, value] of Object.entries(route)) { 13 | if (occupied > capacity) return false 14 | occupied += value 15 | } 16 | 17 | return true 18 | } 19 | -------------------------------------------------------------------------------- /2021/challenge-22.js: -------------------------------------------------------------------------------- 1 | /** @typedef {{ value: number, left: Tree | null right: Tree | null }} Tree */ 2 | 3 | /** 4 | * @param {Tree} bigTree 5 | * @returns {number} 6 | */ 7 | export default function countDecorations(bigTree) { 8 | return bigTree.value 9 | + (bigTree.right ? countDecorations(bigTree.right) : 0) 10 | + (bigTree.left ? countDecorations(bigTree.left) : 0) 11 | } 12 | -------------------------------------------------------------------------------- /2021/challenge-23.js: -------------------------------------------------------------------------------- 1 | export default function canReconfigure(from, to) { 2 | const reconfigure = {}; 3 | 4 | if (from.length !== to.length) return false 5 | 6 | for (let i = 0; i < from.length; i++) { 7 | if ( 8 | Object.keys(reconfigure).includes(from[i]) && 9 | reconfigure[from[i]] !== to[i] 10 | ) return false 11 | 12 | if ( 13 | Object.values(reconfigure).includes(to[i]) && 14 | !Object.keys(reconfigure).includes(from[i]) 15 | ) return false 16 | 17 | reconfigure[from[i]] = to[i]; 18 | } 19 | 20 | return true 21 | } 22 | -------------------------------------------------------------------------------- /2021/challenge-24.js: -------------------------------------------------------------------------------- 1 | /** @typedef {{ value: number, left: Tree | null right: Tree | null }} Tree */ 2 | 3 | /** 4 | * @param {Tree} bigTree 5 | * @returns {number} 6 | */ 7 | function countDecorations(bigTree) { 8 | return bigTree.value 9 | + (bigTree.right ? countDecorations(bigTree.right) : 0) 10 | + (bigTree.left ? countDecorations(bigTree.left) : 0) 11 | } 12 | 13 | export default function checkIsSameTree(treeA, treeB) { 14 | return countDecorations(treeA) === countDecorations(treeB) 15 | } 16 | -------------------------------------------------------------------------------- /2021/challenge-25.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {(' ' | 'm' | '*')[][]} game 3 | * @param {string} direction 4 | */ 5 | export default function canMouseEat(direction, game) { 6 | // ¡Gracias por jugar a AdventJS 2021! 🤗 7 | // ¡Nos vemos el año que viene! 👋 8 | let position = [0, 0]; 9 | 10 | for (let i = 0; i < game.length; i++) { 11 | for (let j = 0; j < game[i].length; j++) { 12 | if (game[i][j] === 'm') { 13 | position = [i, j]; 14 | break; 15 | } 16 | } 17 | } 18 | 19 | if (direction === 'right') 20 | position[1] += 1; 21 | 22 | if (direction === 'left') 23 | position[1] -= 1; 24 | 25 | if (direction === 'up') 26 | position[0] -= 1; 27 | 28 | if (direction === 'down') 29 | position[0] += 1; 30 | 31 | return game[position[0]] 32 | ? game[position[0]][position[1]] === '*' 33 | : false; 34 | } 35 | -------------------------------------------------------------------------------- /2021/challenge-26.js: -------------------------------------------------------------------------------- 1 | // This challenge isn't totally official. 2 | 3 | /** @returns {'midudev'} */ 4 | function devuelveMidudev() { 5 | return 'midudev'; 6 | } 7 | -------------------------------------------------------------------------------- /2022/.gitignore: -------------------------------------------------------------------------------- 1 | # Deno 2 | deno.lock 3 | -------------------------------------------------------------------------------- /2022/2022.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlamesX-128/adventjs/d6604424ecce422938fda3ab9fe81d545419245f/2022/2022.png -------------------------------------------------------------------------------- /2022/challenge-01/README.md: -------------------------------------------------------------------------------- 1 | # Challenge #1: Automating Christmas gift wrapping! 2 | 3 | ## 📖 Instructions 4 | 5 | The elves bought a gift-wrapping machine this year. But it's not programmed! **We need to create an algorithm that helps it in the task.** 6 | 7 | The machine receives an array of gifts. Each gift is a `string`. We need the machine to wrap each gift in wrapping paper and place it in an array of wrapped gifts. 8 | 9 | The wrapping paper is the `*` symbol, and in order to wrap a gift, you need to place it surrounding the string. For example: 10 | 11 | const gifts = ['cat', 'game', 'socks'] 12 | const wrapped = wrapping(gifts) 13 | 14 | console.log(wrapped) 15 | /* [ 16 | "*****\n*cat*\n*****", 17 | "******\n*game*\n******", 18 | "*******\n*socks*\n*******" 19 | ] */ 20 | As you can see, the wrapping paper wraps the string. On top and bottom, so as not to leave any gaps, the corners are also covered with wrapping paper. 21 | 22 | **Note:** The `\n` represents a line break. 23 | 24 | **Watch out!** Make sure you put the right number of `\*` symbols to wrap completely the string. But not too many! Just enough to cover the string. 25 | 26 | Ah, **and do not mutate the original array!** 27 | 28 | ## 📜 Results 29 | 30 | ![adventjs](https://user-images.githubusercontent.com/78381898/206552696-2fb32f5e-d3bb-457f-b6f3-27464ea0b9c1.png) 31 | -------------------------------------------------------------------------------- /2022/challenge-01/main.test.ts: -------------------------------------------------------------------------------- 1 | import { assertEquals } from 'std/testing/asserts.ts' 2 | 3 | import { wrapping } from './main.ts' 4 | 5 | // First number: Edition number 6 | // Second number: Challenge number 7 | // Third number: Test number 8 | 9 | Deno.test('2022-001-001', () => assertEquals( 10 | wrapping(["cat", "game", "socks"]), 11 | [ 12 | "*****\n*cat*\n*****", 13 | "******\n*game*\n******", 14 | "*******\n*socks*\n*******" 15 | ] 16 | )) 17 | -------------------------------------------------------------------------------- /2022/challenge-01/main.ts: -------------------------------------------------------------------------------- 1 | function wrapping(gifts: string[]): string[] { 2 | return gifts.map((gift) => { 3 | const stars = '*'.repeat(gift.length + 2); 4 | 5 | return stars + `\n*${gift}*\n` + stars; 6 | }); 7 | } 8 | 9 | // Only exporting the function to test it. 10 | export { wrapping } 11 | -------------------------------------------------------------------------------- /2022/challenge-02/README.md: -------------------------------------------------------------------------------- 1 | # Challenge #2: Nobody wants to do extra hours at work 2 | 3 | ## 📖 Instructions 4 | 5 | A millionaire bought a social network, and he doesn't bring good news. He has announced that **each time an employee misses a working day due to a holiday**, they will have to compensate it with **two extra hours another working day of the same year**. 6 | 7 | Obviously the people who work in the company have not made the slightest joke and are **preparing a program that tells them the number of extra hours they would do** in the year if the new rule were applied. 8 | 9 | Since it is office work, their working hours are **from Monday to Friday**. So you only have to worry about the holidays that fall on those days. 10 | 11 | Given a year and an array with the dates of the holidays, return the number of extra hours that would be done during that year: 12 | 13 | ```js 14 | const year = 2022 15 | const holidays = ['01/06', '04/01', '12/25'] // format MM/DD 16 | 17 | // 01/06 is January 6, Thursday. Count. 18 | // 04/01 is April 1, Friday. Count. 19 | // 12/25 is December 25, Sunday. Do not count. 20 | 21 | countHours(year, holidays) // 2 days -> 4 extra hours in the year 22 | ``` 23 | 24 | ## 📝 Things to keep in mind 25 | 26 | - The year may be a leap year. Make the checks you need for it, if necessary. 27 | - Although the holiday is December 31, the extra hours will be done the same year. 28 | - `Date.getDay()` method returns the day of the week of a date. 0 is Sunday, 1 is Monday, etc. 29 | 30 | ## 📜 Results 31 | 32 | ![adventjs](https://user-images.githubusercontent.com/78381898/206299021-dc6c8b0b-3b94-4eb6-ae2f-555568b3540a.png) 33 | -------------------------------------------------------------------------------- /2022/challenge-02/main.test.ts: -------------------------------------------------------------------------------- 1 | import { assertEquals } from 'std/testing/asserts.ts' 2 | 3 | import { countHours } from './main.ts' 4 | 5 | // First number: Edition number 6 | // Second number: Challenge number 7 | // Third number: Test number 8 | 9 | Deno.test('2022-002-001', () => assertEquals( 10 | countHours(2023, ['01/06', '04/01', '12/25']), 11 | 4 12 | )) 13 | 14 | Deno.test('2022-002-002', () => assertEquals( 15 | countHours(2022, ['01/06', '04/01', '12/25']), 16 | 4 17 | )) 18 | 19 | Deno.test('2022-002-003', () => assertEquals( 20 | countHours(2000, ['01/01']), 21 | 0 22 | )) 23 | -------------------------------------------------------------------------------- /2022/challenge-02/main.ts: -------------------------------------------------------------------------------- 1 | function countHours(year: number, holidays: string[]): number { 2 | return 2 * holidays.filter( 3 | (date) => ![0, 6].includes(new Date(`${year}-${date}`).getDay()) 4 | ).length; 5 | } 6 | 7 | // Only exporting the function to test it. 8 | export { countHours } 9 | -------------------------------------------------------------------------------- /2022/challenge-03/README.md: -------------------------------------------------------------------------------- 1 | # Challenge #3: How many packs of gifts can Santa carry? 2 | 3 | ## 📖 Instructions 4 | 5 | You receive a Christmas gifts pack that Santa Claus wants to deliver to the children. **Each gift inside the pack is represented by a string** and it has a weight equal to the number of letters in its name. Santa Claus's sleigh can only carry a **weight limit**. 6 | 7 | Santa Claus also has a list of reindeer able to help him to deliver the gifts. Each reindeer has a **maximum weight limit** that it can carry. The maximum weight limit of each reindeer is **equal to twice the number of letters in its name.** 8 | 9 | Your task is to implement a function `distributeGifts(packOfGifts, reindeers)` that receives a gifts pack and a list of reindeer and returns the maximum number of gifts packs that Santa Claus can deliver. **You can't split gifts packs**. 10 | 11 | ```js 12 | const packOfGifts = ["book", "doll", "ball"] 13 | const reindeers = ["dasher", "dancer"] 14 | 15 | // pack weights 4 + 4 + 4 = 12 16 | // reindeers can carry (2 * 6) + (2 * 6) = 24 17 | distributeGifts(packOfGifts, reindeers) // 2 18 | ``` 19 | 20 | ## 📝 Things to keep in mind 21 | 22 | - The gifts pack can't be splitted. 23 | - Gifts and reindeers' names length will always be greater than 0. 24 | 25 | ## 📜 Results 26 | 27 | ![adventjs](https://user-images.githubusercontent.com/78381898/206296540-2c3988f9-caea-495b-960b-3521483275e3.png) 28 | -------------------------------------------------------------------------------- /2022/challenge-03/main.test.ts: -------------------------------------------------------------------------------- 1 | import { assertEquals } from 'std/testing/asserts.ts' 2 | 3 | import { distributeGifts } from './main.ts' 4 | 5 | // First number: Edition number 6 | // Second number: Challenge number 7 | // Third number: Test number 8 | 9 | Deno.test('2022-003-001', () => assertEquals( 10 | distributeGifts(["book", "doll", "ball"], ["dasher", "dancer"]), 11 | 2 12 | )) 13 | 14 | Deno.test('2022-003-002', () => assertEquals( 15 | distributeGifts(['a', 'b', 'c'], ['d', 'e']), 16 | 1 17 | )) 18 | 19 | Deno.test('2022-003-003', () => assertEquals( 20 | distributeGifts( 21 | ['game', 'videoconsole', 'computer'], 22 | [ 23 | 'midudev', 'pheralb', 'codingwithdani', 'carlosble', 'blasco', 24 | 'facundocapua', 'madeval', 'memxd' 25 | ] 26 | ), 27 | 5 28 | )) 29 | -------------------------------------------------------------------------------- /2022/challenge-03/main.ts: -------------------------------------------------------------------------------- 1 | function distributeGifts(packOfGifts: string[], reindeers: string[]): number { 2 | return ~~ (reindeers.join('').length * 2 / packOfGifts.join('').length) 3 | } 4 | 5 | // Only exporting the function to test it. 6 | export { distributeGifts } 7 | -------------------------------------------------------------------------------- /2022/challenge-04/main.test.ts: -------------------------------------------------------------------------------- 1 | import { assertEquals } from 'std/testing/asserts.ts' 2 | 3 | import { fitsInOneBox } from './main.ts' 4 | 5 | // First number: Edition number 6 | // Second number: Challenge number 7 | // Third number: Test number 8 | 9 | Deno.test('2022-004-001', () => assertEquals( 10 | fitsInOneBox([ 11 | { l: 1, w: 1, h: 10 }, 12 | { l: 3, w: 3, h: 12 }, 13 | { l: 2, w: 2, h: 1 }, 14 | ]), 15 | false 16 | )) 17 | 18 | Deno.test('2022-004-002', () => assertEquals( 19 | fitsInOneBox([ 20 | { l: 1, w: 1, h: 1 }, 21 | { l: 2, w: 2, h: 2 } 22 | ]), 23 | true 24 | )) 25 | 26 | Deno.test('2022-004-003', () => assertEquals( 27 | fitsInOneBox([ 28 | { l: 1, w: 1, h: 1 }, 29 | { l: 2, w: 2, h: 2 }, 30 | { l: 3, w: 1, h: 3 } 31 | ]), 32 | false 33 | )) 34 | -------------------------------------------------------------------------------- /2022/challenge-04/main.ts: -------------------------------------------------------------------------------- 1 | function fitsInOneBox(boxes: { l: number, w: number, h: number }[]): boolean { 2 | return boxes.sort( 3 | (a, b) => a.l * a.w * a.h - b.l * b.w * b.h 4 | ).every((box, i) => { 5 | const item = boxes[i + 1] 6 | 7 | return item 8 | ? item.l > box.l && item.w > box.w && item.h > box.h 9 | : true 10 | }) 11 | } 12 | 13 | // Only exporting the function to test it. 14 | export { fitsInOneBox } 15 | -------------------------------------------------------------------------------- /2022/challenge-05/main.test.ts: -------------------------------------------------------------------------------- 1 | import { assertEquals } from 'std/testing/asserts.ts' 2 | 3 | import { getMaxGifts } from './main.ts' 4 | 5 | // First number: Edition number 6 | // Second number: Challenge number 7 | // Third number: Test number 8 | 9 | Deno.test('2022-005-001', () => assertEquals( 10 | getMaxGifts([12, 3, 11, 5, 7], 20, 3), 11 | 20 12 | )) 13 | 14 | Deno.test('2022-005-002', () => assertEquals( 15 | getMaxGifts([50], 15, 1), 16 | 0 17 | )) 18 | 19 | Deno.test('2022-005-003', () => assertEquals( 20 | getMaxGifts([50, 70, 30], 100, 5), 21 | 100 22 | )) 23 | 24 | Deno.test('2022-005-004', () => assertEquals( 25 | getMaxGifts([50, 70], 100, 1), 26 | 70 27 | )) 28 | 29 | Deno.test('2022-005-005', () => assertEquals( 30 | getMaxGifts([50, 10, 40, 1000, 500, 200], 199, 4), 31 | 100 32 | )) 33 | 34 | Deno.test('2022-005-006', () => assertEquals( 35 | getMaxGifts([50, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11], 1000, 1000), 36 | 115 37 | )) 38 | -------------------------------------------------------------------------------- /2022/challenge-05/main.ts: -------------------------------------------------------------------------------- 1 | function getMaxGifts(giftsCities: number[], maxGifts: number, maxCities: number): number { 2 | const sort = giftsCities.sort((a, b) => b - a); 3 | 4 | const size = sort.length < maxCities 5 | ? sort.length 6 | : maxCities; 7 | 8 | // deno-lint-ignore no-inferrable-types 9 | return (function _(arr: number[], n: number, m: number, i: number = 0): number { 10 | if (i >= n) return 0 11 | 12 | return arr.reduce((acc, curr, index) => { 13 | if (m === 0) return acc 14 | 15 | const data = _(arr.slice(index + 1), n, m - curr, i + 1) 16 | const sum = data + curr 17 | 18 | return sum > acc && sum <= m 19 | ? sum 20 | : acc 21 | }, 0) 22 | })(sort, size, maxGifts) 23 | 24 | } 25 | 26 | // Only exporting the function to test it. 27 | export { getMaxGifts } 28 | -------------------------------------------------------------------------------- /2022/challenge-06/main.test.ts: -------------------------------------------------------------------------------- 1 | import { assertEquals } from 'std/testing/asserts.ts' 2 | 3 | import { createCube } from './main.ts' 4 | 5 | // First number: Edition number 6 | // Second number: Challenge number 7 | // Third number: Test number 8 | 9 | const specs = [ 10 | ' /\\_\\_\\_\\\n /\\/\\_\\_\\_\\\n/\\/\\/\\_\\_\\_\\\n\\/\\/\\/_/_/_/\n \\/\\/_/_/_/\n \\/_/_/_/', 11 | ' /\\_\\_\\\n/\\/\\_\\_\\\n\\/\\/_/_/\n \\/_/_/', 12 | '/\\_\\\n\\/_/', 13 | ] 14 | 15 | Deno.test('2022-006-001', () => assertEquals( 16 | createCube(3), 17 | specs[0] 18 | )) 19 | 20 | Deno.test('2022-006-002', () => assertEquals( 21 | createCube(2), 22 | specs[1] 23 | )) 24 | 25 | Deno.test('2022-006-003', () => assertEquals( 26 | createCube(1), 27 | specs[2] 28 | )) 29 | -------------------------------------------------------------------------------- /2022/challenge-06/main.ts: -------------------------------------------------------------------------------- 1 | function createCube(size: number): string { 2 | const head: string[] = [], tail: string[] = [] 3 | 4 | for (let i = 0; i < size; i++) { 5 | head.push( 6 | ' '.repeat(size - i - 1) + '/\\'.repeat(i + 1) + '_\\'.repeat(size) 7 | ) 8 | 9 | tail.push( 10 | ' '.repeat(i) + '\\/'.repeat(size - i) + '_/'.repeat(size) 11 | ) 12 | } 13 | 14 | return head.concat(...tail).join('\n') 15 | } 16 | 17 | // Only exporting the function to test it. 18 | export { createCube } 19 | -------------------------------------------------------------------------------- /2022/challenge-07/main.test.ts: -------------------------------------------------------------------------------- 1 | import { assertEquals } from 'std/testing/asserts.ts' 2 | 3 | import { getGiftsToRefill } from './main.ts' 4 | 5 | // First number: Edition number 6 | // Second number: Challenge number 7 | // Third number: Test number 8 | 9 | Deno.test('2022-007-001', () => assertEquals( 10 | getGiftsToRefill( 11 | ['bike', 'car', 'bike', 'bike'], 12 | ['car', 'bike', 'doll', 'car'], 13 | ['bike', 'pc', 'pc'] 14 | ), 15 | ['doll', 'pc'] 16 | )) 17 | 18 | Deno.test('2022-007-002', () => assertEquals( 19 | getGiftsToRefill( 20 | [], 21 | [], 22 | [] 23 | ), 24 | [] 25 | )) 26 | -------------------------------------------------------------------------------- /2022/challenge-07/main.ts: -------------------------------------------------------------------------------- 1 | function getGiftsToRefill(a1: string[], a2: string[], a3: string[]): string[] { 2 | // Set all elements of the arrays to a new Array without duplicates. 3 | return [...new Set([...a1, ...a2, ...a3])].filter( 4 | // +.includes() converts the boolean to a number 5 | // and returns true if the elements exists only once in the arrays. 6 | (e) => +a1.includes(e) + +a2.includes(e) + +a3.includes(e) === 1 7 | ) 8 | } 9 | 10 | // Only exporting the function to test it. 11 | export { getGiftsToRefill } 12 | -------------------------------------------------------------------------------- /2022/challenge-08/README.md: -------------------------------------------------------------------------------- 1 | # Challenge #8: We need a mechanic! 2 | 3 | ## 📖 Instructions 4 | 5 | Some electric sleds have broken down and the elves are looking for spare parts to fix them, but they are not sure if the parts they have are valid. 6 | 7 | The spare parts are strings and the mechanic Elfon Masc has said that a spare part is valid **if the part can be a palindrome after removing, at most, one character.** 8 | 9 | *A palindrome is a word or phrase that reads the same from left to right as it does from right to left.* 10 | 11 | Our function should return a boolean that indicates whether the spare part is valid or not with that rule: 12 | 13 | ```js 14 | checkPart("uwu") // true 15 | // "uwu" is a palindrome without removing any character 16 | 17 | checkPart("miidim") // true 18 | // "miidim" can be a palindrome after removing the first "i" 19 | 20 | checkPart("midu") // false 21 | // "midu" cannot be a palindrome after removing a character 22 | ``` 23 | 24 | ## 📜 Results 25 | 26 | ![adventjs](https://user-images.githubusercontent.com/78381898/206553249-f5676926-9db8-42d2-9a67-02d91d5c16e6.png) 27 | -------------------------------------------------------------------------------- /2022/challenge-08/main.test.ts: -------------------------------------------------------------------------------- 1 | import { assertEquals } from 'std/testing/asserts.ts' 2 | 3 | import { checkPart } from './main.ts' 4 | 5 | // First number: Edition number 6 | // Second number: Challenge number 7 | // Third number: Test number 8 | 9 | Deno.test('2022-008-001', () => assertEquals( 10 | checkPart('uwu'), 11 | true 12 | )) 13 | 14 | Deno.test('2022-008-002', () => assertEquals( 15 | checkPart('owo'), 16 | true 17 | )) 18 | 19 | Deno.test('2022-008-003', () => assertEquals( 20 | checkPart('yolooloy'), 21 | true 22 | )) 23 | 24 | Deno.test('2022-008-004', () => assertEquals( 25 | checkPart('zzzoonzzz'), 26 | true 27 | )) 28 | -------------------------------------------------------------------------------- /2022/challenge-08/main.ts: -------------------------------------------------------------------------------- 1 | function checkPart(part: string): boolean { 2 | // split the string into an array of characters. 3 | return [...part, ' '].some( 4 | // if some of the caracters are removed, and the remaining string is a palindrome, return true. 5 | (_, i, arr) => { 6 | // create a subarray without the current character. 7 | const part = arr.slice(0, i).concat(arr.slice(i + 1, -1)) 8 | 9 | // if the index is the last one and the word is 'owo' 10 | // the subarray will be ['o', 'w', 'o'] and the reverse will be ['o', 'w', 'o'] 11 | // so the word is a palindrome. 12 | return part.join('') === part.reverse().join('') 13 | } 14 | ); 15 | } 16 | 17 | // Only exporting the function to test it. 18 | export { checkPart } 19 | -------------------------------------------------------------------------------- /2022/challenge-09/README.md: -------------------------------------------------------------------------------- 1 | # Challenge #9: Crazy Xmas lights 2 | 3 | ## 📖 Instructions 4 | 5 | A company that manufactures Christmas lights has asked us to help them. 6 | 7 | They have led strips that are like an `Array`. Each position is a led and can be on (`1`) or off (`0`). 8 | 9 | **Every 7 seconds**, the leds change state in this way: 10 | 11 | - **If the led is off**, it turns on if the led on its left (`index - 1`) was on before. 12 | - **If the led is on**, it remains on. 13 | 14 | They asked us for a program that tells us how many seconds it takes for all the leds to turn on. **The seconds are expressed as an integer.** For example: 15 | 16 | ```js 17 | const leds = [0, 1, 1, 0, 1] 18 | countTime(leds) // 7 19 | // 7 seconds because in the first change 20 | // all the lights turned on 21 | // 0s: [0, 1, 1, 0, 1] 22 | // 7s: [1, 1, 1, 1, 1] 23 | 24 | countTime([0, 0, 0, 1]) // 21 25 | // 21 seconds because it needs three changes: 26 | // 0s: [0, 0, 0, 1] 27 | // 7s: [1, 0, 0, 1] 28 | // 14s: [1, 1, 0, 1] 29 | // 21s: [1, 1, 1, 1] 30 | 31 | countTime([0, 0, 1, 0, 0]) // 28 32 | // 28 seconds because it needs four changes: 33 | // 0s: [0, 0, 1, 0, 0] 34 | // 7s: [0, 0, 1, 1, 0] 35 | // 14s: [0, 0, 1, 1, 1] 36 | // 21s: [1, 0, 1, 1, 1] 37 | // 28s: [1, 1, 1, 1, 1] 38 | ``` 39 | 40 | ## 📝 Keep in mind 41 | 42 | - The array will always have at least one led on. 43 | - The array can have any length. 44 | - If all the leds are on, the time is 0. 45 | 46 | ## 📜 Results 47 | 48 | ![adventjs](https://user-images.githubusercontent.com/78381898/206791570-c901f337-9518-4796-9ffa-0851853c2955.png) 49 | -------------------------------------------------------------------------------- /2022/challenge-09/main.test.ts: -------------------------------------------------------------------------------- 1 | import { assertEquals } from 'std/testing/asserts.ts' 2 | 3 | import { countTime } from './main.ts' 4 | 5 | // First number: Edition number 6 | // Second number: Challenge number 7 | // Third number: Test number 8 | 9 | Deno.test('2022-009-001', () => assertEquals( 10 | countTime([0, 1, 0, 0, 1]), 11 | 14 12 | )) 13 | 14 | Deno.test('2022-009-002', () => assertEquals( 15 | countTime([0, 0, 0, 1]), 16 | 21 17 | )) 18 | 19 | Deno.test('2022-009-003', () => assertEquals( 20 | countTime([0, 0, 1, 0, 0]), 21 | 28 22 | )) 23 | -------------------------------------------------------------------------------- /2022/challenge-09/main.ts: -------------------------------------------------------------------------------- 1 | // Based on pabloc54#6798's solution. 2 | function countTime(leds: number[]): number { 3 | const arr = leds.join('').split('1'); 4 | 5 | const e_n = arr[arr.length - 1].length; 6 | const e_0 = arr[0].length; 7 | 8 | return 7 * Math.max( 9 | ...arr.map((e) => e.length), e_0 + e_n 10 | ) 11 | } 12 | 13 | // Only exporting the function to test it. 14 | export { countTime } 15 | -------------------------------------------------------------------------------- /2022/challenge-10/README.md: -------------------------------------------------------------------------------- 1 | # Challenge #10: The Santa Claus sleigh jump 2 | 3 | ## 📖 Instructions 4 | 5 | Create a program that checks if Santa's sleigh makes a **parabola jump between cities.** You receive a **number array** that represents the **height** at which the sleigh is at each moment. 6 | 7 | For the parabola to be correct, the sleigh's trip must be ascending at the beginning and descending at the end. **It cannot go up again once it has gone down, nor can it start the trip going down.** Let's see some examples: 8 | 9 | ```js 10 | const heights = [1, 3, 8, 5, 2] 11 | checkJump(heights) // true 12 | 13 | /* 14 | It's `true`. 15 | The jump goes up-down. 16 | 17 | 8 (highest point) 18 | ↗ ↘ 19 | 3 5 20 | ↗ ↘ 21 | 1 2 22 | */ 23 | 24 | const heights = [1, 7, 3, 5] 25 | checkJump(heights) // false 26 | 27 | /* 28 | It's `false`. 29 | It goes up-down-up. 30 | 31 | 7 5 32 | ↗ ↘ ↗ 33 | 1 3 34 | ``` 35 | 36 | We need the program to return a `boolean` that indicates whether the sleigh makes a parabola or not. 37 | 38 | ## 📝 Things to keep in mind 39 | 40 | - The jump is valid if it goes up once and down once. If the sleigh stays at the same height between two positions, the parabola continues. 41 | - It is not necessary for the starting and ending points to be the same (cities can be at different heights). 42 | 43 | ## 📜 Results 44 | 45 | ![adventjs](https://user-images.githubusercontent.com/78381898/206879725-6bae3bbc-8a7b-4e05-a850-77d3a48c49e0.png) 46 | 47 | -------------------------------------------------------------------------------- /2022/challenge-10/main.test.ts: -------------------------------------------------------------------------------- 1 | import { assertEquals } from 'std/testing/asserts.ts' 2 | 3 | import { checkJump } from './main.ts' 4 | 5 | // First number: Edition number 6 | // Second number: Challenge number 7 | // Third number: Test number 8 | 9 | Deno.test('2022-010-001', () => assertEquals( 10 | checkJump([1, 2, 1]), 11 | true 12 | )) 13 | 14 | Deno.test('2022-010-002', () => assertEquals( 15 | checkJump([1, 2, 3, 2, 1]), 16 | true 17 | )) 18 | 19 | Deno.test('2022-010-003', () => assertEquals( 20 | checkJump([1, 2, 3]), 21 | false 22 | )) 23 | 24 | Deno.test('2022-010-004', () => assertEquals( 25 | checkJump([1, 1000, 900, 800]), 26 | true 27 | )) 28 | 29 | Deno.test('2022-010-005', () => assertEquals( 30 | checkJump([1, 7, 3, 5]), 31 | false 32 | )) 33 | -------------------------------------------------------------------------------- /2022/challenge-10/main.ts: -------------------------------------------------------------------------------- 1 | // Create a dictionary to map, where: 2 | // - Down: occurs when the current height is less than the previous. 3 | // + sign = -1 == 'd' 4 | 5 | // - Up: occurs when the current height is greater than the previous. 6 | // + sign = 1 == 'u' 7 | 8 | // - Stagnant: occurs when current and previous heights are equal. 9 | // + sign = 0 == '' 10 | 11 | // - Stagnant: height difference gives a non-numeric value 12 | // + sign = NaN == '' 13 | 14 | // The form would be: 15 | // [ 1, 3, 8, 5, 2 ] -> uudd (up up down down) 16 | // [ 1, 7, 3, 5 ] -> udu (up down up) 17 | 18 | // And the problem is only solved when the result starts with at least one 'u' and 19 | // ends with at least one 'd'. 20 | 21 | // then the regex should be "/^[u]+[d]+$/g". 22 | 23 | // Solution by rasom#7936 24 | function checkJump(heights: number[]): boolean { 25 | const dict: { [key: number]: string } = { 26 | 'NaN': '', '0': '', '-1': 'd', '1': 'u' 27 | } 28 | 29 | const str = heights.reduce((acc, curr, i) => { 30 | const sign = Math.sign(curr - heights[i - 1]) 31 | return acc + dict[sign] 32 | }, '') 33 | 34 | return str.match(/^[u]+[d]+$/g) !== null; 35 | } 36 | 37 | // Only exporting the function to test it. 38 | export { checkJump } 39 | -------------------------------------------------------------------------------- /2022/challenge-11/main.test.ts: -------------------------------------------------------------------------------- 1 | import { assertEquals } from 'std/testing/asserts.ts' 2 | 3 | import { getCompleted } from './main.ts' 4 | 5 | // First number: Edition number 6 | // Second number: Challenge number 7 | // Third number: Test number 8 | 9 | Deno.test('2022-011-001', () => assertEquals( 10 | getCompleted('01:00:00', '03:00:00'), 11 | "1/3" 12 | )) 13 | 14 | Deno.test('2022-011-002', () => assertEquals( 15 | getCompleted('02:00:00', '04:00:00'), 16 | "1/2" 17 | )) 18 | 19 | Deno.test('2022-011-003', () => assertEquals( 20 | getCompleted('02:20:20', '03:30:30'), 21 | "2/3" 22 | )) 23 | 24 | Deno.test('2022-011-004', () => assertEquals( 25 | getCompleted('03:30:30', '05:50:50'), 26 | "3/5" 27 | )) 28 | -------------------------------------------------------------------------------- /2022/challenge-11/main.ts: -------------------------------------------------------------------------------- 1 | function getCompleted(part: string, total: string): string { 2 | const mcd = (a: number, b: number): number => 3 | b === 0 ? a : mcd(b, a % b) 4 | 5 | const x = total.split(':').reduce( 6 | (acc, curr) => acc * 60 + +curr, 0 7 | ) 8 | 9 | const y = part.split(':').reduce( 10 | (acc, curr) => acc * 60 + +curr, 0 11 | ) 12 | 13 | const z = mcd(y, x) 14 | 15 | return `${y / z}/${x / z}` 16 | } 17 | 18 | // Only exporting the function to test it. 19 | export { getCompleted } 20 | -------------------------------------------------------------------------------- /2022/challenge-12/main.test.ts: -------------------------------------------------------------------------------- 1 | import { assertEquals } from 'std/testing/asserts.ts' 2 | 3 | import { selectSleigh } from './main.ts' 4 | 5 | // First number: Edition number 6 | // Second number: Challenge number 7 | // Third number: Test number 8 | 9 | Deno.test('2022-012-001', () => assertEquals( 10 | selectSleigh(1, [ 11 | { name: 'pheralb', consumption: 0.3 }, 12 | { name: 'TMChein', consumption: 0.5 } 13 | ]), 14 | 'TMChein' 15 | )) 16 | 17 | Deno.test('2022-012-002', () => assertEquals( 18 | selectSleigh(10, [ 19 | { name: 'Pedrosillano', consumption: 1 }, 20 | { name: 'SamarJaffal', consumption: 5 } 21 | ]), 22 | 'Pedrosillano' 23 | )) 24 | 25 | Deno.test('2022-012-003', () => assertEquals( 26 | selectSleigh(50, [ 27 | { name: 'Pedrosillano', consumption: 1 }, 28 | { name: 'SamarJaffal', consumption: 2 }, 29 | { name: 'marcospage', consumption: 3 } 30 | ]), 31 | null 32 | )) 33 | 34 | Deno.test('2022-012-004', () => assertEquals( 35 | selectSleigh(10, [ 36 | { name: 'Pedrosillano', consumption: 1 }, 37 | { name: 'SamarJaffal', consumption: 2 }, 38 | { name: 'marcospage', consumption: 3 } 39 | ]), 40 | 'SamarJaffal' 41 | )) 42 | -------------------------------------------------------------------------------- /2022/challenge-12/main.ts: -------------------------------------------------------------------------------- 1 | interface Sleigh { 2 | consumption: number, 3 | name?: string 4 | } 5 | 6 | function selectSleigh(distance: number, sleighs: Sleigh[]): string | null { 7 | const sleigh = sleighs.reduce((acc, curr) => { 8 | const c = curr.consumption * distance 9 | 10 | return c <= 20 && c > acc.consumption * distance 11 | ? curr 12 | : acc 13 | }, { consumption: -1 }) 14 | 15 | return sleigh.name || null 16 | } 17 | 18 | // Only exporting the function to test it. 19 | export { selectSleigh } 20 | -------------------------------------------------------------------------------- /2022/challenge-13/README.md: -------------------------------------------------------------------------------- 1 | # Challenge #13: Backups for Santa Claus files 2 | 3 | ## 📖 Instructions 4 | 5 | To avoid losing data when the server crashes, Santa Claus has decided to make incremental backups. A hacker called S4vitelf is helping him. 6 | 7 | On one hand, we have the **timestamp** of when the last backup was made. 8 | 9 | We also have the changes that have been made in an array of arrays. Each internal array contains **two elements**: the **id** of the modified file and the **timestamp** of the modification. 10 | 11 | You have to create a program that returns an array with the **id** of the files that we would have to make backup because they have been modified since the last backup. Example: 12 | 13 | ```js 14 | const lastBackup = 1546300800 15 | 16 | const changes = [ 17 | [ 1, 1546300800 ], 18 | [ 2, 1546300800 ], 19 | [ 1, 1546300900 ], 20 | [ 1, 1546301000 ], 21 | [ 3, 1546301100 ] 22 | ] 23 | 24 | getFilesToBackup(lastBackup, changes) // => [ 1, 3 ] 25 | // The file with id 1 has been modified twice 26 | // after the last backup. 27 | 28 | // The file with id 2 has not been modified after 29 | // the last backup. 30 | 31 | // The file with id 3 has been modified once 32 | // after the last backup. 33 | 34 | // We have to make a backup 35 | // of the files 1 and 3. 36 | ``` 37 | 38 | ## 📝 Remember that: 39 | 40 | - Returns the id of the files that have been modified after the last backup. 41 | - Returns an empty array if there are no files to make backup. 42 | - Returns in ascending sort. 43 | 44 | ## 📜 Results 45 | -------------------------------------------------------------------------------- /2022/challenge-13/main.test.ts: -------------------------------------------------------------------------------- 1 | import { assertEquals } from 'std/testing/asserts.ts' 2 | 3 | import { getFilesToBackup } from './main.ts' 4 | 5 | // First number: Edition number 6 | // Second number: Challenge number 7 | // Third number: Test number 8 | 9 | Deno.test('2022-013-001', () => assertEquals( 10 | getFilesToBackup(1546300800, [ 11 | [1, 1546300800], 12 | [2, 1546300800], 13 | [1, 1546300900], 14 | [1, 1546301000], 15 | [3, 1546301100] 16 | ]), 17 | [ 18 | 1, 19 | 3 20 | ] 21 | )) 22 | 23 | Deno.test('2022-013-002', () => assertEquals( 24 | getFilesToBackup(1546300600, [ 25 | [1, 1546300800], 26 | [2, 1546300800], 27 | [1, 1546300900], 28 | [1, 1546301000], 29 | [3, 1546301100] 30 | ]), 31 | [ 32 | 1, 33 | 2, 34 | 3 35 | ] 36 | )) 37 | 38 | Deno.test('2022-013-003', () => assertEquals( 39 | getFilesToBackup(1556300600, [ 40 | [1, 1546300800], 41 | [2, 1546300800], 42 | [1, 1546300900], 43 | [1, 1546301000], 44 | [3, 1546301100] 45 | ]), 46 | [] 47 | )) 48 | -------------------------------------------------------------------------------- /2022/challenge-13/main.ts: -------------------------------------------------------------------------------- 1 | function getFilesToBackup(lastBackup: number, changes: [number, number][]): number[] { 2 | const res = changes.filter( 3 | (entry) => entry[1] > lastBackup 4 | ) 5 | 6 | return [ 7 | ...new Set(res.map( 8 | (entry) => entry[0] 9 | )) 10 | ].sort( 11 | (a, b) => a - b 12 | ) 13 | } 14 | 15 | // Only exporting the function to test it. 16 | export { getFilesToBackup } 17 | -------------------------------------------------------------------------------- /2022/challenge-14/main.test.ts: -------------------------------------------------------------------------------- 1 | import { assertEquals } from 'std/testing/asserts.ts' 2 | 3 | import { getOptimalPath } from './main.ts' 4 | 5 | // First number: Edition number 6 | // Second number: Challenge number 7 | // Third number: Test number 8 | 9 | Deno.test('2022-014-001', () => assertEquals( 10 | getOptimalPath([[0], [7, 4], [2, 4, 6]]), 11 | 8 12 | )) 13 | 14 | Deno.test('2022-014-002', () => assertEquals( 15 | getOptimalPath([[0], [2, 3]]), 16 | 2 17 | )) 18 | 19 | Deno.test('2022-014-003', () => assertEquals( 20 | getOptimalPath([[1], [1, 5], [7, 5, 8], [9, 4, 1, 3]]), 21 | 8 22 | )) 23 | 24 | -------------------------------------------------------------------------------- /2022/challenge-14/main.ts: -------------------------------------------------------------------------------- 1 | // Based on Achalogy/advent-js-2022 solution. 2 | function getOptimalPath(path: number[][]): number { 3 | return path.reduceRight((acc, curr) => { 4 | return curr.map((val, i) => { 5 | return val + Math.min(acc[i], acc[i + 1]) 6 | }) 7 | })[0] 8 | } 9 | 10 | // [[0], [7, 4], [2, 4, 6]] 11 | // [] : [2, 4, 6] 12 | // 2 + min(undefined, undefined) = 2 13 | // 4 + min(undefined, undefined) = 4 14 | // 6 + min(undefined, undefined) = 6 15 | 16 | // [2, 4, 6] : [7, 4] 17 | // 7 + min(2, 4) = 9 18 | // 4 + min(4, 6) = 8 19 | 20 | // [4, 8, 12] : [0] 21 | // 0 + min(9, 8) = 8 22 | 23 | // Only exporting the function to test it. 24 | export { getOptimalPath } 25 | -------------------------------------------------------------------------------- /2022/challenge-15/main.test.ts: -------------------------------------------------------------------------------- 1 | import { assertEquals } from 'std/testing/asserts.ts' 2 | 3 | import { decorateTree } from './main.ts' 4 | 5 | // First number: Edition number 6 | // Second number: Challenge number 7 | // Third number: Test number 8 | 9 | Deno.test('2022-015-001', () => assertEquals( 10 | decorateTree('B P R P'), 11 | [ 12 | "R", 13 | "P B", 14 | "R B B", 15 | "B P R P" 16 | ] 17 | )) 18 | 19 | Deno.test('2022-015-002', () => assertEquals( 20 | decorateTree('B B'), 21 | [ 22 | "B", 23 | "B B" 24 | ] 25 | )) 26 | 27 | Deno.test('2022-015-003', () => assertEquals( 28 | decorateTree('R R P R R'), 29 | [ 30 | "R", 31 | "R R", 32 | "P B P", 33 | "R B B R", 34 | "R R P R R" 35 | ] 36 | )) 37 | 38 | Deno.test('2022-015-004', () => assertEquals( 39 | decorateTree('B B P R P R R'), 40 | [ 41 | "B", 42 | "R P", 43 | "B P P", 44 | "P R B R", 45 | "P P B B P", 46 | "B R B B B R", 47 | "B B P R P R R" 48 | ] 49 | )) 50 | -------------------------------------------------------------------------------- /2022/challenge-15/main.ts: -------------------------------------------------------------------------------- 1 | function decorateTree(base: string): string[] { 2 | const data: string[][] = [base.split(' ')] 3 | 4 | const dict: { [key: string]: string } = { 5 | BP: 'R', 6 | BR: 'P', 7 | BB: 'B', 8 | 9 | RP: 'B', 10 | RR: 'R', 11 | RB: 'P', 12 | 13 | PP: 'P', 14 | PR: 'B', 15 | PB: 'R', 16 | } 17 | 18 | while ((data.at(-1) || []).length > 1) { 19 | const prev: string[] = data.at(-1)! 20 | const next: string[] = [] 21 | 22 | for (let i= 0; i < prev.length - 1; i++) { 23 | const item = dict[prev[i] + prev[i + 1]] 24 | 25 | item && next.push(item) 26 | } 27 | 28 | data.push(next) 29 | } 30 | 31 | return data.reverse().map( 32 | (row) => row.join(' ') 33 | ) 34 | } 35 | 36 | // Only exporting the function to test it. 37 | export { decorateTree } 38 | -------------------------------------------------------------------------------- /2022/challenge-16/README.md: -------------------------------------------------------------------------------- 1 | # Challenge #16: Fixing Santa Claus' letters 2 | 3 | ## 📖 Instructions 4 | 5 | Santa Claus is starting to receive a lot of letters but they have some formatting problems. To improve readability, he will write a program that given a text, formats it according to the following rules: 6 | 7 | - Remove spaces at the beginning and end of the prase 8 | - Remove multiple spaces and leave only one 9 | - Leave a space after each comma and point 10 | - Remove spaces before comma or point 11 | - Questions must only end with a question mark 12 | - The first letter of each sentence must be capitalized 13 | - Put the word "Santa Claus" in uppercase if it appears in the letter 14 | - Put a point at the end of the sentence if it does not have punctuation 15 | - Letters are written in English and here we have an example: 16 | 17 | ```js 18 | fixLetter(` hello, how are you?? do you know if santa claus exists? i really hope he does! bye `) 19 | // Hello, how are you? Do you know if Santa Claus exists? I really hope he does! Bye. 20 | 21 | fixLetter(" Hi Santa claus. I'm a girl from Barcelona , Spain . please, send me a bike. Is it possible?") 22 | // Hi Santa Claus. I'm a girl from Barcelona, Spain. Please, send me a bike. Is it possible? 23 | ``` 24 | 25 | ## 📝 Note: 26 | 27 | - You do not have to worry about punctuation marks other than comma, point or question mark. 28 | - Make sure you respect break lines and original whitespaces. 29 | 30 | ## 📜 Results 31 | 32 | ![adventjs](https://user-images.githubusercontent.com/78381898/208271446-95366622-175d-4ebb-bcfe-770a47e7d0a5.png) 33 | -------------------------------------------------------------------------------- /2022/challenge-16/main.test.ts: -------------------------------------------------------------------------------- 1 | import { assertEquals } from 'std/testing/asserts.ts' 2 | 3 | import { fixLetter } from './main.ts' 4 | 5 | // First number: Edition number 6 | // Second number: Challenge number 7 | // Third number: Test number 8 | 9 | Deno.test('2022-016-001', () => assertEquals( 10 | fixLetter(` hello, how are you?? do you know if santa claus exists? i really hope he does! bye `), 11 | "Hello, how are you? Do you know if Santa Claus exists? I really hope he does! Bye." 12 | )) 13 | 14 | Deno.test('2022-016-002', () => assertEquals( 15 | fixLetter(" Hi Santa claus. I'm a girl from Barcelona , Spain . please, send me a bike. Is it possible?"), 16 | "Hi Santa Claus. I'm a girl from Barcelona, Spain. Please, send me a bike. Is it possible?" 17 | )) 18 | 19 | Deno.test('2022-016-003', () => assertEquals( 20 | fixLetter(" hi santa claus "), 21 | "Hi Santa Claus." 22 | )) 23 | 24 | 25 | Deno.test('2022-016-004', () => assertEquals( 26 | fixLetter(' hi,santa claus. are you there ? '), 27 | "Hi, Santa Claus. Are you there?" 28 | )) 29 | -------------------------------------------------------------------------------- /2022/challenge-16/main.ts: -------------------------------------------------------------------------------- 1 | function fixLetter(letter: string): string { 2 | const s_1 = letter.replace(/^\s+|\s+$/g, '') 3 | 4 | const s_2 = s_1.replace(/\s+(?=[.,?])|(?<=\s)\s+/g, '') 5 | 6 | const s_3 = s_2.replace(/(?<=[.,?!])(?=[.,?!])./g, '') 7 | 8 | const s_4 = s_3.replace(/^.|(?<=[.?!] )./g, 9 | (char) => { 10 | return char.toUpperCase() 11 | } 12 | ) 13 | 14 | const s_5 = s_4.replace(/santa claus/gi, 'Santa Claus') 15 | 16 | const s_6 = s_5.replace(/(?<=[^.?!])$/, '.') 17 | 18 | const s_7 = s_6.replace(/(?<=[,.?!])./g, 19 | (char) => { 20 | return char !== ' ' ? ' ' + char : char 21 | } 22 | ) 23 | 24 | return s_7 25 | } 26 | 27 | // Only exporting the function to test it. 28 | export { fixLetter } 29 | -------------------------------------------------------------------------------- /2022/challenge-17/main.test.ts: -------------------------------------------------------------------------------- 1 | import { assertEquals } from 'std/testing/asserts.ts' 2 | 3 | import { carryGifts } from './main.ts' 4 | 5 | // First number: Edition number 6 | // Second number: Challenge number 7 | // Third number: Test number 8 | 9 | Deno.test('2022-017-001', () => assertEquals( 10 | carryGifts(['game', 'bike', 'book', 'toy'], 10), 11 | [ 12 | "game bike", 13 | "book toy" 14 | ] 15 | )) 16 | 17 | Deno.test('2022-017-002', () => assertEquals( 18 | carryGifts(['game', 'bike', 'book', 'toy'], 7), 19 | [ 20 | "game", 21 | "bike", 22 | "book toy" 23 | ] 24 | )) 25 | -------------------------------------------------------------------------------- /2022/challenge-17/main.ts: -------------------------------------------------------------------------------- 1 | // Great solution by Avrick#3850 2 | function carryGifts(gifts: string[], maxWeight: number): string[] { 3 | // Basically collects words until the weight of the phrase is greater than maxWeight 4 | // and then starts a new collection of words. 5 | const regExp = new RegExp(`\\b(\\w ?){1,${maxWeight}}(?= |$)`, 'g') 6 | 7 | return gifts.join(' ').match(regExp) || []; 8 | } 9 | 10 | // Only exporting the function to test it. 11 | export { carryGifts } 12 | -------------------------------------------------------------------------------- /2022/challenge-18/README.md: -------------------------------------------------------------------------------- 1 | # Challenge #18: We ran out of ink! 2 | 3 | ## 📖 Instructions 4 | 5 | We are printing barcodes for the packages. At Santa's factory we use a numbering system where each number is printed with a different ink. It's an old but reliable system. However, sometimes we run out of ink for a number. 6 | 7 | **Write a function** that **receives the number we don't have ink for** (a number between 0 and 9) and as a second parameter, **the number of barcodes to print** (we start from 1 to the number we receive). 8 | 9 | It should **return an array with the numbers that include the number we don't have ink for**. Let's see an example: 10 | 11 | ```js 12 | dryNumber(1, 15) // [1, 10, 11, 12, 13, 14, 15] 13 | 14 | // we don't have ink for the number 1 15 | // we have to print 15 barcodes from 1 to 15 16 | // the barcodes that will be wrong because of the lack of ink are: 17 | // 1, 10, 11, 12, 13, 14, 15 18 | 19 | dryNumber(2, 20) // [2, 12, 20] 20 | 21 | // we don't have ink for the number 2 22 | // we have to print 20 barcodes from 1 to 20 23 | // the barcodes that will be wrong because of the lack of ink are: 24 | 25 | // 2, 12, 20 26 | ``` 27 | 28 | ## 📝 Keep in mind that: 29 | 30 | - The number we don't have ink for can only be between 0 and 9. 31 | - The number we don't have ink for can be in any position of the barcode. 32 | - The number of barcodes to print can be very large. 33 | 34 | ## 📜 Results 35 | 36 | ![adventjs](https://user-images.githubusercontent.com/78381898/208763973-ee2312dd-fd61-4c66-9617-677f7cc249cb.png) 37 | -------------------------------------------------------------------------------- /2022/challenge-18/main.test.ts: -------------------------------------------------------------------------------- 1 | import { assertEquals } from 'std/testing/asserts.ts' 2 | 3 | import { dryNumber } from './main.ts' 4 | 5 | // First number: Edition number 6 | // Second number: Challenge number 7 | // Third number: Test number 8 | 9 | Deno.test('2022-018-001', () => assertEquals( 10 | dryNumber(1, 15), 11 | [ 12 | 1, 13 | 10, 14 | 11, 15 | 12, 16 | 13, 17 | 14, 18 | 15 19 | ] 20 | )) 21 | 22 | Deno.test('2022-018-002', () => assertEquals( 23 | dryNumber(2, 20), 24 | [ 25 | 2, 26 | 12, 27 | 20 28 | ] 29 | )) 30 | -------------------------------------------------------------------------------- /2022/challenge-18/main.ts: -------------------------------------------------------------------------------- 1 | function dryNumber(dry: number, numbers: number): number[] { 2 | return [...new Array(numbers)] 3 | .map( 4 | (_, i) => i + 1 5 | ) 6 | .filter( 7 | (e) => e.toString().includes( 8 | dry.toString() 9 | ) 10 | ) 11 | } 12 | 13 | // Only exporting the function to test it. 14 | export { dryNumber } 15 | -------------------------------------------------------------------------------- /2022/challenge-19/README.md: -------------------------------------------------------------------------------- 1 | # Challenge #19: Sorting the toys! 2 | 3 | ## 📖 Instructions 4 | 5 | The day is coming and Santa Claus has the toy store a mess. Help him to sort the toys in the warehouse so he can find them easier. 6 | 7 | For this, we are given two arrays. **The first is an array of toys**, and the second is an **array of numbers** that indicate the position of each toy in the warehouse. 8 | 9 | Te only thing to keep in mind is that **the positions may not start at 0**, although they will always be consecutive numbers and in ascending order. 10 | 11 | We have to **return an array where each toy is in the position it corresponds to**. 12 | 13 | ```js 14 | const toys = ['ball', 'doll', 'car', 'puzzle'] 15 | const positions = [2, 3, 1, 0] 16 | 17 | sortToys(toys, positions) 18 | // ['puzzle', 'car', 'ball', 'doll'] 19 | 20 | const moreToys = ['pc', 'xbox', 'ps4', 'switch', 'nintendo'] 21 | const morePositions = [8, 6, 5, 7, 9] 22 | 23 | sortToys(moreToys, morePositions) 24 | // ['ps4', 'xbox', 'switch', 'pc', 'nintendo'] 25 | ``` 26 | 27 | ## 📝 Things to keep in mind 28 | 29 | - There will always be the same number of toys as positions. 30 | - Neither the toys nor the positions are repeated. 31 | 32 | ## 📜 Results 33 | 34 | ![adventjs](https://user-images.githubusercontent.com/78381898/208765671-444d51ae-1a26-4ea9-b477-60a4d5a899df.png) 35 | -------------------------------------------------------------------------------- /2022/challenge-19/main.test.ts: -------------------------------------------------------------------------------- 1 | import { assertEquals } from 'std/testing/asserts.ts' 2 | 3 | import { sortToys } from './main.ts' 4 | 5 | // First number: Edition number 6 | // Second number: Challenge number 7 | // Third number: Test number 8 | 9 | Deno.test('2022-019-001', () => assertEquals( 10 | sortToys(['ball', 'doll', 'car', 'puzzle'], [2, 3, 1, 0]), 11 | [ 12 | "puzzle", 13 | "car", 14 | "ball", 15 | "doll" 16 | ] 17 | )) 18 | 19 | Deno.test('2022-019-002', () => assertEquals( 20 | sortToys(['pc', 'xbox', 'ps4', 'switch', 'nintendo'], [3, 1, 0, 2, 4]), 21 | [ 22 | "ps4", 23 | "xbox", 24 | "switch", 25 | "pc", 26 | "nintendo" 27 | ] 28 | )) 29 | -------------------------------------------------------------------------------- /2022/challenge-19/main.ts: -------------------------------------------------------------------------------- 1 | function sortToys(toys: string[], positions: number[]): string[] { 2 | return toys.sort( 3 | (i1, i2) => positions[toys.indexOf(i1)] - positions[toys.indexOf(i2)] 4 | ) 5 | } 6 | 7 | // Only exporting the function to test it. 8 | export { sortToys } 9 | -------------------------------------------------------------------------------- /2022/challenge-20/main.ts: -------------------------------------------------------------------------------- 1 | interface Reindeer { 2 | weightCapacity: number, 3 | type: string 4 | } 5 | 6 | interface Reindeers { 7 | reindeers: { 8 | type: string, 9 | num: number 10 | }[], 11 | country: string 12 | } 13 | 14 | interface Gift { 15 | country: string, 16 | weight: number 17 | } 18 | 19 | function howManyReindeers(reindeerTypes: Reindeer[], gifts: Gift[]): Reindeers[] { 20 | const ord_reindeers = reindeerTypes.sort( 21 | (i1, i2) => i2.weightCapacity - i1.weightCapacity 22 | ) 23 | 24 | return gifts.map(({ country, weight }) => { 25 | const data: Reindeers['reindeers'] = ord_reindeers 26 | .map( 27 | (i1) => ({ type: i1.type, num: 0 }) 28 | ) 29 | 30 | const reindeers = [...ord_reindeers].reverse() 31 | let sum = 0 32 | 33 | while (sum < weight) for (const reindeer of reindeers) { 34 | if (sum + reindeer.weightCapacity > weight) continue 35 | 36 | sum += reindeer.weightCapacity 37 | 38 | data.find( 39 | (i2) => reindeer.type === i2.type 40 | )!.num++ 41 | } 42 | 43 | return { 44 | reindeers: data.filter( 45 | ({ num }) => num > 0 46 | ), 47 | country 48 | } 49 | }) 50 | 51 | } 52 | 53 | // Only exporting the function to test it. 54 | export { howManyReindeers } 55 | -------------------------------------------------------------------------------- /2022/challenge-21/main.test.ts: -------------------------------------------------------------------------------- 1 | import { assertEquals } from 'std/testing/asserts.ts' 2 | 3 | import { printTable } from './main.ts' 4 | 5 | // First number: Edition number 6 | // Second number: Challenge number 7 | // Third number: Test number 8 | 9 | Deno.test('2022-021-001', () => assertEquals( 10 | printTable([ 11 | { name: 'PlayStation 5', quantity: 9234782374892 }, 12 | { name: 'Book Learn Web Dev', quantity: 23531 } 13 | ]), 14 | `++++++++++++++++++++++++++++++++++++++ 15 | | Gift | Quantity | 16 | | ------------------ | ------------- | 17 | | PlayStation 5 | 9234782374892 | 18 | | Book Learn Web Dev | 23531 | 19 | **************************************` 20 | )) 21 | 22 | Deno.test('2022-021-002', () => assertEquals( 23 | printTable([ 24 | { name: 'Game', quantity: 2 }, 25 | { name: 'Bike', quantity: 1 }, 26 | { name: 'Book', quantity: 3 } 27 | ]), 28 | `+++++++++++++++++++ 29 | | Gift | Quantity | 30 | | ---- | -------- | 31 | | Game | 2 | 32 | | Bike | 1 | 33 | | Book | 3 | 34 | *******************` 35 | )) 36 | 37 | Deno.test('2022-021-003', () => assertEquals( 38 | printTable([ 39 | { name: 'Game', quantity: 1234567890 } 40 | ]), 41 | `+++++++++++++++++++++ 42 | | Gift | Quantity | 43 | | ---- | ---------- | 44 | | Game | 1234567890 | 45 | *********************` 46 | )) 47 | -------------------------------------------------------------------------------- /2022/challenge-21/main.ts: -------------------------------------------------------------------------------- 1 | interface Gift { 2 | quantity: number 3 | name: string 4 | } 5 | 6 | function printTable(gifts: Gift[]) { 7 | const quantitySize = Math.max( 8 | 8, ...gifts.map((e) => e.quantity.toString().length) 9 | ) 10 | 11 | const giftSize = Math.max( 12 | 4, ...gifts.map((e) => e.name.length) 13 | ) 14 | 15 | const table = ['+'.repeat(giftSize + quantitySize + 7)] 16 | 17 | table.push( 18 | `| Gift${' '.repeat(giftSize - 3)}` + 19 | `| Quantity${' '.repeat(quantitySize - 7)}|` 20 | ) 21 | 22 | table.push( 23 | `| ${'-'.repeat(giftSize)} | ${'-'.repeat(quantitySize)} |` 24 | ) 25 | 26 | for (const gift of gifts) { 27 | table.push( 28 | `| ${gift.name}${' '.repeat(giftSize - gift.name.length + 1)}` + 29 | `| ${gift.quantity}${' '.repeat(quantitySize - gift.quantity.toString().length + 1)}|` 30 | ) 31 | } 32 | 33 | table.push('*'.repeat(giftSize + quantitySize + 7)) 34 | 35 | return table.join('\n') 36 | } 37 | 38 | // Only exporting the function to test it. 39 | export { printTable } 40 | -------------------------------------------------------------------------------- /2022/challenge-22/README.md: -------------------------------------------------------------------------------- 1 | # Challenge #22: The lights in sync 2 | 3 | ## 📖 Instructions 4 | 5 | Verify that all independent sequences of Christmas lighting systems are in strictly increasing order. We have two arrays: `systemNames` and `stepNumbers`. 6 | 7 | `systemNames` contains the names of the Christmas lighting systems, and `stepNumbers` contains the step numbers of each system. 8 | 9 | We must verify that the `stepNumbers` of each system are in strictly increasing order. If this is true, return `true`; otherwise, return `false`. 10 | 11 | For example: 12 | 13 | ```js 14 | const systemNames = ["tree_1", "tree_2", "house", "tree_1", "tree_2", "house"] 15 | const stepNumbers = [1, 33, 10, 2, 44, 20] 16 | 17 | checkStepNumbers(systemNames, stepNumbers) // => true 18 | 19 | // tree_1 has steps: [1, 2] 20 | // tree_2 has steps: [33, 44] 21 | // house has steps: [10, 20] 22 | 23 | // true: The steps of each system are in strictly increasing order 24 | 25 | checkStepNumbers(["tree_1", "tree_1", "house"], [2, 1, 10]) // => false 26 | 27 | // tree_1 has steps: [2, 1] 28 | // house has steps: [10] 29 | 30 | // false: tree_1 has steps in decreasing order 31 | ``` 32 | 33 | ## 📝 Note that: 34 | 35 | - The position of the system name in `systemNames` and the step number in `stepNumbers` correspond to the same system. 36 | - The steps in `stepNumbers` can be repeated for different systems. 37 | 38 | ## 📜 Results 39 | -------------------------------------------------------------------------------- /2022/challenge-22/main.test.ts: -------------------------------------------------------------------------------- 1 | import { assertEquals } from 'std/testing/asserts.ts' 2 | 3 | import { checkStepNumbers } from './main.ts' 4 | 5 | // First number: Edition number 6 | // Second number: Challenge number 7 | // Third number: Test number 8 | 9 | Deno.test('2022-022-001', () => assertEquals( 10 | checkStepNumbers( 11 | ["tree_1", "tree_2", "house", "tree_1", "tree_2", "house"], 12 | [1, 33, 10, 2, 44, 20] 13 | ), 14 | true 15 | )) 16 | 17 | Deno.test('2022-022-002', () => assertEquals( 18 | checkStepNumbers( 19 | ["tree_1", "tree_1", "house"], 20 | [2, 1, 10] 21 | ), 22 | false 23 | )) 24 | 25 | Deno.test('2022-022-003', () => assertEquals( 26 | checkStepNumbers( 27 | ["house", "house", "tree_1", "tree_1", "house", "tree_2", "tree_2", "tree_3"], 28 | [5, 2, 1, 2, 3, 4, 5, 6] 29 | ), 30 | false 31 | )) 32 | -------------------------------------------------------------------------------- /2022/challenge-22/main.ts: -------------------------------------------------------------------------------- 1 | function checkStepNumbers(systemNames: string[], stepNumbers: number[]): boolean { 2 | return systemNames.every( 3 | (name, i) => stepNumbers[i] <= stepNumbers[ 4 | i + systemNames.slice(i + 1).indexOf(name) + 1 5 | ] 6 | ) 7 | } 8 | 9 | // Only exporting the function to test it. 10 | export { checkStepNumbers } 11 | -------------------------------------------------------------------------------- /2022/challenge-23/main.test.ts: -------------------------------------------------------------------------------- 1 | import { assertEquals } from 'std/testing/asserts.ts' 2 | 3 | import { executeCommands } from './main.ts' 4 | 5 | // First number: Edition number 6 | // Second number: Challenge number 7 | // Third number: Test number 8 | 9 | Deno.test('2022-023-001', () => assertEquals( 10 | executeCommands([ 11 | 'MOV 5,V00', 12 | 'MOV 10,V01', 13 | 'DEC V00', 14 | 'ADD V00,V01', 15 | ]), 16 | [ 17 | 14, 18 | 10, 19 | 0, 20 | 0, 21 | 0, 22 | 0, 23 | 0, 24 | 0 25 | ] 26 | )) 27 | 28 | Deno.test('2022-023-002', () => assertEquals( 29 | executeCommands([ 30 | 'MOV 10,V00', 31 | 'DEC V00', 32 | 'INC V01', 33 | 'JMP 1', 34 | 'INC V06' 35 | ]), 36 | [ 37 | 0, 38 | 10, 39 | 0, 40 | 0, 41 | 0, 42 | 0, 43 | 1, 44 | 0 45 | ] 46 | )) 47 | 48 | Deno.test('2022-023-003', () => assertEquals( 49 | executeCommands([ 50 | 'MOV 255,V00', 51 | 'INC V00', 52 | 'DEC V01', 53 | 'DEC V01' 54 | ]), 55 | [ 56 | 0, 57 | 254, 58 | 0, 59 | 0, 60 | 0, 61 | 0, 62 | 0, 63 | 0 64 | ] 65 | )) 66 | -------------------------------------------------------------------------------- /2022/deno.json: -------------------------------------------------------------------------------- 1 | { 2 | "importMap": "importMap.json" 3 | } -------------------------------------------------------------------------------- /2022/importMap.json: -------------------------------------------------------------------------------- 1 | { 2 | "imports": { 3 | "std/": "https://deno.land/std@0.170.0/", 4 | "assert": "https://deno.land/std@0.208.0/assert/mod.ts" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /2023/.gitignore: -------------------------------------------------------------------------------- 1 | # Deno 2 | deno.lock 3 | -------------------------------------------------------------------------------- /2023/2023.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlamesX-128/adventjs/d6604424ecce422938fda3ab9fe81d545419245f/2023/2023.png -------------------------------------------------------------------------------- /2023/challenge-01/README.md: -------------------------------------------------------------------------------- 1 | # Challenge #1: 🎁 First gift repeated! 2 | 3 | In the toy factory of the North Pole, each toy has a unique identification number. 4 | 5 | However, due to an error in the toy machine, some numbers have been assigned to more than one toy. 6 | 7 | Find the first identification number that has been repeated, **where the second occurrence has the smallest index**! 8 | 9 | In other words, if there is more than one repeated number, you must return the number whose second occurrence appears first in the list. If there are no repeated numbers, return -1. 10 | 11 | ```js 12 | const giftIds = [2, 1, 3, 5, 3, 2] 13 | const firstRepeatedId = findFirstRepeated(giftIds) 14 | console.log(firstRepeatedId) // 3 15 | // Even though 2 and 3 are repeated 16 | // 3 appears second time first 17 | 18 | const giftIds2 = [1, 2, 3, 4] 19 | const firstRepeatedId2 = findFirstRepeated(giftIds2) 20 | console.log(firstRepeatedId2) // -1 21 | // It is -1 since no number is repeated 22 | 23 | const giftIds3 = [5, 1, 5, 1] 24 | const firstRepeatedId3 = findFirstRepeated(giftIds3) 25 | console.log(firstRepeatedId3) // 5 26 | ``` 27 | 28 | **Watch out!** The elves say this is a Google technical test. 29 | -------------------------------------------------------------------------------- /2023/challenge-01/main.test.ts: -------------------------------------------------------------------------------- 1 | import { assertEquals } from 'assert' 2 | 3 | import { findFirstRepeated } from './main.ts' 4 | 5 | Deno.test('Challenge #1: 🎁 First gift repeated!', () => { 6 | const data = [2, 1, 3, 5, 3, 2] 7 | 8 | const result = findFirstRepeated(data) 9 | const expected = 3 10 | 11 | assertEquals(result, expected) 12 | }) 13 | 14 | Deno.test('Challenge #1: 🎁 First gift repeated!', () => { 15 | const data = [1, 2, 3, 4] 16 | 17 | const result = findFirstRepeated(data) 18 | const expected = -1 19 | 20 | assertEquals(result, expected) 21 | }) 22 | 23 | Deno.test('Challenge #1: 🎁 First gift repeated!', () => { 24 | const data = [5, 1, 5, 1] 25 | 26 | const result = findFirstRepeated(data) 27 | const expected = 5 28 | 29 | assertEquals(result, expected) 30 | }) 31 | -------------------------------------------------------------------------------- /2023/challenge-01/main.ts: -------------------------------------------------------------------------------- 1 | // /(\d)(?=.*\1)/ for searching duplicated numbers 2 | 3 | function findFirstRepeated(gifts: number[]) { 4 | return gifts.find((e, i, arr) => arr.indexOf(e) != i) ?? -1 5 | } 6 | 7 | export { findFirstRepeated } 8 | -------------------------------------------------------------------------------- /2023/challenge-02/README.md: -------------------------------------------------------------------------------- 1 | # Challenge #2: 🏭 We start the factory 2 | 3 | In Santa's workshop, the elves have a **gift list** they wish to make and a limited set of materials. 4 | 5 | Gifts are strings of text and materials are characters. Your task is to write a function that, given a list of gifts and the available materials, returns a **list of the gifts that can be made**. 6 | 7 | A gift can be made if we have all the necessary materials to make it. 8 | 9 | ```js 10 | const gifts = ['tren', 'oso', 'pelota'] 11 | const materials = 'tronesa' 12 | 13 | manufacture(gifts, materials) // ["tren", "oso"] 14 | 15 | const gifts = ['juego', 'puzzle'] 16 | const materials = 'jlepuz' 17 | 18 | manufacture(gifts, materials) // ["puzzle"] 19 | 20 | const gifts = ['libro', 'ps5'] 21 | const materials = 'psli' 22 | 23 | manufacture(gifts, materials) // [] 24 | ``` 25 | -------------------------------------------------------------------------------- /2023/challenge-02/main.test.ts: -------------------------------------------------------------------------------- 1 | import { assertEquals } from 'assert' 2 | 3 | import { manufacture } from './main.ts' 4 | 5 | Deno.test('Challenge #2: 🏭 We start the factory', () => { 6 | const gifts = ['tren', 'oso', 'pelota'] 7 | const materials = 'tronesa' 8 | 9 | const result = manufacture(gifts, materials) 10 | const expected = ["tren", "oso"] 11 | 12 | assertEquals(result, expected) 13 | }) 14 | 15 | Deno.test('Challenge #2: 🏭 We start the factory', () => { 16 | const gifts = ['juego', 'puzzle'] 17 | const materials = 'jlepuz' 18 | 19 | const result = manufacture(gifts, materials) 20 | const expected = ["puzzle"] 21 | 22 | assertEquals(result, expected) 23 | }) 24 | 25 | Deno.test('Challenge #2: 🏭 We start the factory', () => { 26 | const gifts = ['libro', 'ps5'] 27 | const materials = 'psli' 28 | 29 | const result = manufacture(gifts, materials) 30 | const expected: string[] = [] 31 | 32 | assertEquals(result, expected) 33 | }) 34 | -------------------------------------------------------------------------------- /2023/challenge-02/main.ts: -------------------------------------------------------------------------------- 1 | function manufacture(gifts: string[], materials: string) { 2 | return gifts.filter( 3 | (gift) => [...gift].every( 4 | (char) => materials.includes(char) 5 | ) 6 | ) 7 | } 8 | 9 | export { manufacture } 10 | -------------------------------------------------------------------------------- /2023/challenge-03/README.md: -------------------------------------------------------------------------------- 1 | # Challenge #3: 😏 The naughty elf 2 | 3 | In Santa's workshop, **a mischievous elf** has been playing around with the gift production line, adding or removing an unplanned step. 4 | 5 | You have the original sequence of `original` manufacturing steps and the modified `modified` sequence that may include an extra step or be missing a step. 6 | 7 | Your task is to **write a function that identifies and returns the first extra step that was added or removed in the manufacturing chain**. If there is no difference between the sequences, return an empty string. 8 | 9 | ```js 10 | const original = 'abcd' 11 | const modified = 'abcde' 12 | findNaughtyStep(original, modified) // 'e' 13 | 14 | const original = 'stepfor' 15 | const modified = 'stepor' 16 | findNaughtyStep(original, modified) // 'f' 17 | 18 | const original = 'abcde' 19 | const modified = 'abcde' 20 | findNaughtyStep(original, modified) // '' 21 | ``` 22 | 23 | Please, keep in mind: 24 | 25 | - There will always be one different step or none. 26 | - The modification can occur anywhere in the string. 27 | - The original steps could be empty 28 | -------------------------------------------------------------------------------- /2023/challenge-03/main.test.ts: -------------------------------------------------------------------------------- 1 | import { assertEquals } from 'assert' 2 | 3 | import { findNaughtyStep } from './main.ts' 4 | 5 | Deno.test('Challenge #3: 😏 The naughty elf', () => { 6 | const original = 'abcd' 7 | const modified = 'abcde' 8 | 9 | const result = findNaughtyStep(original, modified) 10 | const expected = 'e' 11 | 12 | assertEquals(result, expected) 13 | }) 14 | 15 | Deno.test('Challenge #3: 😏 The naughty elf', () => { 16 | const original = 'stepfor' 17 | const modified = 'stepor' 18 | 19 | const result = findNaughtyStep(original, modified) 20 | const expected = 'f' 21 | 22 | assertEquals(result, expected) 23 | }) 24 | 25 | Deno.test('Challenge #3: 😏 The naughty elf', () => { 26 | const original = 'abcde' 27 | const modified = 'abcde' 28 | 29 | const result = findNaughtyStep(original, modified) 30 | const expected = '' 31 | 32 | assertEquals(result, expected) 33 | }) 34 | -------------------------------------------------------------------------------- /2023/challenge-03/main.ts: -------------------------------------------------------------------------------- 1 | function findNaughtyStep(original: string, modified: string) { 2 | const [shorterStr, longerStr] = [original, modified].sort( 3 | (a, b) => b.length - a.length 4 | ); 5 | 6 | for (let i = 0; i < shorterStr.length; i++) { 7 | if (shorterStr[i] !== longerStr[i]) return shorterStr[i]; 8 | } 9 | 10 | return '' 11 | } 12 | 13 | export { findNaughtyStep } 14 | -------------------------------------------------------------------------------- /2023/challenge-04/README.md: -------------------------------------------------------------------------------- 1 | # Challenge #4: 😵‍💫 Turn the parentheses around 2 | 3 | In 🎅 Santa's workshop, **some Christmas messages have been written in a peculiar way**: the words within the brackets must be read backwards. 4 | 5 | **Santa needs these messages to be correctly formatted**. Your task is to write a function that takes a string and reverses the characters within each pair of parentheses, removing the parentheses as well. 6 | 7 | However, bear in mind that there may be nested parentheses, so you should reverse the characters in the correct order. 8 | 9 | ```js 10 | const a = decode('hola (odnum)') 11 | console.log(a) // hola mundo 12 | 13 | const b = decode('(olleh) (dlrow)!') 14 | console.log(b) // hello world! 15 | 16 | const c = decode('sa(u(cla)atn)s') 17 | console.log(c) // santaclaus 18 | 19 | // Step by step: 20 | // 1. Reverse the nested -> sa(ualcatn)s 21 | // 2. Reverse the remaining one -> santaclaus 22 | ``` 23 | 24 | Notes: 25 | 26 | - The input strings will always be well formed with parentheses that match correctly, you do not need to validate them. 27 | - There should not be any parentheses left in the final message. 28 | - The maximum nesting level is 2. 29 | -------------------------------------------------------------------------------- /2023/challenge-04/main.test.ts: -------------------------------------------------------------------------------- 1 | import { assertEquals } from 'assert' 2 | 3 | import { decode } from './main.ts' 4 | 5 | Deno.test('Challenge #4: 😵‍💫 Turn the parentheses around', () => { 6 | const message = 'hola (odnum)' 7 | 8 | const result = decode(message) 9 | const expected = 'hola mundo' 10 | 11 | assertEquals(result, expected) 12 | }) 13 | 14 | Deno.test('Challenge #4: 😵‍💫 Turn the parentheses around', () => { 15 | const message = '(olleh) (dlrow)!' 16 | 17 | const result = decode(message) 18 | const expected = 'hello world!' 19 | 20 | assertEquals(result, expected) 21 | }) 22 | 23 | Deno.test('Challenge #4: 😵‍💫 Turn the parentheses around', () => { 24 | const message = 'sa(u(cla)atn)s' 25 | 26 | const result = decode(message) 27 | const expected = 'santaclaus' 28 | 29 | assertEquals(result, expected) 30 | }) 31 | -------------------------------------------------------------------------------- /2023/challenge-04/main.ts: -------------------------------------------------------------------------------- 1 | function decode(message: string) { 2 | const stack = []; 3 | let result = ''; 4 | 5 | for (const char of message) { 6 | if (char === '(') { 7 | stack.push(result); 8 | result = ''; 9 | } else if (char === ')') { 10 | result = stack.pop() 11 | + result.split('').reverse().join(''); 12 | } else { 13 | result += char; 14 | } 15 | } 16 | 17 | return result; 18 | } 19 | 20 | export { decode } 21 | -------------------------------------------------------------------------------- /2023/challenge-05/main.test.ts: -------------------------------------------------------------------------------- 1 | import { assertEquals } from 'assert' 2 | 3 | import { cyberReindeer } from './main.ts' 4 | 5 | Deno.test("Challenge #5: 🛷 Santa's CyberTruck", () => { 6 | const road = 'S..|...|..' 7 | const time = 10 8 | 9 | const result = cyberReindeer(road, time) 10 | const expected = [ 11 | 'S..|...|..', // initial state 12 | '.S.|...|..', // sled advances on the road 13 | '..S|...|..', // sled advances on the road 14 | '..S|...|..', // sled stops at the barrier 15 | '..S|...|..', // sled stops at the barrier 16 | '...S...*..', // barrier opens, sled advances 17 | '...*S..*..', // sled advances on the road 18 | '...*.S.*..', // sled advances on the road 19 | '...*..S*..', // sled advances on the road 20 | '...*...S..', // passes through the open barrier 21 | ] 22 | 23 | assertEquals(result, expected) 24 | }) 25 | -------------------------------------------------------------------------------- /2023/challenge-05/main.ts: -------------------------------------------------------------------------------- 1 | function cyberReindeer(road: string, time: number) { 2 | const raceData = [road] 3 | 4 | road = road.replace('S', '.') 5 | 6 | let reindeerPosition = 2 7 | let pointer = 2 8 | 9 | let raceTime = 1 10 | 11 | while (raceTime < time) { 12 | const symbol = road[pointer] 13 | 14 | raceData.push( 15 | road.slice(0, reindeerPosition - 1) + 'S' + 16 | road.slice(reindeerPosition) 17 | ) 18 | 19 | if (++raceTime === 5) { 20 | road = road.replace(/\|/g, '*') 21 | } else if (symbol === '|') { 22 | continue 23 | } 24 | 25 | reindeerPosition++ 26 | pointer++ 27 | } 28 | 29 | return raceData 30 | } 31 | 32 | export { cyberReindeer } 33 | -------------------------------------------------------------------------------- /2023/challenge-06/README.md: -------------------------------------------------------------------------------- 1 | # Challenge #6: 🦌 The reindeer on trial 2 | 3 | The elves are cataloging Santa's reindeer 🦌 based on the distance they can travel. 4 | 5 | For this, they have a text string movements where each character represents the direction of the reindeer's movement: 6 | 7 | - `>` = Moves to the right 8 | - `<` = Moves to the left 9 | - `*` = Can move forward or backward 10 | 11 | For example, if the movement is `>>*<`, it goes to the right twice, then it can go either left or right (whichever maximizes the final traveled distance) and then left. 12 | 13 | The elves want to know what the maximum distance the reindeer travels is **after completing all movements**. 14 | 15 | **In the previous example, the maximum distance the reindeer travels is 2**. It goes to the right twice +2, then with the * it can go to the right again to maximize the distance +1 and then it goes to the left -1. 16 | 17 | Create a maxDistance function that takes the text string movements and returns **the maximum distance** that the reindeer can travel **in any direction**: 18 | 19 | ```js 20 | const movements = '>>*<' 21 | const result = maxDistance(movements) 22 | console.log(result) // -> 2 23 | 24 | const movements2 = '<<<>' 25 | const result2 = maxDistance(movements2) 26 | console.log(result2) // -> 2 27 | 28 | const movements3 = '>***>' 29 | const result3 = maxDistance(movements3) 30 | console.log(result3) // -> 5 31 | ``` 32 | 33 | Keep in mind that it doesn't matter whether it is to the left or right, the distance is **the absolute value of the maximum distance traveled at the end of the movements**. 34 | -------------------------------------------------------------------------------- /2023/challenge-06/main.test.ts: -------------------------------------------------------------------------------- 1 | import { assertEquals } from 'assert' 2 | 3 | import { maxDistance } from './main.ts' 4 | 5 | Deno.test('# Challenge #6: 🦌 The reindeer on trial', () => { 6 | const movements = '>>*<' 7 | 8 | const result = maxDistance(movements) 9 | const expected = 2 10 | 11 | assertEquals(result, expected) 12 | }) 13 | 14 | Deno.test('# Challenge #6: 🦌 The reindeer on trial', () => { 15 | const movements = '<<<<<' 16 | 17 | const result = maxDistance(movements) 18 | const expected = 5 19 | 20 | assertEquals(result, expected) 21 | }) 22 | 23 | Deno.test('# Challenge #6: 🦌 The reindeer on trial', () => { 24 | const movements = '>***>' 25 | 26 | const result = maxDistance(movements) 27 | const expected = 5 28 | 29 | assertEquals(result, expected) 30 | }) 31 | -------------------------------------------------------------------------------- /2023/challenge-06/main.ts: -------------------------------------------------------------------------------- 1 | function maxDistance(movements: string) { 2 | const steps = [...movements]; 3 | const movementValues: Record = { 4 | '<': -1, 5 | '>': 1, 6 | }; 7 | 8 | let a = 0; 9 | let b = 0; 10 | 11 | for (const step of steps) { 12 | step !== '*' 13 | ? a += movementValues[step] 14 | : b++ 15 | } 16 | 17 | return Math.abs(a) + b; 18 | } 19 | 20 | export { maxDistance } 21 | -------------------------------------------------------------------------------- /2023/challenge-07/README.md: -------------------------------------------------------------------------------- 1 | # Challenge #7: 📦 The 3D boxes 2 | 3 | Santa is experimenting with new gift designs and **he needs your help to visualize them in 3D**. 4 | 5 | Your task is to write a function that, given a size `n` (integer), **generates a drawing of a 3D gift** using ASCII characters. 6 | 7 | The lines of the gifts are drawn with `#` and the faces with the symbol passed to us as a parameter: 8 | 9 | ```js 10 | drawGift(4, '+') 11 | 12 | /* 13 | #### 14 | #++## 15 | #++#+# 16 | ####++# 17 | #++#+# 18 | #++## 19 | #### 20 | */ 21 | 22 | drawGift(5, '*') 23 | /* 24 | ##### 25 | #***## 26 | #***#*# 27 | #***#**# 28 | #####***# 29 | #***#**# 30 | #***#*# 31 | #***## 32 | ##### 33 | */ 34 | 35 | drawGift(1, '^') 36 | /* 37 | # 38 | */ 39 | ``` 40 | 41 | Important: We have been told that **there is always to leave a newline at the end of the drawing**. 42 | -------------------------------------------------------------------------------- /2023/challenge-07/main.test.ts: -------------------------------------------------------------------------------- 1 | import { assertEquals } from 'assert' 2 | 3 | import { drawGift } from './main.ts' 4 | 5 | Deno.test('Challenge #7: 📦 The 3D boxes', () => { 6 | const size = 5 7 | const symbol = '*' 8 | 9 | const result = drawGift(size, symbol) 10 | const expected = 11 | ` ##### 12 | #***## 13 | #***#*# 14 | #***#**# 15 | #####***# 16 | #***#**# 17 | #***#*# 18 | #***## 19 | ##### 20 | ` 21 | 22 | assertEquals(result, expected) 23 | }) 24 | -------------------------------------------------------------------------------- /2023/challenge-08/README.md: -------------------------------------------------------------------------------- 1 | # Challenge #8: 🏬 Sorting the warehouse 2 | 3 | The elves are very busy in Santa Claus' workshop organizing gifts 🎁 for Christmas Eve 🎄. 4 | 5 | The input format is special, as it indicates the number of gifts and the type of gift with letters from `a` to `z`. For example, `'66a11b'` means 66 a gifts and 11 b gifts. 6 | 7 | The elves have a **special system** for organizing the gifts: 8 | 9 | - Every 10 gifts of the same type are packed in a box, represented by `{x}`. For example, 20 type a gifts are packed in two boxes like this: `{a}{a}`. 10 | 11 | - Every 5 boxes are stacked on a pallet, represented by `[x]`. For example, 10 a boxes are stacked on 2 pallets in this way: `[a][a]` 12 | 13 | - Any additional gift is placed in a bag, represented by `()` and all of them are placed inside. For example, 4 b gifts are placed in a bag like this `(bbbb)` 14 | 15 | **The gifts are then placed in the following order**: pallets, *boxes and bags*. And the gifts appear in the same order as the input string. 16 | 17 | Your task is to write a function `organizeGifts` that takes a string of gifts as an argument and returns a string representing the warehouse. 18 | 19 | ```js 20 | const result1 = organizeGifts('76a11b') 21 | console.log(result1) 22 | // `[a]{a}{a}(aaaaaa){b}(b)` 23 | 24 | /* Explanation: 25 | 26 | 76a: 76 gifts type 'a' would be packed in 7 boxes and 6 gifts would be left, resulting in 1 pallet [a] (for the first 5 boxes), 2 loose boxes {a}{a} and a bag with 6 gifts (aaaaaa) 27 | 28 | 11b: 11 gifts type 'b' would be packed in 1 box and 1 gift would be left, resulting in 1 loose box {b} and a bag with 1 gift (b) 29 | ``` 30 | -------------------------------------------------------------------------------- /2023/challenge-08/main.test.ts: -------------------------------------------------------------------------------- 1 | import { assertEquals } from 'assert' 2 | 3 | import { organizeGifts } from './main.ts' 4 | 5 | Deno.test('Challenge #8: 🏬 Sorting the warehouse', () => { 6 | const gifts = '76a11b' 7 | 8 | const result = organizeGifts(gifts) 9 | const expected = '[a]{a}{a}(aaaaaa){b}(b)' 10 | 11 | assertEquals(result, expected) 12 | }) 13 | -------------------------------------------------------------------------------- /2023/challenge-08/main.ts: -------------------------------------------------------------------------------- 1 | function organizeGifts(gifts: string): string { 2 | let result = ''; 3 | 4 | for (const giftInfo of gifts.match(/\d+\w/g) || []) { 5 | const symbol = giftInfo.slice(-1); 6 | const size = +giftInfo.slice(0, -1); 7 | 8 | const repetitions = ~~(size / 50); 9 | const remainingBoxes = ~~((size % 50) / 10); 10 | const remainingGifts = size % 10; 11 | 12 | result += `[${symbol}]`.repeat(repetitions); 13 | result += `{${symbol}}`.repeat(remainingBoxes); 14 | 15 | if (remainingGifts > 0) { 16 | result += `(${symbol.repeat(remainingGifts)})`; 17 | } 18 | } 19 | 20 | return result; 21 | } 22 | 23 | export { organizeGifts } 24 | -------------------------------------------------------------------------------- /2023/challenge-09/README.md: -------------------------------------------------------------------------------- 1 | # Challenge #9: 🚦 Switch the lights 2 | 3 | They are turning on the **Christmas lights** 🎄 in the city and, as every year, they have to be fixed! 4 | 5 | The lights are of two colors: 🔴 and 🟢 . For the effect to be appropriate, **they must always alternate**.** That is, if the first light is red, the second must be green, the third red, the fourth green, etc. 6 | 7 | We have been asked to write a function `adjustLights` that, given an array of strings with the color of each light, return the **minimum number** of lights that need to be changed for the colors to alternate. 8 | 9 | ```js 10 | adjustLights(['🟢', '🔴', '🟢', '🟢', '🟢']) 11 | // -> 1 (you change the fourth light to 🔴) 12 | 13 | adjustLights(['🔴', '🔴', '🟢', '🟢', '🔴']) 14 | // -> 2 (you change the second light to 🟢 and the third to 🔴) 15 | 16 | adjustLights(['🟢', '🔴', '🟢', '🔴', '🟢']) 17 | // -> 0 (they are already alternating) 18 | 19 | adjustLights(['🔴', '🔴', '🔴']) 20 | // -> 1 (you change the second light to 🟢) 21 | ``` 22 | -------------------------------------------------------------------------------- /2023/challenge-09/main.test.ts: -------------------------------------------------------------------------------- 1 | import { assertEquals } from 'assert' 2 | 3 | import { adjustLights } from './main.ts' 4 | 5 | Deno.test('Challenge #9: 🚦 Switch the lights', () => { 6 | const lights = ['🟢', '🔴', '🟢', '🟢', '🟢'] 7 | 8 | const result = adjustLights(lights) 9 | const expected = 1 10 | 11 | assertEquals(result, expected) 12 | }) 13 | 14 | Deno.test('Challenge #9: 🚦 Switch the lights', () => { 15 | const lights = ['🔴', '🔴', '🟢', '🟢', '🔴'] 16 | 17 | const result = adjustLights(lights) 18 | const expected = 2 19 | 20 | assertEquals(result, expected) 21 | }) 22 | 23 | Deno.test('Challenge #9: 🚦 Switch the lights', () => { 24 | const lights = ['🟢', '🔴', '🟢', '🔴', '🟢'] 25 | 26 | const result = adjustLights(lights) 27 | const expected = 0 28 | 29 | assertEquals(result, expected) 30 | }) 31 | 32 | Deno.test('Challenge #9: 🚦 Switch the lights', () => { 33 | const lights = ['🔴', '🔴', '🔴'] 34 | 35 | const result = adjustLights(lights) 36 | const expected = 1 37 | 38 | assertEquals(result, expected) 39 | }) 40 | -------------------------------------------------------------------------------- /2023/challenge-09/main.ts: -------------------------------------------------------------------------------- 1 | // Ingenious solution developed by Hectorreto/adventjs-solutions. 2 | 3 | type ParityRule = { 4 | [key: string]: [number, number]; 5 | }; 6 | 7 | type ParityRules = { 8 | [key: number]: ParityRule; 9 | }; 10 | 11 | function adjustLights(lights: string[]) { 12 | let totalChangesForGreen = 0; 13 | let totalChangesForRed = 0; 14 | 15 | const parityRules: ParityRules = { 16 | 0: { 17 | '🔴': [1, 0], 18 | '🟢': [0, 1], 19 | }, 20 | 1: { 21 | '🔴': [0, 1], 22 | '🟢': [1, 0], 23 | } 24 | }; 25 | 26 | let parity = 0; 27 | 28 | for (const light of lights) { 29 | const [changesForRed, changesForGreen] = parityRules[parity][light]; 30 | 31 | totalChangesForGreen += changesForGreen; 32 | totalChangesForRed += changesForRed; 33 | 34 | parity = 1 - parity; 35 | } 36 | 37 | return Math.min(totalChangesForRed, totalChangesForGreen); 38 | } 39 | 40 | 41 | export { adjustLights } 42 | -------------------------------------------------------------------------------- /2023/challenge-10/README.md: -------------------------------------------------------------------------------- 1 | # Challenge #10: 🎄 Create your own Christmas tree 2 | 3 | What an idea Sam Elfman has had! He wants to offer a service that creates a **customized Christmas tree** 🎄 in a matter of seconds. 4 | 5 | To create it, we are given a **string to form the tree** and a **number that indicates its height**. 6 | 7 | Each character of the string represents an ornament of the tree, and we use them **cyclically** until we reach the indicated height. At **least, they will always pass us one**. 8 | 9 | We must return a multiline **string** with the Christmas tree made with the ornaments, the indicated height **plus a final line with the trunk formed by the character |** in the center and, finally, a newline `\n`. 10 | 11 | For example, if we receive the string `"123"` and the number `4` as height, we would have to build this tree: 12 | 13 | ``` 14 | 1 15 | 2 3 16 | 1 2 3 17 | 1 2 3 1 2 18 | | 19 | ``` 20 | 21 | If we receive the string `*@o` and the number `3`, the tree we should return is: 22 | 23 | ``` 24 | * 25 | @ o 26 | * @ o 27 | | 28 | ``` 29 | 30 | Note: 31 | 32 | - The tree should always be centered, for that reason add blank spaces to the left of each line. 33 | 34 | - Create spaces only to the left of each line of the tree. Do not leave blank spaces to the right. 35 | 36 | - The ornaments have a white space between them for separation. 37 | -------------------------------------------------------------------------------- /2023/challenge-10/main.test.ts: -------------------------------------------------------------------------------- 1 | import { assertEquals } from 'assert' 2 | 3 | import { createChristmasTree } from './main.ts' 4 | 5 | Deno.test('Challenge #10: 🎄 Create your own Christmas tree', () => { 6 | const ornaments = 'x' 7 | const height = 3 8 | 9 | const result = createChristmasTree(ornaments, height) 10 | const expected = ` x\n x x\nx x x\n |\n` 11 | 12 | assertEquals(result, expected) 13 | }) 14 | 15 | Deno.test('Challenge #10: 🎄 Create your own Christmas tree', () => { 16 | const ornaments = 'xo' 17 | const height = 4 18 | 19 | const result = createChristmasTree(ornaments, height) 20 | const expected = ` x\n o x\n o x o\nx o x o\n |\n` 21 | 22 | assertEquals(result, expected) 23 | }) 24 | -------------------------------------------------------------------------------- /2023/challenge-10/main.ts: -------------------------------------------------------------------------------- 1 | // Cognitive complexity: 3, lower score, and more realistic. 2 | function _createChristmasTree(ornaments: string, height: number): string { 3 | const overflow = ornaments.length; 4 | 5 | let pointer = 0; 6 | let tree = ''; 7 | 8 | for (let i = 1; i <= height; i++) { 9 | let row = ' '.repeat(height - i); 10 | 11 | for (let j = 0; j < i; j++) { 12 | row += ornaments[pointer++ % overflow] + ' '; 13 | } 14 | 15 | tree += row.trimEnd() + '\n'; 16 | } 17 | 18 | return tree += ' '.repeat(height - 1) + '|\n'; 19 | } 20 | 21 | // Cognitive complexity: 1, better score, and less realistic. 22 | function createChristmasTree(ornaments: string, height: number): string { 23 | const overflow = ornaments.length; 24 | let tree = ''; 25 | 26 | let pointer = 0; 27 | let i = 1; 28 | 29 | for (const _ of ' '.repeat(height)) { 30 | let row = ' '.repeat(height - i); 31 | 32 | for (const _ of ' '.repeat(i)) { 33 | row += ornaments[pointer++ % overflow] + ' '; 34 | } 35 | 36 | tree += row.trimEnd() + '\n'; 37 | i++ 38 | } 39 | 40 | return tree += ' '.repeat(height - 1) + '|\n'; 41 | } 42 | 43 | export { createChristmasTree } 44 | -------------------------------------------------------------------------------- /2023/challenge-11/README.md: -------------------------------------------------------------------------------- 1 | # Challenge #11: 📖 The studious elves 2 | 3 | In Santa's workshop, the elves love puzzles 🧠. This year, they have created a special one: a challenge to form a Christmas palindrome. 4 | 5 | **A palindrome is a word that reads the same forwards and backwards**. The elves want to know if it is possible to form a palindrome by making, at most, one exchange of letters. 6 | 7 | Create a function `getIndexsForPalindrome` that receives a string and returns: 8 | 9 | - If it is already a palindrome, an empty array. 10 | - If it is not possible, null. 11 | - If a palindrome can be formed with one change, an array with the two positions (indexes) that must be swapped to create it. 12 | 13 | For example: 14 | 15 | ```js 16 | getIndexsForPalindrome('anna') // [] 17 | getIndexsForPalindrome('abab') // [0, 1] 18 | getIndexsForPalindrome('abac') // null 19 | getIndexsForPalindrome('aaaaaaaa') // [] 20 | getIndexsForPalindrome('aaababa') // [1, 3] 21 | getIndexsForPalindrome('caababa') // null 22 | ``` 23 | 24 | If the palindrome can be formed with different swaps, **always return the first one found**. 25 | -------------------------------------------------------------------------------- /2023/challenge-11/main.test.ts: -------------------------------------------------------------------------------- 1 | import { assertEquals } from 'assert' 2 | 3 | import { getIndexsForPalindrome } from './main.ts' 4 | 5 | Deno.test('Challenge #11: 📖 The studious elves', () => { 6 | const word = 'anna' 7 | 8 | const result = getIndexsForPalindrome(word) 9 | const expected: number[] = [] 10 | 11 | assertEquals(result, expected) 12 | }) 13 | 14 | Deno.test('Challenge #11: 📖 The studious elves', () => { 15 | const word = 'abab' 16 | 17 | const result = getIndexsForPalindrome(word) 18 | const expected: number[] = [0, 1] 19 | 20 | assertEquals(result, expected) 21 | }) 22 | 23 | Deno.test('Challenge #11: 📖 The studious elves', () => { 24 | const word = 'abac' 25 | 26 | const result = getIndexsForPalindrome(word) 27 | const expected = null 28 | 29 | assertEquals(result, expected) 30 | }) 31 | -------------------------------------------------------------------------------- /2023/challenge-11/main.ts: -------------------------------------------------------------------------------- 1 | function getIndexsForPalindrome(word: string) { 2 | const isPalindrome = (arr: string | string[]) => { 3 | return [...arr].reverse().every( 4 | (char, index) => char === arr[index] 5 | ); 6 | }; 7 | 8 | if (isPalindrome(word)) { 9 | return []; 10 | } 11 | 12 | let i = 0; 13 | for (const char1 of word) { 14 | let j = i + 1; 15 | for (const char2 of word.slice(j)) { 16 | const swapped = [...word]; 17 | 18 | swapped[i] = char2; 19 | swapped[j] = char1; 20 | 21 | if (isPalindrome(swapped)) { 22 | return [i, j]; 23 | } 24 | 25 | j++; 26 | } 27 | 28 | i++; 29 | } 30 | 31 | return null; 32 | } 33 | 34 | export { getIndexsForPalindrome } 35 | -------------------------------------------------------------------------------- /2023/challenge-12/main.test.ts: -------------------------------------------------------------------------------- 1 | import { assertEquals } from 'assert' 2 | 3 | import { checkIsValidCopy } from './main.ts' 4 | 5 | Deno.test('Challenge #12: 📸 Is it a valid copy?', () => { 6 | const oriignal = 'Santa Claus is coming' 7 | const copy = 'sa#ta cl#us is comin#' 8 | 9 | const result = checkIsValidCopy(oriignal, copy) 10 | const expected = true 11 | 12 | assertEquals(result, expected) 13 | }) 14 | 15 | Deno.test('Challenge #12: 📸 Is it a valid copy?', () => { 16 | const oriignal = 'Santa Claus is coming' 17 | const copy = 'p#nt: cla#s #s c+min#' 18 | 19 | const result = checkIsValidCopy(oriignal, copy) 20 | const expected = false 21 | 22 | assertEquals(result, expected) 23 | }) 24 | 25 | Deno.test('Challenge #12: 📸 Is it a valid copy?', () => { 26 | const oriignal = 'Santa Claus' 27 | const copy = 's#+:. c:. s' 28 | 29 | const result = checkIsValidCopy(oriignal, copy) 30 | const expected = true 31 | 32 | assertEquals(result, expected) 33 | }) 34 | -------------------------------------------------------------------------------- /2023/challenge-13/README.md: -------------------------------------------------------------------------------- 1 | # Challenge #13: ⌚️ Calculating the time 2 | 3 | The elves are preparing for **Christmas Eve** and they need your help to determine if they have enough time or not ⏳. 4 | 5 | For this, they give you an array with the duration of each delivery. The format of the duration is `HH:mm:ss`, the deliveries start at `00:00:00` and the time limit is `07:00:00`. 6 | 7 | Your function must return the time they will lack or the time they will have left over in order to finish the deliveries. The format of the returned duration should be `HH:mm:ss`. 8 | 9 | If they finish before `07:00:00`, the remaining time until `07:00:00` should be displayed with a negative sign. For example, **if they have 1 hour and 30 minutes left over, return `-01:30:00`** 10 | 11 | ```js 12 | calculateTime(['00:10:00', '01:00:00', '03:30:00']) 13 | // '-02:20:00' 14 | 15 | calculateTime(['02:00:00', '05:00:00', '00:30:00']) 16 | // '00:30:00' 17 | 18 | calculateTime([ 19 | '00:45:00', 20 | '00:45:00', 21 | '00:00:30', 22 | '00:00:30' 23 | ]) // '-05:29:00' 24 | ``` 25 | -------------------------------------------------------------------------------- /2023/challenge-13/main.test.ts: -------------------------------------------------------------------------------- 1 | import { assertEquals } from 'assert' 2 | 3 | import { calculateTime } from './main.ts' 4 | 5 | Deno.test('Challenge #13: ⌚️ Calculating the time', () => { 6 | const deliveries = ['00:10:00', '01:00:00', '03:30:00'] 7 | 8 | const result = calculateTime(deliveries) 9 | const expected = '-02:20:00' 10 | 11 | assertEquals(result, expected) 12 | }) 13 | 14 | Deno.test('Challenge #13: ⌚️ Calculating the time', () => { 15 | const deliveries = ['02:00:00', '05:00:00', '00:30:00'] 16 | 17 | const result = calculateTime(deliveries) 18 | const expected = '00:30:00' 19 | 20 | assertEquals(result, expected) 21 | }) 22 | -------------------------------------------------------------------------------- /2023/challenge-13/main.ts: -------------------------------------------------------------------------------- 1 | function calculateTime(deliveries: string[]) { 2 | let totalSeconds = 0; 3 | 4 | for (const delivery of deliveries) { 5 | const [hour, minute, second] = delivery.split(':'); 6 | 7 | totalSeconds += +hour * 3600 + +minute * 60 + +second; 8 | } 9 | 10 | const timeLimitSeconds = 7 * 3600; 11 | const remainingTimeSeconds = totalSeconds - timeLimitSeconds; 12 | 13 | const signType = ['', '-']; 14 | const sign = signType[Number(remainingTimeSeconds < 0)]; 15 | 16 | const absoluteRemainingTime = Math.abs(remainingTimeSeconds); 17 | 18 | const hours = Math.floor(absoluteRemainingTime / 3600); 19 | const minutes = Math.floor((absoluteRemainingTime % 3600) / 60); 20 | const seconds = absoluteRemainingTime % 60; 21 | 22 | return ( 23 | sign 24 | + hours.toString().padStart(2, '0') + ':' 25 | + minutes.toString().padStart(2, '0') + ':' 26 | + seconds.toString().padStart(2, '0') 27 | ); 28 | } 29 | 30 | export { calculateTime } 31 | -------------------------------------------------------------------------------- /2023/challenge-14/README.md: -------------------------------------------------------------------------------- 1 | # Challenge #14: 🚨 Avoid the alarm 2 | 3 | With the rise of social media, Santa Claus **is terrified that children might wake up while he is delivering gifts in their homes**, use their phone to record him and go viral on TikTok. 4 | 5 | He wants to avoid this at all costs. Each house on that street has a number of prepared gifts. However, **the houses have a security system connected between adjacent houses**, so **he can't leave gifts in two consecutive houses**, or the alarm will be triggered and alert the children. 6 | 7 | Given an **array of non-negative integers gifts** that represents the number of gifts in each house, your task is to help Santa Claus determine the **maximum number of gifts he can deliver** in one night without setting off any alarms. 8 | 9 | ```js 10 | maxGifts([2, 4, 2]) // 4 (4) 11 | maxGifts([5, 1, 1, 5]) // 10 (5 + 5) 12 | maxGifts([4, 1, 1, 4, 2, 1]) // 9 (4 + 4 + 1) 13 | maxGifts([1, 3, 1, 3, 100]) // 103 (3 + 100) 14 | ``` 15 | -------------------------------------------------------------------------------- /2023/challenge-14/main.test.ts: -------------------------------------------------------------------------------- 1 | import { assertEquals } from 'assert' 2 | 3 | import { maxGifts } from './main.ts' 4 | 5 | Deno.test('Challenge #14: 🚨 Avoid the alarm', () => { 6 | const gifts: number[] = [2, 4, 2] 7 | 8 | const result = maxGifts(gifts) 9 | const expected = 4 10 | 11 | assertEquals(result, expected) 12 | }) 13 | 14 | Deno.test('Challenge #14: 🚨 Avoid the alarm', () => { 15 | const gifts: number[] = [5, 1, 1, 5] 16 | 17 | const result = maxGifts(gifts) 18 | const expected = 10 19 | 20 | assertEquals(result, expected) 21 | }) 22 | 23 | Deno.test('Challenge #14: 🚨 Avoid the alarm', () => { 24 | const gifts: number[] = [4, 1, 1, 4, 2, 1] 25 | 26 | const result = maxGifts(gifts) 27 | const expected = 9 28 | 29 | assertEquals(result, expected) 30 | }) 31 | 32 | Deno.test('Challenge #14: 🚨 Avoid the alarm', () => { 33 | const gifts: number[] = [1, 3, 1, 3, 100] 34 | 35 | const result = maxGifts(gifts) 36 | const expected = 103 37 | 38 | assertEquals(result, expected) 39 | }) 40 | -------------------------------------------------------------------------------- /2023/challenge-14/main.ts: -------------------------------------------------------------------------------- 1 | function maxGifts(houses: number[]) { 2 | const n = houses.length; 3 | const maxGiftsArray: number[] = Array.from({ length: n }) 4 | 5 | maxGiftsArray[1] = Math.max(houses[0], houses[1]); 6 | maxGiftsArray[0] = houses[0]; 7 | 8 | let i = 1; 9 | for (const house of houses.slice(2)) { 10 | maxGiftsArray[++i] = Math.max( 11 | maxGiftsArray[i - 1], maxGiftsArray[i - 2] + house 12 | ); 13 | } 14 | 15 | return maxGiftsArray[n - 1]; 16 | } 17 | 18 | export { maxGifts } 19 | -------------------------------------------------------------------------------- /2023/challenge-15/README.md: -------------------------------------------------------------------------------- 1 | # Challenge #15: ↔️ Autonomous robot 2 | 3 | We are programming some **robots** called _giftbot_ 🤖🎁 that autonomously navigate gift warehouses. 4 | 5 | We are creating a function to which we pass: the _warehouse_ 🏬 they must navigate and the _movements_ ↔️ they can make. 6 | 7 | The _warehouse_ is represented as an **array of text strings**, where: 8 | 9 | - `.` means there is a clear path. 10 | - `*` means there is an obstacle. 11 | - `!` is the robot's initial position. 12 | 13 | The _movements_ are an **array of text strings**, where: 14 | 15 | - `R` moves the robot one position to the right. 16 | - `L` moves the robot one position to the left. 17 | - `U` moves the robot one position upwards. 18 | - `D` moves the robot one position downwards. 19 | 20 | It must be taken into account that **the robot cannot overcome obstacles or the warehouse boundaries**. 21 | 22 | Given a warehouse and the movements, we need to return the array with the robot's final position. 23 | 24 | ```js 25 | const store = ['..!....', '...*.*.'] 26 | 27 | const movements = ['R', 'R', 'D', 'L'] 28 | const result = autonomousDrive(store, movements) 29 | console.log(result) 30 | /* 31 | [ 32 | ".......", 33 | "...*!*." 34 | ] 35 | */ 36 | 37 | // The last movement is to the left, but it cannot move because there is an obstacle. 38 | ``` 39 | 40 | Keep in mind that the `store` is **an array that can have a number of rows ranging from 1 to 100**, as we have warehouses of all sizes. 41 | 42 | Also note that the robot **might end up in its initial position** if it can't move or if it's going around in circles. 43 | -------------------------------------------------------------------------------- /2023/challenge-15/main.test.ts: -------------------------------------------------------------------------------- 1 | import { assertEquals } from 'assert' 2 | 3 | import { autonomousDrive } from './main.ts' 4 | 5 | Deno.test('Challenge #15: ↔️ Autonomous robot', () => { 6 | const movements: string[] = ['R', 'R', 'D', 'L'] 7 | const store: string[] = ['..!....', '...*.*.'] 8 | 9 | const result = autonomousDrive(store, movements) 10 | const expected: string[] = [".......", "...*!*."] 11 | 12 | assertEquals(result, expected) 13 | }) 14 | -------------------------------------------------------------------------------- /2023/challenge-15/main.ts: -------------------------------------------------------------------------------- 1 | function autonomousDrive(store: string[], movements: string[]) { 2 | let robotX = -1; 3 | let robotY = -1; 4 | 5 | let y = 0; 6 | for (const road of store) { 7 | const x = road.search('!'); 8 | 9 | const searchMoveOptions = [ 10 | [ 11 | [0, 0], [x + 1, ++y] 12 | ], 13 | [ 14 | [0, 0], [0, 0] 15 | ] 16 | ] 17 | 18 | const hasRobotBeenFound = robotX !== -1; 19 | const isRobotInRow = x !== -1; 20 | 21 | const [newX, newY] = searchMoveOptions[+hasRobotBeenFound] 22 | [+isRobotInRow]; 23 | 24 | robotX += newX; 25 | robotY += newY; 26 | } 27 | 28 | store[robotY] = store[robotY].replace('!', '.'); 29 | 30 | const moveOptions: Record = { 31 | 'R': [+1, +0], 32 | 'L': [-1, +0], 33 | 'U': [+0, -1], 34 | 'D': [+0, +1] 35 | } 36 | 37 | for (const movement of movements) { 38 | const [x, y] = moveOptions[movement]; 39 | const obstacle = store[robotY + y]?.[robotX + x]; 40 | 41 | const isObstacle = ['*', undefined].includes(obstacle); 42 | const robotMoveOptions = [ 43 | [x, y], 44 | [0, 0] 45 | ] 46 | 47 | const [newX, newY] = robotMoveOptions[+isObstacle]; 48 | 49 | robotX += newX; 50 | robotY += newY; 51 | } 52 | 53 | store[robotY] = store[robotY].substring(0, robotX) + '!' 54 | + store[robotY].substring(robotX + 1); 55 | 56 | return store; 57 | } 58 | 59 | export { autonomousDrive } 60 | -------------------------------------------------------------------------------- /2023/challenge-16/README.md: -------------------------------------------------------------------------------- 1 | # Challenge #16: ❌ Friday deployment 2 | 3 | Yesterday, someone did a _production deployment_ and the application for 4 | assembling Christmas trees broke. We've been asked to fix it as soon as 5 | possible. 6 | 7 | The problem is that the format of the trees has changed. **It's an array of 8 | numbers**… **but it should be an object!** For example, the tree: 9 | `[3, 1, 0, 8, 12, null, 1]` looks like this: 10 | 11 | ```js 12 | // 3 13 | // / \ 14 | // 1 0 15 | // / \ \ 16 | // 8 12 1 17 | ``` 18 | 19 | What we need is to transform the array into an object where each node of the 20 | tree has `value`, `left`, and `right` properties. 21 | 22 | For example, running your `transformTree` function with 23 | [`3, 1, 0, 8, 12, null, 1]` should return this: 24 | 25 | ```js 26 | { 27 | value: 3, 28 | left: { 29 | value: 1, 30 | left: { 31 | value: 8, 32 | left: null, 33 | right: null 34 | }, 35 | right: { 36 | value: 12, 37 | left: null, 38 | right: null 39 | } 40 | }, 41 | right: { 42 | value: 0, 43 | left: null, 44 | right: { 45 | value: 1, 46 | left: null, 47 | right: null 48 | } 49 | } 50 | } 51 | ``` 52 | 53 | The elf on duty who tried to solve the problem before going home, left us some 54 | clues: 55 | 56 | - If a node doesn't have a value, it's represented with `null`. Therefore, if a 57 | node has a `null` value, it won't have any children. 58 | - The root node is at index `0` in the array. 59 | - There's a relationship between the index of a node and the index of its 60 | children. Look for the pattern! 61 | -------------------------------------------------------------------------------- /2023/challenge-16/main.test.ts: -------------------------------------------------------------------------------- 1 | import { assertEquals } from 'assert' 2 | 3 | import { TreeNode, transformTree } from './main.ts' 4 | 5 | Deno.test('Challenge #16: ❌ Friday deployment', () => { 6 | const tree: number[] = [1] 7 | 8 | const result = transformTree(tree) 9 | const expected: null | TreeNode = { 10 | "value": 1, 11 | "left": null, 12 | "right": null 13 | } 14 | 15 | assertEquals(result, expected) 16 | }) 17 | 18 | Deno.test('Challenge #16: ❌ Friday deployment', () => { 19 | const tree: number[] = [1, 2, 3] 20 | 21 | const result = transformTree(tree) 22 | const expected: null | TreeNode = { 23 | "value": 1, 24 | "left": { 25 | "value": 2, 26 | "left": null, 27 | "right": null 28 | }, 29 | "right": { 30 | "value": 3, 31 | "left": null, 32 | "right": null 33 | } 34 | } 35 | 36 | assertEquals(result, expected) 37 | }) 38 | -------------------------------------------------------------------------------- /2023/challenge-17/README.md: -------------------------------------------------------------------------------- 1 | # Challenge #17: 🛷 Optimizing the rental 2 | 3 | In `Rovaniemi, Finland 🇫🇮`, sleds 🛷 are rented by time intervals. **Each interval is represented as an array of two elements**, where the first element is the start of the rental and the second one is the end. 4 | 5 | For example, the array `[2, 7]` represents a rental that begins at hour `2` and ends at hour `7`. The problem is that sometimes the intervals overlap with each other, making it confusing to figure out from what time to what time the sled was rented. 6 | 7 | We're asked to, in order to simplify the task of calculating the total rental time, **write a function that merges all overlapping intervals** and **returns an array of sorted intervals**: 8 | 9 | ```js 10 | optimizeIntervals([ 11 | [5, 8], 12 | [2, 7], 13 | [3, 4] 14 | ]) // [[2, 8]] 15 | 16 | optimizeIntervals([ 17 | [1, 3], 18 | [8, 10], 19 | [2, 6] 20 | ]) // [[1, 6], [8, 10]] 21 | 22 | optimizeIntervals([ 23 | [3, 4], 24 | [1, 2], 25 | [5, 6] 26 | ]) // [[1, 2], [3, 4], [5, 6]] 27 | ``` 28 | 29 | You can assume that **the first element of each interval is always less than or equal to the second element**. But **the intervals are not necessarily sorted**. 30 | 31 | The hour numbers can go up to the figure `9999`. 32 | -------------------------------------------------------------------------------- /2023/challenge-17/main.test.ts: -------------------------------------------------------------------------------- 1 | import { assertEquals } from 'assert' 2 | 3 | import { optimizeIntervals } from './main.ts' 4 | 5 | Deno.test('Challenge #17: 🛷 Optimizing the rental', () => { 6 | const intervals: number[][] = [ 7 | [2, 7], [3, 4], [5, 8] 8 | ] 9 | 10 | const result = optimizeIntervals(intervals) 11 | const expected: number[][] = [ 12 | [2, 8] 13 | ] 14 | 15 | assertEquals(result, expected) 16 | }) 17 | 18 | Deno.test('Challenge #17: 🛷 Optimizing the rental', () => { 19 | const intervals: number[][] = [ 20 | [3, 4], [5, 8], [2, 7] 21 | ] 22 | 23 | const result = optimizeIntervals(intervals) 24 | const expected: number[][] = [ 25 | [2, 8] 26 | ] 27 | 28 | assertEquals(result, expected) 29 | }) 30 | -------------------------------------------------------------------------------- /2023/challenge-17/main.ts: -------------------------------------------------------------------------------- 1 | function optimizeIntervals(intervals: number[][]) { 2 | intervals.sort((a, b) => a[0] - b[0]); 3 | 4 | const mergedIntervals = [intervals[0]]; 5 | 6 | for (const currentInterval of intervals) { 7 | const lastMergedInterval = mergedIntervals[mergedIntervals.length - 1]; 8 | 9 | if (currentInterval[0] <= lastMergedInterval[1]) { 10 | lastMergedInterval[1] = Math.max( 11 | lastMergedInterval[1], currentInterval[1] 12 | ); 13 | } else { 14 | mergedIntervals.push(currentInterval); 15 | } 16 | } 17 | 18 | return mergedIntervals; 19 | } 20 | 21 | export { optimizeIntervals } 22 | -------------------------------------------------------------------------------- /2023/challenge-19/README.md: -------------------------------------------------------------------------------- 1 | # Challenge #19: 💣 Face the sabotage 2 | 3 | Alert at Santa's toy factory! The **Grinch** 😈 has infiltrated the warehouse and sabotaged some of the toys 💣. 4 | 5 | **The elves need help to find the sabotaged toys** and remove them before Christmas comes. For this, we have the warehouse map 🗺️, which is a matrix. 6 | 7 | The `*` represent the _sabotaged toys_, and empty cells with a blank space ` ` are safe places. 8 | 9 | Your task is to write **a function that returns the same matrix but, at each position, shows us the number of sabotaged toys in the adjacent cells**. 10 | 11 | If a cell contains a sabotaged toy, it should remain the same. If a cell does not touch any sabotaged toy, it should contain a blank space ` `. 12 | 13 | ```js 14 | const store = [ 15 | ['*', ' ', ' ', ' '], 16 | [' ', ' ', '*', ' '], 17 | [' ', ' ', ' ', ' '], 18 | ['*', ' ', ' ', ' '] 19 | ] 20 | 21 | console.log(revealSabotage(board)) 22 | /* Should display: 23 | [ 24 | ['*', '2', '1', '1'], 25 | ['1', '2', '*', '1'], 26 | ['1', '2', '1', '1'], 27 | ['*', '1', ' ', ' '] 28 | ] 29 | */ 30 | ``` 31 | 32 | Keep in mind that… 33 | 34 | - Diagonal cells are also considered adjacent. 35 | - The board will always have at least one empty cell ` ` and a sabotaged toy `*`. 36 | - The board can be of any size. 37 | - The numbers are strings. 38 | -------------------------------------------------------------------------------- /2023/challenge-19/main.test.ts: -------------------------------------------------------------------------------- 1 | import { assertEquals } from 'assert' 2 | 3 | import { revealSabotage } from './main.ts' 4 | 5 | Deno.test('Challenge #19: 💣 Face the sabotage', () => { 6 | const store: string[][] = [ 7 | ['*', ' ', ' ', ' '], 8 | [' ', ' ', '*', ' '], 9 | [' ', ' ', ' ', ' '], 10 | ['*', ' ', ' ', ' '] 11 | ] 12 | 13 | const result = revealSabotage(store) 14 | const expected: string[][] = [ 15 | ['*', '2', '1', '1'], 16 | ['1', '2', '*', '1'], 17 | ['1', '2', '1', '1'], 18 | ['*', '1', ' ', ' '] 19 | ] 20 | 21 | assertEquals(result, expected) 22 | }) 23 | 24 | Deno.test('Challenge #19: 💣 Face the sabotage', () => { 25 | const store: string[][] = [ 26 | ['*', ' ', ' '], 27 | [' ', ' ', ' '], 28 | [' ', ' ', ' '] 29 | ] 30 | 31 | const result = revealSabotage(store) 32 | const expected: string[][] = [ 33 | ["*", "1", " "], 34 | ["1", "1", " "], 35 | [" ", " ", " "] 36 | ] 37 | 38 | assertEquals(result, expected) 39 | }) 40 | -------------------------------------------------------------------------------- /2023/challenge-19/main.ts: -------------------------------------------------------------------------------- 1 | function revealSabotage(store: string[][]) { 2 | let y = 0; 3 | for (const currRow of store) { 4 | let x = 0; 5 | for (const cell of currRow) { 6 | if (cell === '*') { 7 | x++; continue; 8 | } 9 | 10 | const prevRow = store[y - 1] 11 | const nextRow = store[y + 1] 12 | 13 | const adjacentCells = [ 14 | prevRow?.[x - 1], 15 | prevRow?.[x], 16 | prevRow?.[x + 1], 17 | currRow[x - 1], 18 | currRow[x + 1], 19 | nextRow?.[x - 1], 20 | nextRow?.[x], 21 | nextRow?.[x + 1], 22 | ]; 23 | 24 | const adjacentMines = adjacentCells.reduce( 25 | (acc, curr) => acc + Number(curr === '*'), 26 | 0 27 | ); 28 | 29 | if (adjacentMines > 0) { 30 | currRow[x] = '' + adjacentMines; 31 | } 32 | 33 | x++; 34 | } 35 | 36 | y++; 37 | } 38 | 39 | return store; 40 | } 41 | 42 | export { revealSabotage } 43 | -------------------------------------------------------------------------------- /2023/challenge-20/main.test.ts: -------------------------------------------------------------------------------- 1 | import { assertEquals } from 'assert' 2 | 3 | import { distributeGifts } from './main.ts' 4 | 5 | Deno.test('Challenge #20: 🏋️‍♂️ Distribute the weight', () => { 6 | const store: (number | null)[][] = [ 7 | [4, 5, 1], 8 | [6, null, 3], 9 | [8, null, 4] 10 | ] 11 | 12 | const result = distributeGifts(store) 13 | const expected: (number | null)[][] = [ 14 | [5, 3, 3], 15 | [6, 5, 3], 16 | [7, 6, 4] 17 | ] 18 | 19 | assertEquals(result, expected) 20 | }) 21 | -------------------------------------------------------------------------------- /2023/challenge-20/main.ts: -------------------------------------------------------------------------------- 1 | function distributeGifts(weights: (number | null)[][]): number[][] { 2 | const resultGrid: number[][] = []; 3 | 4 | let rowIndex = -1; 5 | for (const currentRow of weights) { 6 | resultGrid[++rowIndex] = []; 7 | 8 | let columnIndex = 0; 9 | for (const currentCol of currentRow) { 10 | const topRowValue = weights[rowIndex - 1]?.[columnIndex]; 11 | const bottomRowValue = weights[rowIndex + 1]?.[columnIndex]; 12 | 13 | const rightColValue = currentRow[columnIndex + 1]; 14 | const leftColValue = currentRow[columnIndex - 1]; 15 | 16 | const values = [ 17 | currentCol, leftColValue, rightColValue, 18 | topRowValue, bottomRowValue 19 | ]; 20 | 21 | const divisor = values.reduce( 22 | (accumulator, value) => accumulator! + Number(value != null), 23 | 0, 24 | ); 25 | 26 | const total = values.reduce( 27 | (accumulator, value) => accumulator! + (value ?? 0), 28 | 0 29 | ); 30 | 31 | resultGrid[rowIndex][columnIndex++] = Math.round(total! / divisor!); 32 | } 33 | } 34 | 35 | return resultGrid; 36 | } 37 | 38 | export { distributeGifts } 39 | -------------------------------------------------------------------------------- /2023/challenge-21/README.md: -------------------------------------------------------------------------------- 1 | # Challenge #21: 🪐 Binary message 2 | 3 | The elves are receiving strange binary messages from Mars 🪐. Are the aliens trying to communicate with them? 👽 4 | 5 | **The message that arrives is an array of 0s and 1s**. It seems they have found a pattern… To make sure, they want to **find the longest segment of the string where the number of 0s and 1s is equal**. 6 | 7 | ```js 8 | findBalancedSegment([1, 1, 0, 1, 1, 0, 1, 1]) 9 | // |________| 10 | // position of segment: [2, 5] 11 | // longest balanced 12 | // of 0s and 1s 13 | 14 | findBalancedSegment([1, 1, 0]) 15 | // |__| 16 | // [1, 2] 17 | 18 | findBalancedSegment([1, 1, 1]) 19 | // no balanced segments: [] 20 | ``` 21 | 22 | Keep in mind that if there is more than one balanced pattern, **you should return the longest and the first one you find from left to right**. 23 | 24 | They say that if they find the pattern, they will be able to send a message back to Mars 🚀. It seems that they have to send it to https://mars.codes. 25 | -------------------------------------------------------------------------------- /2023/challenge-21/main.test.ts: -------------------------------------------------------------------------------- 1 | import { assertEquals } from 'assert' 2 | 3 | import { findBalancedSegment } from './main.ts' 4 | 5 | Deno.test('Challenge #21: 🪐 Binary message', () => { 6 | const message: number[] = [1, 1, 0, 1, 1, 0, 1, 1] 7 | 8 | const result = findBalancedSegment(message) 9 | const expected: number[] = [2, 5] 10 | 11 | assertEquals(result, expected) 12 | }) 13 | 14 | Deno.test('Challenge #21: 🪐 Binary message', () => { 15 | const message: number[] = [1, 1, 0] 16 | 17 | const result = findBalancedSegment(message) 18 | const expected: number[] = [1, 2] 19 | 20 | assertEquals(result, expected) 21 | }) 22 | 23 | Deno.test('Challenge #21: 🪐 Binary message', () => { 24 | const message: number[] = [1, 1, 1] 25 | 26 | const result = findBalancedSegment(message) 27 | const expected: number[] = [] 28 | 29 | assertEquals(result, expected) 30 | }) 31 | -------------------------------------------------------------------------------- /2023/challenge-21/main.ts: -------------------------------------------------------------------------------- 1 | function findBalancedSegment(message: number[]) { 2 | let longestSegmentIndices = [0, 0]; 3 | let currentStartIndex = 0; 4 | 5 | for (const currentBit of message) { 6 | const currentSegmentIndices = [currentStartIndex, currentStartIndex]; 7 | 8 | let zerosCount = +(currentBit === 0); 9 | let onesCount = currentBit; 10 | 11 | let currentIndex = currentStartIndex + 1; 12 | for (const nextBit of message.slice(currentIndex)) { 13 | zerosCount += +(nextBit === 0); 14 | onesCount += nextBit; 15 | 16 | const segmentOptions = [currentSegmentIndices[1], currentIndex]; 17 | const segment = segmentOptions[ 18 | +(onesCount === zerosCount) 19 | ]; 20 | 21 | currentSegmentIndices[1] = segment; 22 | currentIndex++; 23 | } 24 | 25 | const segmentOptions = [currentSegmentIndices, longestSegmentIndices]; 26 | const segment = segmentOptions[ 27 | +( 28 | currentSegmentIndices[1] - currentSegmentIndices[0] <= 29 | longestSegmentIndices[1] - longestSegmentIndices[0] 30 | ) 31 | ]; 32 | 33 | longestSegmentIndices = segment; 34 | currentStartIndex++; 35 | } 36 | 37 | const outputOptions = [longestSegmentIndices, []]; 38 | const output = outputOptions[ 39 | +(longestSegmentIndices[0] === longestSegmentIndices[1]) 40 | ]; 41 | 42 | return output; 43 | } 44 | 45 | export { findBalancedSegment } 46 | -------------------------------------------------------------------------------- /2023/challenge-22/README.md: -------------------------------------------------------------------------------- 1 | # Challenge #22: 🚂 Programming language 2 | 3 | In Santa's toy factory, **the elves are developing a programming language called Santa.js** 👨‍💻👩‍💻 based on symbols to control their toy machines 🚂. 4 | 5 | They have created a simple instruction system and need your help to build **a compiler that interprets these symbols**. 6 | 7 | The compiler works with a counter that initially has a value of `0`. **The instructions will modify the value of this counter**. 8 | 9 | Elves' language instructions based on symbols: 10 | 11 | - `+`: Increments the counter value by `1`. 12 | - `*`: Multiplies the counter value by `2`. 13 | - `-`: Subtracts `1` from the counter value. 14 | - `%`: Mark a return point. Does not modify the counter. 15 | - `<`: Go back **once to the last instruction with the `%` symbol it has seen**. If there is no previous `%`, it does nothing. 16 | - `¿`: Starts a conditional block that executes if the counter is greater than `0`. 17 | - `?`: Ends a conditional block. 18 | 19 | Create a `compile` function that receives a string with the language instructions and returns the result of executing them. Here are some examples: 20 | 21 | ```js 22 | compile('++*-') // 3 23 | // (1 + 1) * 2 - 1 = 3 24 | 25 | compile('++%++<') // 6 26 | // 1 + 1 + 1 + 1 + 1 + 1 = 6 27 | 28 | compile('++<--') // 0 29 | // 1 + 1 - 1 - 1 = 0 30 | 31 | compile('++¿+?') // 3 32 | // 1 + 1 + 1 = 3 33 | 34 | compile('--¿+++?') // -2 35 | // - 1 - 1 = -2 36 | ``` 37 | -------------------------------------------------------------------------------- /2023/challenge-22/main.test.ts: -------------------------------------------------------------------------------- 1 | import { assertEquals } from 'assert' 2 | 3 | import { compile } from './main.ts' 4 | 5 | Deno.test('Challenge #22: 🚂 Programming language', () => { 6 | const code = '++*-' 7 | 8 | const result = compile(code) 9 | const expected = 3 10 | 11 | assertEquals(result, expected) 12 | }) 13 | 14 | Deno.test('Challenge #22: 🚂 Programming language', () => { 15 | const code = '++%++<' 16 | 17 | const result = compile(code) 18 | const expected = 6 19 | 20 | assertEquals(result, expected) 21 | }) 22 | 23 | Deno.test('Challenge #22: 🚂 Programming language', () => { 24 | const code = '++<--' 25 | 26 | const result = compile(code) 27 | const expected = 0 28 | 29 | assertEquals(result, expected) 30 | }) 31 | 32 | Deno.test('Challenge #22: 🚂 Programming language', () => { 33 | const code = '++¿+?' 34 | 35 | const result = compile(code) 36 | const expected = 3 37 | 38 | assertEquals(result, expected) 39 | }) 40 | 41 | Deno.test('Challenge #22: 🚂 Programming language', () => { 42 | const code = '--¿+++?' 43 | 44 | const result = compile(code) 45 | const expected = -2 46 | 47 | assertEquals(result, expected) 48 | }) 49 | -------------------------------------------------------------------------------- /2023/challenge-23/main.test.ts: -------------------------------------------------------------------------------- 1 | import { assertEquals } from 'assert' 2 | 3 | import { organizeChristmasDinner } from './main.ts' 4 | 5 | Deno.test('Challenge #23: 🍽️ Christmas dinner', () => { 6 | const dishes: string[][] = [ 7 | ["christmas turkey", "turkey", "sauce", "herbs"], 8 | ["cake", "flour", "sugar", "egg"], 9 | ["hot chocolate", "chocolate", "milk", "sugar"], 10 | ["pizza", "sauce", "tomato", "cheese", "ham"], 11 | ] 12 | 13 | const result = organizeChristmasDinner(dishes) 14 | const expected: string[][] = [ 15 | ["sauce", "christmas turkey", "pizza"], 16 | ["sugar", "cake", "hot chocolate"] 17 | ] 18 | 19 | assertEquals(result, expected) 20 | }) 21 | -------------------------------------------------------------------------------- /2023/challenge-23/main.ts: -------------------------------------------------------------------------------- 1 | function organizeChristmasDinner(dishes: string[][]) { 2 | const ingredients = new Map(); 3 | 4 | for (const [dishName, ...dishIngredients] of dishes) { 5 | for (const ingredient of dishIngredients) { 6 | ingredients.set(ingredient, 7 | (ingredients.get(ingredient) || []).concat(dishName) 8 | ); 9 | } 10 | } 11 | 12 | const organizedDishes: string[][] = []; 13 | for (const [ingredient, dishes] of ingredients.entries()) { 14 | if (dishes.length >= 2) { 15 | organizedDishes.push([ingredient, ...dishes.sort()]); 16 | } 17 | } 18 | 19 | return organizedDishes.sort(); 20 | } 21 | 22 | export { organizeChristmasDinner } 23 | -------------------------------------------------------------------------------- /2023/challenge-24/README.md: -------------------------------------------------------------------------------- 1 | # Challenge #24: 🪜 Jump on the stairs 2 | 3 | In the village of Santa, there is a magic staircase that leads to the toy factory 🧸. The elves, always looking to get exercise and have fun 🏃‍♂️, decide to jump the steps of the staircase. 4 | 5 | They give us `steps` as the number of steps on the staircase and the maximum number of steps `maxJump` that an elf can jump in a single jump. 6 | 7 | Your task is to help the elves find **all the possible sequences of jumps they can make to go up the staircase, sorted from least to most**. Considering that the elves can jump at most maxJump steps in a single jump (but they can jump fewer steps if they wish). 8 | 9 | For example, if there is a staircase of `steps = 4` and `maxJump = 2` is the maximum number of steps that an elf can jump in a single jump, then there are five possible jumping sequences: 10 | 11 | - `[1, 1, 1, 1]` (jumps 1 step 4 times) 12 | - `[1, 1, 2]` (jumps 1 step 2 times and then 2 steps) 13 | - `[1, 2, 1]` (jumps 1 step, then 2 steps and then 1 step) 14 | - `[2, 1, 1]` (jumps 2 steps, then 1 step and then 1 step) 15 | - `[2, 2]` (jumps 2 steps 2 times) 16 | 17 | More examples: 18 | 19 | ```js 20 | getStaircasePaths(2, 1) // [[1, 1]] 21 | getStaircasePaths(3, 3) // [[1, 1, 1], [1, 2], [2, 1], [3]] 22 | getStaircasePaths(5, 1) // [[1, 1, 1, 1, 1]] 23 | getStaircasePaths(5, 2) 24 | /* 25 | [ 26 | [1, 1, 1, 1, 1], 27 | [1, 1, 1, 2], 28 | [1, 1, 2, 1], 29 | [1, 2, 1, 1], 30 | [1, 2, 2], 31 | [2, 1, 1, 1], 32 | [2, 1, 2], 33 | [2, 2, 1], 34 | ] 35 | */ 36 | ``` 37 | -------------------------------------------------------------------------------- /2023/challenge-24/main.test.ts: -------------------------------------------------------------------------------- 1 | import { assertEquals } from 'assert' 2 | 3 | import { getStaircasePaths } from './main.ts' 4 | 5 | Deno.test('Challenge #24: 🪜 Jump on the stairs', () => { 6 | const steps = 2 7 | const maxJump = 1 8 | 9 | const result = getStaircasePaths(steps, maxJump) 10 | const expected: number[][] = [ 11 | [1, 1] 12 | ] 13 | 14 | assertEquals(result, expected) 15 | }) 16 | 17 | Deno.test('Challenge #24: 🪜 Jump on the stairs', () => { 18 | const steps = 3 19 | const maxJump = 3 20 | 21 | const result = getStaircasePaths(steps, maxJump) 22 | const expected: number[][] = [ 23 | [1, 1, 1], 24 | [1, 2], 25 | [2, 1], 26 | [3] 27 | ] 28 | 29 | assertEquals(result, expected) 30 | }) 31 | 32 | Deno.test('Challenge #24: 🪜 Jump on the stairs', () => { 33 | const steps = 5 34 | const maxJump = 1 35 | 36 | const result = getStaircasePaths(steps, maxJump) 37 | const expected: number[][] = [ 38 | [1, 1, 1, 1, 1] 39 | ] 40 | 41 | assertEquals(result, expected) 42 | }) 43 | 44 | Deno.test('Challenge #24: 🪜 Jump on the stairs', () => { 45 | const steps = 5 46 | const maxJump = 2 47 | 48 | const result = getStaircasePaths(steps, maxJump) 49 | const expected: number[][] = [ 50 | [1, 1, 1, 1, 1], 51 | [1, 1, 1, 2], 52 | [1, 1, 2, 1], 53 | [1, 2, 1, 1], 54 | [1, 2, 2], 55 | [2, 1, 1, 1], 56 | [2, 1, 2], 57 | [2, 2, 1] 58 | ] 59 | 60 | assertEquals(result, expected) 61 | }) 62 | -------------------------------------------------------------------------------- /2023/challenge-24/main.ts: -------------------------------------------------------------------------------- 1 | // The code at 'github.com/mariaelisaaraya/adventJS2023' has been instrumental in simplifying the complexity to 1 and eliminating recursion. 2 | 3 | // Complejidad cognitiva: 3, lower score, and more readable. 4 | function _getStaircasePaths(steps: number, maxJump: number): number[][] { 5 | const paths: number[][][] = []; 6 | paths[0] = [[]]; 7 | 8 | for (let currentStep = 1; currentStep <= steps; currentStep++) { 9 | paths[currentStep] = []; 10 | 11 | for (let jump = 1; jump <= Math.min(currentStep, maxJump); jump++) { 12 | for (const path of paths[currentStep - jump]) { 13 | paths[currentStep].push([jump, ...path]); 14 | } 15 | } 16 | } 17 | 18 | return paths[steps]; 19 | } 20 | 21 | // Cognitive complexity: 1, better score, and less readable. 22 | function getStaircasePaths(steps: number, maxJump: number): number[][] { 23 | const paths: number[][][] = []; 24 | paths[0] = [[]]; 25 | 26 | let currentSteps = 1; 27 | const stepsArray = Array.from({ 28 | length: steps 29 | }); 30 | 31 | for (const _ of stepsArray) { 32 | paths[currentSteps] = []; 33 | 34 | let i = 1; 35 | const jumpArray = Array.from({ 36 | length: Math.min(currentSteps, maxJump) 37 | }); 38 | 39 | for (const _ of jumpArray) { 40 | for (const path of paths[currentSteps - i]) { 41 | paths[currentSteps].push([i, ...path]); 42 | } 43 | 44 | i++; 45 | } 46 | 47 | currentSteps++; 48 | } 49 | 50 | return paths[steps]; 51 | } 52 | 53 | export { getStaircasePaths } 54 | -------------------------------------------------------------------------------- /2023/challenge-25/README.md: -------------------------------------------------------------------------------- 1 | # Challenge #25: 🗺️ Calculating distances 2 | 3 | **Santa Claus 🎅 has already delivered all the gifts** to the children but they want to see if they can improve for next year. 4 | 5 | The elves want to **know how many moves Santa Claus** 🛷 made to deliver all the gifts. For this, they give you a map of the city with the location of each child and Santa. 6 | 7 | The map is a _multiline text string_ where each character represents a square. The children are represented by numbers from `1` to `9` and _Santa Claus_ by the letter `S`. The rest of the squares are `.` 8 | 9 | _Santa Claus_ can only move up, down, left, or right, and each move counts as 1 km. In addition, he always starts at the `S` position and must deliver the gifts in order, from `1` to `9`. 10 | 11 | ```js 12 | const map = `.....1.... 13 | ..S....... 14 | .......... 15 | ....3..... 16 | ......2...` 17 | 18 | const result = travelDistance(map) 19 | console.log(result) // -> 12 km 20 | /* 21 | From S to kid 1: 4 moves 22 | From kid 1 to 2: 5 moves 23 | From kid 2 to 3: 3 moves 24 | Total: 12 moves 25 | */ 26 | 27 | const result2 = travelDistance(`..S.1...`) 28 | console.log(result2) // -> 2 29 | ``` 30 | 31 | Write a `travelDistance` function that receives a map and returns the total distance Santa Claus has traveled according to the position of the children. 32 | 33 | Take into account that: 34 | 35 | - The map doesn't have to be square. 36 | - The map will always have at least one child. 37 | - The map will always have an initial position for Santa Claus. 38 | - The numbers of the kids never repeat. 39 | -------------------------------------------------------------------------------- /2023/challenge-25/main.test.ts: -------------------------------------------------------------------------------- 1 | import { assertEquals } from 'assert' 2 | 3 | import { travelDistance } from './main.ts' 4 | 5 | Deno.test('Challenge #25: 🗺️ Calculating distances', () => { 6 | const map = `.....1....\n..S.......\n..........\n....3.....\n......2...`; 7 | 8 | const result = travelDistance(map) 9 | const expected = 12; 10 | 11 | assertEquals(result, expected) 12 | }) 13 | 14 | Deno.test('Challenge #25: 🗺️ Calculating distances', () => { 15 | const map = `..S.1...`; 16 | 17 | const result = travelDistance(map) 18 | const expected = 2; 19 | 20 | assertEquals(result, expected) 21 | }) 22 | -------------------------------------------------------------------------------- /2023/challenge-25/main.ts: -------------------------------------------------------------------------------- 1 | // Ingenious solution found at 'github.com/iswilljr/adventjs'. 2 | 3 | function travelDistance(map: string) { 4 | const rows = map.split('\n'); 5 | 6 | const flattenedMap = rows.join(''); 7 | const numCols = rows[0].length; 8 | 9 | const giftPositions = flattenedMap.replace(/\.|S/g, ''); 10 | const santaPosition = flattenedMap.indexOf('S'); 11 | 12 | let santaRow = Math.floor(santaPosition / numCols); 13 | let santaCol = santaPosition % numCols; 14 | 15 | let totalMovements = 0; 16 | let currentGift = 1; 17 | for (const _ of giftPositions) { 18 | const giftPosition = flattenedMap.indexOf(`${currentGift++}`); 19 | const giftCol = giftPosition % numCols; 20 | const giftRow = Math.floor(giftPosition / numCols); 21 | 22 | totalMovements 23 | += Math.abs(santaRow - giftRow) 24 | + Math.abs(santaCol - giftCol); 25 | 26 | santaCol = giftCol; 27 | santaRow = giftRow; 28 | } 29 | 30 | return totalMovements; 31 | } 32 | 33 | export { travelDistance } 34 | -------------------------------------------------------------------------------- /2023/deno.json: -------------------------------------------------------------------------------- 1 | { 2 | "importMap": "importMap.json" 3 | } -------------------------------------------------------------------------------- /2023/importMap.json: -------------------------------------------------------------------------------- 1 | { 2 | "imports": { 3 | "std/": "https://deno.land/std@0.170.0/", 4 | "assert": "https://deno.land/std@0.208.0/assert/mod.ts" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /2024/challenge-01/README.md: -------------------------------------------------------------------------------- 1 | # Challenge #1: 🎁 First gift repeated! 2 | 3 | **Santa Claus** 🎅 has received a list of magical numbers representing gifts 🎁, but some of them are duplicated and must be removed to avoid confusion. Additionally, **the gifts must be sorted in ascending order before being delivered to the elves**. 4 | 5 | Your task is to write a function that receives a list of integers (which may include duplicates) and returns a new list without duplicates, sorted in ascending order. 6 | 7 | ```js 8 | const gifts1 = [3, 1, 2, 3, 4, 2, 5] 9 | const preparedGifts1 = prepareGifts(gifts1) 10 | console.log(preparedGifts1) // [1, 2, 3, 4, 5] 11 | 12 | const gifts2 = [6, 5, 5, 5, 5] 13 | const preparedGifts2 = prepareGifts(gifts2) 14 | console.log(preparedGifts2) // [5, 6] 15 | 16 | const gifts3 = [] 17 | const preparedGifts3 = prepareGifts(gifts3) 18 | console.log(preparedGifts3) // [] 19 | // There are no gifts, the list remains empty 20 | ``` 21 | -------------------------------------------------------------------------------- /2024/challenge-01/main.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | 3 | def prepare_gifts(gifts: List[int]) -> List[int]: 4 | return sorted(set(gifts)) 5 | -------------------------------------------------------------------------------- /2024/challenge-01/mod.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {number[]} gifts 3 | * @returns {number[]} 4 | */ 5 | function prepareGifts(gifts) { 6 | return Array.from(new Set(gifts.sort((a, b) => a - b))); 7 | } 8 | 9 | export { 10 | prepareGifts 11 | } 12 | -------------------------------------------------------------------------------- /2024/challenge-01/mod.ts: -------------------------------------------------------------------------------- 1 | function prepareGifts(gifts: number[]): number[] { 2 | return Array.from(new Set(gifts.sort((a, b) => a - b))); 3 | } 4 | -------------------------------------------------------------------------------- /2024/challenge-02/README.md: -------------------------------------------------------------------------------- 1 | # Challenge #2: 🖼️ Framing names 2 | 3 | **Santa Claus** 🎅 wants to frame the names of the good children to decorate his workshop 🖼️, but the frame must follow specific rules. Your task is to help the elves generate this magical frame. 4 | 5 | Rules: 6 | 7 | - Given an array of names, you must create a rectangular frame that contains all of them. 8 | - Each name must be on a line, aligned to the left. 9 | - The frame is built with * and has a border one line thick. 10 | - The width of the frame automatically adapts to the longest name plus a margin of 1 space on each side. 11 | 12 | Example of how it works: 13 | 14 | ```js 15 | createFrame(['midu', 'madeval', 'educalvolpz']) 16 | 17 | // Expected result: 18 | *************** 19 | * midu * 20 | * madeval * 21 | * educalvolpz * 22 | *************** 23 | 24 | createFrame(['midu']) 25 | 26 | // Expected result: 27 | ******** 28 | * midu * 29 | ******** 30 | 31 | createFrame(['a', 'bb', 'ccc']) 32 | 33 | // Expected result: 34 | ******* 35 | * a * 36 | * bb * 37 | * ccc * 38 | ******* 39 | 40 | createFrame(['a', 'bb', 'ccc', 'dddd']) 41 | ``` 42 | -------------------------------------------------------------------------------- /2024/challenge-02/mod.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {string[]} names 3 | * @returns {string} 4 | */ 5 | function createFrame(names) { 6 | const maxLength = Math.max(...names.map(name => name.length)); 7 | 8 | const border = '*'.repeat(maxLength + 4); 9 | 10 | const framedNames = names.map(name => { 11 | const paddedName = name.padEnd(maxLength, ' '); 12 | 13 | return `* ${paddedName} *`; 14 | }); 15 | 16 | return [border, ...framedNames, border].join('\n'); 17 | } 18 | -------------------------------------------------------------------------------- /2024/challenge-02/mod.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | 3 | def create_frame(names: List[str]) -> str: 4 | max_length = max(len(name) for name in names) 5 | border = '*' * (max_length + 4) 6 | 7 | framed_names = [ 8 | f"* {name.ljust(max_length)} *" 9 | for name in names 10 | ] 11 | 12 | return border + "\n".join(framed_names) + "\n" + border 13 | -------------------------------------------------------------------------------- /2024/challenge-02/mod.ts: -------------------------------------------------------------------------------- 1 | function createFrame(names: string[]): string { 2 | const maxLength = Math.max(...names.map(name => name.length)); 3 | 4 | const border = '*'.repeat(maxLength + 4); 5 | 6 | const framedNames = names.map(name => { 7 | const paddedName = name.padEnd(maxLength, ' '); 8 | 9 | return `* ${paddedName} *`; 10 | }); 11 | 12 | return [border, ...framedNames, border].join('\n'); 13 | } 14 | -------------------------------------------------------------------------------- /2024/challenge-03/mod.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @typedef {Object} InventoryItem 3 | * @property {string} name - The name of the inventory item. 4 | * @property {number} quantity - The quantity of the inventory item. 5 | * @property {string} category - The category of the inventory item. 6 | */ 7 | 8 | /** 9 | * @typedef {Object.>} OrganizedInventory 10 | */ 11 | 12 | /** 13 | * @param {InventoryItem[]} inventory 14 | * @returns {OrganizedInventory} 15 | */ 16 | function organizeInventory(inventory) { 17 | return inventory.reduce((acc, item) => { 18 | const { category, name, quantity } = item; 19 | 20 | acc[category] ||= {}; 21 | acc[category][name] = (acc[category][name] ?? 0) + quantity; 22 | 23 | return acc; 24 | }, {}); 25 | } 26 | -------------------------------------------------------------------------------- /2024/challenge-03/mod.py: -------------------------------------------------------------------------------- 1 | from typing import List, Dict 2 | 3 | def organize_inventory(inventory: List[Dict[str, str]]) -> Dict[str, Dict[str, int]]: 4 | organized = {} 5 | 6 | for item in inventory: 7 | category = item["category"] 8 | name = item["name"] 9 | quantity = item["quantity"] 10 | 11 | if category not in organized: 12 | organized[category] = {} 13 | if name not in organized[category]: 14 | organized[category][name] = 0 15 | 16 | organized[category][name] += quantity 17 | 18 | return organized 19 | -------------------------------------------------------------------------------- /2024/challenge-03/mod.ts: -------------------------------------------------------------------------------- 1 | type OrganizedInventory = Record>; 2 | 3 | type InventoryItem = { 4 | name: string; 5 | quantity: number; 6 | category: string; 7 | }; 8 | 9 | function organizeInventory(inventory: InventoryItem[]): OrganizedInventory { 10 | return inventory.reduce((acc, item) => { 11 | const { category, name, quantity } = item; 12 | 13 | acc[category] ||= {}; 14 | acc[category][name] = (acc[category][name] ?? 0) + quantity; 15 | 16 | return acc; 17 | }, {}); 18 | } 19 | -------------------------------------------------------------------------------- /2024/challenge-04/README.md: -------------------------------------------------------------------------------- 1 | # Challenge #4: 🎄 Decorating the Christmas tree 2 | 3 | **It's time to put up the Christmas tree at home!** 🎄 But this year we want it to be special. We're going to create a function that receives the height of the tree (a positive integer between 1 and 100) and a special character to decorate it. 4 | 5 | The function should return a string that represents the Christmas tree, constructed as follows: 6 | 7 | - The tree is made up of triangles of special characters. 8 | - The spaces on the sides of the tree are represented with underscores _. 9 | - All trees have a trunk of two lines, represented by the # character. 10 | - The tree should always have the same length on each side. 11 | - You must ensure the tree has the correct shape using line breaks \n for each line. 12 | 13 | Examples: 14 | 15 | ```js 16 | const tree = createXmasTree(5, '*') 17 | console.log(tree) 18 | /* 19 | ____*____ 20 | ___***___ 21 | __*****__ 22 | _*******_ 23 | ********* 24 | ____#____ 25 | ____#____ 26 | */ 27 | 28 | const tree2 = createXmasTree(3, '+') 29 | console.log(tree2) 30 | /* 31 | __+__ 32 | _+++_ 33 | +++++ 34 | __#__ 35 | __#__ 36 | */ 37 | 38 | const tree3 = createXmasTree(6, '@') 39 | console.log(tree3) 40 | /* 41 | _____@_____ 42 | ____@@@____ 43 | ___@@@@@___ 44 | __@@@@@@@__ 45 | _@@@@@@@@@_ 46 | @@@@@@@@@@@ 47 | _____#_____ 48 | _____#_____ 49 | */ 50 | ``` 51 | 52 | Make sure to use line breaks \n at the end of each line, **except for the last one**. 53 | -------------------------------------------------------------------------------- /2024/challenge-04/mod.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {number} height 3 | * @param {string} ornament 4 | * @returns {string} 5 | */ 6 | function createXmasTree(height, ornament) { 7 | /** @type {string[]} */ 8 | const treeLines = []; 9 | 10 | for (let i = 0; i < height; i++) { 11 | const spaces = '_'.repeat(height - i - 1); 12 | const ornaments = ornament.repeat(2 * i + 1); 13 | 14 | treeLines.push(`${spaces}${ornaments}${spaces}`); 15 | } 16 | 17 | const trunkSpaces = '_'.repeat(height - 1); 18 | const trunk = `${trunkSpaces}#${trunkSpaces}`; 19 | 20 | treeLines.push(trunk); 21 | treeLines.push(trunk); 22 | 23 | return treeLines.join('\n'); 24 | } 25 | -------------------------------------------------------------------------------- /2024/challenge-04/mod.py: -------------------------------------------------------------------------------- 1 | def create_xmas_tree(height: int, ornament: str) -> str: 2 | tree_lines = [] 3 | 4 | for i in range(height): 5 | spaces = '_' * (height - i - 1) 6 | ornaments = ornament * (2 * i + 1) 7 | tree_lines.append(f"{spaces}{ornaments}{spaces}") 8 | 9 | trunk_spaces = '_' * (height - 1) 10 | trunk = f"{trunk_spaces}#{trunk_spaces}" 11 | 12 | tree_lines.append(trunk) 13 | tree_lines.append(trunk) 14 | 15 | return "\n".join(tree_lines) 16 | -------------------------------------------------------------------------------- /2024/challenge-04/mod.ts: -------------------------------------------------------------------------------- 1 | function createXmasTree(height: number, ornament: string): string { 2 | const treeLines: string[] = []; 3 | 4 | for (let i = 0; i < height; i++) { 5 | const spaces = '_'.repeat(height - i - 1); 6 | const ornaments = ornament.repeat(2 * i + 1); 7 | 8 | treeLines.push(`${spaces}${ornaments}${spaces}`); 9 | } 10 | 11 | const trunkSpaces = '_'.repeat(height - 1); 12 | const trunk = `${trunkSpaces}#${trunkSpaces}`; 13 | 14 | treeLines.push(trunk); 15 | treeLines.push(trunk); 16 | 17 | return treeLines.join('\n'); 18 | } 19 | -------------------------------------------------------------------------------- /2024/challenge-05/README.md: -------------------------------------------------------------------------------- 1 | # Challenge #5: 👞 Shoe pairing 2 | 3 | **Santa Claus's elves** 🧝🧝‍♂️ have found a bunch of mismatched magic boots in the workshop. Each boot is described by two values: 4 | 5 | - `type` indicates if it's a left boot (I) or a right boot (R). 6 | - `size` indicates the size of the boot. 7 | 8 | Your task is to help the elves pair all the boots of the same size having a left and a right one. To do this, you should return a list of the available boots after pairing them. 9 | 10 | **Note**: You can have more than one pair of boots of the same size! 11 | 12 | ```js 13 | const shoes = [ 14 | { type: 'I', size: 38 }, 15 | { type: 'R', size: 38 }, 16 | { type: 'R', size: 42 }, 17 | { type: 'I', size: 41 }, 18 | { type: 'I', size: 42 } 19 | ] 20 | 21 | organizeShoes(shoes) 22 | // [38, 42] 23 | 24 | const shoes2 = [ 25 | { type: 'I', size: 38 }, 26 | { type: 'R', size: 38 }, 27 | { type: 'I', size: 38 }, 28 | { type: 'I', size: 38 }, 29 | { type: 'R', size: 38 } 30 | ] 31 | // [38, 38] 32 | 33 | const shoes3 = [ 34 | { type: 'I', size: 38 }, 35 | { type: 'R', size: 36 }, 36 | { type: 'R', size: 42 }, 37 | { type: 'I', size: 41 }, 38 | { type: 'I', size: 43 } 39 | ] 40 | 41 | organizeShoes(shoes3) 42 | // [] 43 | ``` 44 | -------------------------------------------------------------------------------- /2024/challenge-05/mod.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @typedef {Object} Shoe 3 | * @property {'I' | 'R'} type 4 | * @property {number} size 5 | */ 6 | 7 | /** 8 | * @param {Shoe[]} shoes 9 | * @returns {number[]} 10 | */ 11 | function organizeShoes(shoes) { 12 | /** @type {Map} */ 13 | const inventory = new Map(); 14 | /** @type {number[]} */ 15 | const sizes = []; 16 | 17 | for (const shoe of shoes) { 18 | const { type, size } = shoe; 19 | 20 | if (!inventory.has(size)) { 21 | inventory.set(size, { I: 0, R: 0 }); 22 | } 23 | 24 | inventory.get(size)[type] += 1; 25 | } 26 | 27 | for (const [size, counts] of inventory) { 28 | const numberOfPairs = Math.min(counts.I, counts.R); 29 | 30 | for (let i = 0; i < numberOfPairs; i++) { 31 | sizes.push(size); 32 | } 33 | } 34 | 35 | return sizes; 36 | } 37 | -------------------------------------------------------------------------------- /2024/challenge-05/mod.py: -------------------------------------------------------------------------------- 1 | from typing import List, Dict 2 | 3 | def organize_shoes(shoes: List[Dict[str, int]]) -> List[int]: 4 | inventory = {} 5 | sizes = [] 6 | 7 | for shoe in shoes: 8 | shoe_type = shoe["type"] 9 | size = shoe["size"] 10 | 11 | if size not in inventory: 12 | inventory[size] = {"I": 0, "R": 0} 13 | 14 | inventory[size][shoe_type] += 1 15 | 16 | for size, counts in inventory.items(): 17 | number_of_pairs = min(counts["I"], counts["R"]) 18 | 19 | sizes.extend([size] * number_of_pairs) 20 | 21 | return sizes 22 | -------------------------------------------------------------------------------- /2024/challenge-05/mod.ts: -------------------------------------------------------------------------------- 1 | type Shoe = { 2 | type: 'I' | 'R'; 3 | size: number; 4 | }; 5 | 6 | function organizeShoes(shoes: Shoe[]): number[] { 7 | const inventory: Map = new Map(); 8 | const sizes: number[] = []; 9 | 10 | for (const shoe of shoes) { 11 | const { type, size } = shoe; 12 | 13 | if (!inventory.has(size)) { 14 | inventory.set(size, { I: 0, R: 0 }); 15 | } 16 | 17 | inventory.get(size)![type] += 1; 18 | } 19 | 20 | for (const [size, counts] of inventory) { 21 | const numberOfPairs = Math.min(counts.I, counts.R); 22 | 23 | for (let i = 0; i < numberOfPairs; i++) { 24 | sizes.push(size); 25 | } 26 | } 27 | 28 | return sizes; 29 | } 30 | -------------------------------------------------------------------------------- /2024/challenge-06/README.md: -------------------------------------------------------------------------------- 1 | # Challenge #6: 📦 Is the gift inside the box? 2 | 3 | We have already wrapped hundreds of presents 🎁… but an elf forgot to check if the present, represented by an asterisk `\*`, is inside the box. 4 | 5 | The box has a present (`\*`) and counts as "*inside the box*" if: 6 | 7 | - It is completely surrounded by `#` on the box's edges. 8 | - The `\*` is not on the box's edges. 9 | 10 | Keep in mind that the `\*` can be inside, outside, or may not even be there. We must return `true` if the `\*` is inside the box and `false` otherwise. 11 | 12 | Examples: 13 | 14 | ```js 15 | inBox([ 16 | "###", 17 | "#*#", 18 | "###" 19 | ]) // ➞ true 20 | 21 | inBox([ 22 | "####", 23 | "#* #", 24 | "# #", 25 | "####" 26 | ]) // ➞ true 27 | 28 | inBox([ 29 | "#####", 30 | "# #", 31 | "# #*", 32 | "#####" 33 | ]) // ➞ false 34 | 35 | inBox([ 36 | "#####", 37 | "# #", 38 | "# #", 39 | "# #", 40 | "#####" 41 | ]) // ➞ false 42 | ``` 43 | -------------------------------------------------------------------------------- /2024/challenge-06/mod.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {string[]} box 3 | * @returns {boolean} 4 | */ 5 | function inBox(box) { 6 | return box.slice(1, -1).some(row => /^#.*\*.*#$/.test(row)); 7 | } 8 | -------------------------------------------------------------------------------- /2024/challenge-06/mod.py: -------------------------------------------------------------------------------- 1 | import re 2 | from typing import List 3 | 4 | def in_box(box: List[str]) -> bool: 5 | return any(re.match(r'^#.*\*.*#$', row) for row in box[1:-1]) 6 | -------------------------------------------------------------------------------- /2024/challenge-06/mod.ts: -------------------------------------------------------------------------------- 1 | function inBox(box: string[]): boolean { 2 | return box.slice(1, -1).some(row => /^#.*\*.*#$/.test(row)); 3 | } 4 | -------------------------------------------------------------------------------- /2024/challenge-07/README.md: -------------------------------------------------------------------------------- 1 | # Challenge #7: 👹 The Grinch's attack 2 | 3 | The **grinch** 👹 has passed through Santa Claus's workshop! And what a mess he has made. He has changed the order of some packages, so shipments cannot be made. 4 | 5 | Luckily, the elf Pheralb has detected the pattern the grinch followed to jumble them. **He has written the rules that we must follow to reorder the packages. The instructions are as follows:** 6 | 7 | You will receive a string containing letters and parentheses. 8 | Every time you find a pair of parentheses, you need to reverse the content within them. 9 | If there are nested parentheses, solve the innermost ones first. 10 | Return the resulting string with parentheses removed, but with the content correctly reversed. 11 | He left us some examples: 12 | 13 | ```js 14 | fixPackages('a(cb)de') 15 | // ➞ "abcde" 16 | // We reverse "cb" inside the parentheses 17 | 18 | fixPackages('a(bc(def)g)h') 19 | // ➞ "agdefcbh" 20 | // 1st we reverse "def" → "fed", then we reverse "bcfedg" → "gdefcb" 21 | 22 | fixPackages('abc(def(gh)i)jk') 23 | // ➞ "abcighfedjk" 24 | // 1st we reverse "gh" → "hg", then "defhgi" → "ighfed" 25 | 26 | fixPackages('a(b(c))e') 27 | // ➞ "acbe" 28 | // 1st we reverse "c" → "c", then "bc" → "cb" 29 | ``` 30 | -------------------------------------------------------------------------------- /2024/challenge-07/mod.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {string} packages 3 | * @returns {string} 4 | */ 5 | function fixPackages(packages) { 6 | const pattern = /\([^()]*\)/; 7 | 8 | do { 9 | packages = packages.replace(pattern, 10 | (match) => [...match.slice(1, -1)].reverse().join('') 11 | ); 12 | } while (pattern.test(packages)) 13 | 14 | return packages; 15 | } 16 | -------------------------------------------------------------------------------- /2024/challenge-07/mod.py: -------------------------------------------------------------------------------- 1 | import re 2 | from typing import List 3 | 4 | def fix_packages(packages: str) -> str: 5 | while '(' in packages: 6 | packages = re.sub( 7 | r'\(([^()]+)\)', 8 | lambda match: ''.join(reversed(match.group(1))), 9 | packages 10 | ) 11 | 12 | return packages 13 | -------------------------------------------------------------------------------- /2024/challenge-07/mod.ts: -------------------------------------------------------------------------------- 1 | function fixPackages(packages: string): string { 2 | const pattern = /\([^()]*\)/; 3 | 4 | do { 5 | packages = packages.replace(pattern, 6 | (match) => [...match.slice(1, -1)].reverse().join('') 7 | ); 8 | } while (pattern.test(packages)) 9 | 10 | return packages; 11 | } 12 | -------------------------------------------------------------------------------- /2024/challenge-08/README.md: -------------------------------------------------------------------------------- 1 | # Challenge #8: 🦌 The reno race 2 | 3 | **It's time to select the fastest reindeer for Santa's journeys!** 🦌🎄 4 | Santa Claus has organized exciting reindeer races to determine which ones are in the best shape. 5 | 6 | Your task is to display each reindeer's progress on a snow track **in isometric format**. 7 | 8 | The information you receive: 9 | 10 | - `indices`: An array of integers representing each reindeer's progress on the track: 11 | - `0`: The lane is empty. 12 | - **Positive number**: The reindeer's current position from the beginning of the track. 13 | - **Negative number**: The reindeer's current position from the end of the track. 14 | - `length`: The length of each lane. 15 | 16 | Return a string representing the race track: 17 | 18 | - Each lane has exactly length positions filled with snow (`~`). 19 | - Each reindeer is represented with the letter `r`. 20 | - Lanes are numbered at the end with `/1`, `/2`, etc. 21 | - The view is **isometric**, so the lower lanes are shifted to the right. 22 | 23 | **Examples:** 24 | 25 | ```js 26 | drawRace([0, 5, -3], 10) 27 | /* 28 | ~~~~~~~~~~ /1 29 | ~~~~~r~~~~ /2 30 | ~~~~~~~r~~ /3 31 | */ 32 | 33 | drawRace([2, -1, 0, 5], 8) 34 | /* 35 | ~~r~~~~~ /1 36 | ~~~~~~~r /2 37 | ~~~~~~~~ /3 38 | ~~~~~r~~ /4 39 | */ 40 | 41 | drawRace([3, 7, -2], 12) 42 | /* 43 | ~~~r~~~~~~~~ /1 44 | ~~~~~~~r~~~~ /2 45 | ~~~~~~~~~~r~ /3 46 | */ 47 | ``` 48 | -------------------------------------------------------------------------------- /2024/challenge-08/mod.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {number[]} indices 3 | * @param {number} length 4 | * @returns {string} 5 | */ 6 | function drawRace(indices, length) { 7 | /** @type {string[]} */ 8 | const lanes = []; 9 | 10 | indices.forEach((position, index) => { 11 | const track = Array(length).fill("~"); 12 | 13 | track[(length + position) % length] = "r"; 14 | track[0] = "~"; 15 | 16 | const indentation = " ".repeat(indices.length - index - 1); 17 | 18 | lanes.push( 19 | `${indentation}${track.join("")} /${index + 1}` 20 | ); 21 | }); 22 | 23 | return lanes.join("\n"); 24 | } 25 | -------------------------------------------------------------------------------- /2024/challenge-08/mod.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | 3 | def draw_race(indices: List[int], length: int) -> str: 4 | lanes = [] 5 | 6 | for index, position in enumerate(indices): 7 | track = ["~"] * length 8 | 9 | r_position = (length + position) % length 10 | track[r_position] = "r" 11 | 12 | track[0] = "~" 13 | 14 | indentation = " " * (len(indices) - index - 1) 15 | 16 | lane_str = f"{indentation}{''.join(track)} /{index + 1}" 17 | lanes.append(lane_str) 18 | 19 | return '\n'.join(map(str, lanes)) 20 | -------------------------------------------------------------------------------- /2024/challenge-08/mod.ts: -------------------------------------------------------------------------------- 1 | function drawRace(indices: number[], length: number): string { 2 | const lanes: string[] = []; 3 | 4 | indices.forEach((position, index) => { 5 | const track = Array(length).fill("~"); 6 | 7 | track[(length + position) % length] = "r"; 8 | track[0] = "~"; 9 | 10 | const indentation = " ".repeat(indices.length - index - 1); 11 | 12 | lanes.push( 13 | `${indentation}${track.join("")} /${index + 1}` 14 | ); 15 | }); 16 | 17 | return lanes.join("\n"); 18 | } 19 | -------------------------------------------------------------------------------- /2024/challenge-09/mod.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @typedef {string[]} Board 3 | * @typedef {'U' | 'D' | 'R' | 'L'} Movement 4 | * @typedef {'none' | 'crash' | 'eat'} Result 5 | */ 6 | 7 | /** 8 | * @param {Board} board 9 | * @param {Movement} mov 10 | * @returns {Result} 11 | */ 12 | function moveTrain(board, mov) { 13 | /** @type {Record} */ 14 | const movementDeltas = { 15 | 'U': [+0, -1], 16 | 'D': [+0, +1], 17 | 'L': [-1, +0], 18 | 'R': [+1, +0], 19 | }; 20 | 21 | /** @type {Record} */ 22 | const positionCases = { 23 | '·': 'none', 24 | '*': 'eat', 25 | }; 26 | 27 | let enginePosition = { x: -1, y: -1 }; 28 | 29 | for (let y = 0; y < board.length; y++) { 30 | const x = board[y].indexOf('@'); 31 | 32 | if (x !== -1) { 33 | enginePosition = { x, y }; 34 | break; 35 | } 36 | } 37 | 38 | const [dx, dy] = movementDeltas[mov]; 39 | const { x, y } = enginePosition; 40 | 41 | const newPositionChar = board[y + dy]?.[x + dx]; 42 | 43 | return positionCases[newPositionChar] ?? 'crash'; 44 | } 45 | -------------------------------------------------------------------------------- /2024/challenge-09/mod.py: -------------------------------------------------------------------------------- 1 | from typing import List, Literal, Tuple 2 | 3 | Board = List[str] 4 | Movement = Literal['U', 'D', 'R', 'L'] 5 | Result = Literal['none', 'crash', 'eat'] 6 | 7 | def move_train(board: Board, mov: Movement) -> Result: 8 | movement_deltas = { 9 | 'U': (+0, -1), 10 | 'D': (+0, +1), 11 | 'L': (-1, +0), 12 | 'R': (+1, +0), 13 | } 14 | 15 | position_cases = { 16 | '·': 'none', 17 | '*': 'eat', 18 | } 19 | 20 | engine_position = (-1, -1) 21 | for y, row in enumerate(board): 22 | x = row.find('@') 23 | 24 | if x != -1: 25 | engine_position = (x, y) 26 | break 27 | 28 | dx, dy = movement_deltas[mov] 29 | x, y = engine_position 30 | 31 | new_position_char = ( 32 | board[y + dy][x + dx] if 0 <= y + dy < len(board) else None 33 | ) 34 | 35 | return position_cases.get(new_position_char, 'crash') 36 | -------------------------------------------------------------------------------- /2024/challenge-09/mod.ts: -------------------------------------------------------------------------------- 1 | type Board = string[]; 2 | type Movement = 'U' | 'D' | 'R' | 'L'; 3 | type Result = 'none' | 'crash' | 'eat'; 4 | 5 | function moveTrain(board: Board, mov: Movement): Result { 6 | const movementDeltas: Record = { 7 | 'U': [+0, -1], 8 | 'D': [+0, +1], 9 | 'L': [-1, +0], 10 | 'R': [+1, +0], 11 | }; 12 | 13 | const positionCases: Record = { 14 | '·': 'none', 15 | '*': 'eat', 16 | }; 17 | 18 | let enginePosition = { x: -1, y: -1 }; 19 | 20 | for (let y = 0; y < board.length; y++) { 21 | const x = board[y].indexOf('@'); 22 | 23 | if (x !== -1) { 24 | enginePosition = { x, y }; 25 | break; 26 | } 27 | } 28 | 29 | const [dx, dy] = movementDeltas[mov]; 30 | const { x, y } = enginePosition; 31 | 32 | const newPositionChar = board[y + dy]?.[x + dx]; 33 | 34 | return positionCases[newPositionChar] ?? 'crash'; 35 | } 36 | -------------------------------------------------------------------------------- /2024/challenge-10/mod.py: -------------------------------------------------------------------------------- 1 | from typing import List, Union, Dict 2 | 3 | def compile(instructions: List[str]) -> Union[int, None]: 4 | registers: Dict[str, int] = {} 5 | i = 0 6 | 7 | delta = {"INC": +1, "DEC": -1} 8 | 9 | def mov(opcode: str, literal: str, identifier: str): 10 | registers[identifier] = int(literal) if literal.isdigit() else registers.get(literal, 0) 11 | 12 | def update_register(opcode: str, identifier: str): 13 | registers[identifier] = registers.get(identifier, 0) + delta[opcode] 14 | 15 | def jmp(opcode: str, identifier: str, index: str): 16 | if registers.get(identifier, 0) == 0: 17 | i = int(index) - 1 18 | 19 | handlers = { 20 | 'MOV': mov, 21 | 'INC': update_register, 22 | 'DEC': update_register, 23 | 'JMP': jmp, 24 | } 25 | 26 | while i < len(instructions): 27 | parts = instructions[i].split() 28 | opcode, *args = parts 29 | 30 | handlers[opcode](opcode, *args) 31 | i += 1 32 | 33 | return registers.get('A') 34 | -------------------------------------------------------------------------------- /2024/challenge-10/mod.ts: -------------------------------------------------------------------------------- 1 | function compile(instructions: string[]): number | undefined { 2 | const registers: Record = {}; 3 | let i = 0; 4 | 5 | const handlers: Record void> = { 6 | 'MOV': (literal, identifier) => { 7 | registers[identifier] = Number(literal) || registers[literal] || 0; 8 | }, 9 | 'INC': (identifier) => { 10 | registers[identifier] = (registers[identifier] || 0) + 1; 11 | }, 12 | 'DEC': (identifier) => { 13 | registers[identifier] = (registers[identifier] || 0) - 1; 14 | }, 15 | 'JMP': (identifier, index) => { 16 | if ((registers[identifier] || 0) === 0) { 17 | i = Number(index) - 1; 18 | } 19 | } 20 | }; 21 | 22 | while (i < instructions.length) { 23 | const [opcode, ...args] = instructions[i].split(' '); 24 | 25 | handlers[opcode](...args); 26 | i++; 27 | } 28 | 29 | return registers['A']; 30 | } 31 | -------------------------------------------------------------------------------- /2024/challenge-11/README.md: -------------------------------------------------------------------------------- 1 | # Challenge #11: 🏴‍☠️ Filenames encoded 2 | 3 | **The Grinch has hacked 🏴‍☠️ Santa Claus's workshop systems** and has encoded the names of all the important files. Now the elves can't find the original files and they need your help to decipher the names. 4 | 5 | Each file follows this format: 6 | 7 | - It starts with a number (can contain any number of digits). 8 | - Then has an underscore `_`. 9 | - Continues with a **file name and its extension**. 10 | - Ends with an extra extension at the end (which we don't need). 11 | 12 | Keep in mind that the file names may contain letters (a-z, A-Z), numbers (0-9), **other underscores** (_), and hyphens (-). 13 | 14 | Your task is to implement a function that receives a string with the name of an encoded file and returns only the important part: **the file name and its extension**. 15 | 16 | Examples: 17 | 18 | ```js 19 | decodeFilename('2023122512345678_sleighDesign.png.grinchwa') 20 | // ➞ "sleighDesign.png" 21 | 22 | decodeFilename('42_chimney_dimensions.pdf.hack2023') 23 | // ➞ "chimney_dimensions.pdf" 24 | 25 | decodeFilename('987654321_elf-roster.csv.tempfile') 26 | // ➞ "elf-roster.csv" 27 | ``` 28 | -------------------------------------------------------------------------------- /2024/challenge-11/mod.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {string} filename 3 | * @returns {string} 4 | */ 5 | function decodeFilename(filename) { 6 | return filename.match(/(?<=\d+_)[\w-]+\.\w+/)[0]; 7 | } 8 | -------------------------------------------------------------------------------- /2024/challenge-11/mod.py: -------------------------------------------------------------------------------- 1 | import re 2 | 3 | def decode_filename(filename: str) -> str: 4 | match = re.search(r"(?<=\d+_)[\w-]+\.\w+", filename) 5 | 6 | return match.group(0) 7 | -------------------------------------------------------------------------------- /2024/challenge-11/mod.ts: -------------------------------------------------------------------------------- 1 | function decodeFilename(filename: string): string { 2 | return filename.match(/(?<=\d+_)[\w-]+\.\w+/)![0]; 3 | } 4 | -------------------------------------------------------------------------------- /2024/challenge-12/README.md: -------------------------------------------------------------------------------- 1 | # Challenge #12: 💵 How much does the tree cost? 2 | 3 | You are in a very special market where Christmas trees 🎄 are sold. Each one comes decorated with a series of very peculiar ornaments, and the price of the tree is determined by the ornaments it has. 4 | 5 | - `*`: Snowflake - Value: 1 6 | - `o`: Christmas Ball - Value: 5 7 | - `^`: Decorative Tree - Value: 10 8 | - `#`: Shiny Garland - Value: 50 9 | - `@`: Polar Star - Value: 100 10 | 11 | Normally, you would sum up all the values of the ornaments and that's it… 12 | 13 | But, watch out! **If an ornament is immediately to the left of another of greater value, instead of adding, its value is subtracted**. 14 | 15 | ```js 16 | calculatePrice('***') // 3 (1 + 1 + 1) 17 | calculatePrice('*o') // 4 (5 - 1) 18 | calculatePrice('o*') // 6 (5 + 1) 19 | calculatePrice('*o*') // 5 (-1 + 5 + 1) 20 | calculatePrice('**o*') // 6 (1 - 1 + 5 + 1) 21 | calculatePrice('o***') // 8 (5 + 3) 22 | calculatePrice('*o@') // 94 (-5 - 1 + 100) 23 | calculatePrice('*#') // 49 (-1 + 50) 24 | calculatePrice('@@@') // 300 (100 + 100 + 100) 25 | calculatePrice('#@') // 50 (-50 + 100) 26 | calculatePrice('#@Z') // undefined (Z is unknown) 27 | ``` 28 | -------------------------------------------------------------------------------- /2024/challenge-12/mod.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {string} ornaments 3 | * @returns {number | undefined} 4 | */ 5 | function calculatePrice(ornaments) { 6 | /** @type {Record} */ 7 | const patterns = { 8 | '*': 1, 9 | 'o': 5, 10 | '^': 10, 11 | '#': 50, 12 | '@': 100, 13 | }; 14 | 15 | let prev = patterns[ornaments[ornaments.length - 1]]; 16 | let acc = prev; 17 | 18 | for (let i = ornaments.length - 2; i >= 0; i--) { 19 | const curr = patterns[ornaments[i]]; 20 | 21 | [acc, prev] = curr < prev 22 | ? [acc - curr, curr] 23 | : [acc + curr, curr]; 24 | } 25 | 26 | return acc || undefined; 27 | } 28 | -------------------------------------------------------------------------------- /2024/challenge-12/mod.py: -------------------------------------------------------------------------------- 1 | def calculate_price(ornaments: str) -> int | None: 2 | patterns = { 3 | '*': 1, 4 | 'o': 5, 5 | '^': 10, 6 | '#': 50, 7 | '@': 100, 8 | } 9 | 10 | prev = patterns.get(ornaments[-1]) 11 | acc = prev 12 | 13 | for i in range(len(ornaments) - 2, -1, -1): 14 | curr = patterns.get(ornaments[i]) 15 | 16 | if curr < prev: 17 | acc -= curr 18 | else: 19 | acc += curr 20 | 21 | prev = curr 22 | 23 | return acc or None 24 | -------------------------------------------------------------------------------- /2024/challenge-12/mod.ts: -------------------------------------------------------------------------------- 1 | function calculatePrice(ornaments: string): number | undefined { 2 | const patterns: Record = { 3 | '*': 1, 4 | 'o': 5, 5 | '^': 10, 6 | '#': 50, 7 | '@': 100, 8 | }; 9 | 10 | let prev = patterns[ornaments[ornaments.length - 1]]; 11 | let acc = prev; 12 | 13 | for (let i = ornaments.length - 2; i >= 0; i--) { 14 | const curr = patterns[ornaments[i]]; 15 | 16 | [acc, prev] = curr < prev 17 | ? [acc - curr, curr] 18 | : [acc + curr, curr]; 19 | } 20 | 21 | return acc || undefined; 22 | } 23 | -------------------------------------------------------------------------------- /2024/challenge-13/mod.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {string} moves 3 | * @returns {true | [number, number]} 4 | */ 5 | function isRobotBack(moves) { 6 | /** @type {Record} */ 7 | const invertMoves = { 8 | R: "L", 9 | L: "R", 10 | U: "D", 11 | D: "U" 12 | }; 13 | 14 | moves = moves 15 | .replace(/\!(.)/g, 16 | (_, char) => invertMoves[char] 17 | ); 18 | 19 | moves = moves.replace(/\*(.)/g, 20 | (_, char) => char + char 21 | ); 22 | 23 | const conditional = /\?(.?)/g; 24 | 25 | do { 26 | moves = moves.replace(conditional, (_, char, offset) => 27 | [char, ""][+moves.substring(0, offset).includes(char)] 28 | ); 29 | } while (conditional.test(moves)); 30 | 31 | /** @type {Record} */ 32 | const movements = { 33 | R: [+1, +0], 34 | L: [-1, +0], 35 | U: [+0, +1], 36 | D: [+0, -1] 37 | }; 38 | 39 | let x = 0; 40 | let y = 0; 41 | 42 | for (const movement of moves) { 43 | const [dx, dy] = movements[movement] || [0, 0]; 44 | 45 | x += dx; 46 | y += dy; 47 | } 48 | 49 | return !!(!x & !y) || [x, y]; 50 | } 51 | -------------------------------------------------------------------------------- /2024/challenge-13/mod.py: -------------------------------------------------------------------------------- 1 | import re 2 | from typing import Union, List 3 | 4 | def isRobotBack(moves: str) -> Union[bool, List[int]]: 5 | invert_moves = { 6 | 'R': 'L', 7 | 'L': 'R', 8 | 'U': 'D', 9 | 'D': 'U' 10 | } 11 | 12 | moves = re.sub(r'\!(.)', lambda m: invert_moves.get(m.group(1), ''), moves) 13 | moves = re.sub(r'\*(.)', lambda m: m.group(1) * 2, moves) 14 | 15 | conditional_pattern = re.compile(r'\?(.?)') 16 | 17 | def replace_conditional(match: re.Match, current_moves: str) -> str: 18 | char = match.group(1) 19 | pos = match.start() 20 | if char: 21 | if char in current_moves[:pos]: 22 | return '' 23 | else: 24 | return char 25 | else: 26 | return '' 27 | while True: 28 | matches = list(conditional_pattern.finditer(moves)) 29 | if not matches: 30 | break 31 | 32 | replacements = [] 33 | for match in matches: 34 | replacement = replace_conditional(match, moves) 35 | replacements.append((match.start(), match.end(), replacement)) 36 | 37 | for start, end, replacement in reversed(replacements): 38 | moves = moves[:start] + replacement + moves[end:] 39 | 40 | movements = { 41 | 'R': (1, 0), 42 | 'L': (-1, 0), 43 | 'U': (0, 1), 44 | 'D': (0, -1) 45 | } 46 | 47 | x, y = 0, 0 48 | 49 | for move in moves: 50 | dx, dy = movements.get(move, (0, 0)) 51 | x += dx 52 | y += dy 53 | 54 | if x == 0 and y == 0: 55 | return True 56 | else: 57 | return [x, y] 58 | -------------------------------------------------------------------------------- /2024/challenge-14/mod.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {number[]} reindeer 3 | * @param {number[]} stables 4 | * @returns {number} 5 | */ 6 | function minMovesToStables(reindeer, stables) { 7 | reindeer.sort(); 8 | stables.sort(); 9 | 10 | return reindeer.reduce( 11 | (acc, curr, index) => acc + Math.abs(stables[index] - curr), 0 12 | ); 13 | } 14 | -------------------------------------------------------------------------------- /2024/challenge-14/mod.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | 3 | def min_moves_to_stables(reindeer: List[int], stables: List[int]) -> int: 4 | reindeer.sort() 5 | stables.sort() 6 | 7 | return sum(abs(stables[i] - reindeer[i]) for i in range(len(reindeer))) 8 | -------------------------------------------------------------------------------- /2024/challenge-14/mod.ts: -------------------------------------------------------------------------------- 1 | function minMovesToStables(reindeer: number[], stables: number[]): number { 2 | reindeer.sort(); 3 | stables.sort(); 4 | 5 | return reindeer.reduce( 6 | (acc, curr, index) => acc + Math.abs(stables[index] - curr), 0 7 | ); 8 | } 9 | -------------------------------------------------------------------------------- /2024/challenge-15/README.md: -------------------------------------------------------------------------------- 1 | # Challenge #15: ✏️ Drawing tables 2 | 3 | **ChatGPT has arrived at the North Pole** and the *elf Sam Elfman* is working on an application for managing gifts and children. 4 | 5 | To enhance the presentation, he wants to create a function `drawTable` that receives an array of objects and converts it into a text table. 6 | 7 | The drawn table should represent the object data as follows: 8 | 9 | - It has a header with the column name. 10 | - The column name has the **first letter capitalized**. 11 | - Each row should contain the values of the objects in the corresponding order. 12 | - Each value must be left-aligned. 13 | - Fields always leave a space on the left. 14 | - Fields leave the necessary space on the right to align the box. 15 | 16 | Look at the example to see how you should draw the table: 17 | 18 | ```js 19 | drawTable([ 20 | { name: 'Alice', city: 'London' }, 21 | { name: 'Bob', city: 'Paris' }, 22 | { name: 'Charlie', city: 'New York' } 23 | ]) 24 | // +---------+-----------+ 25 | // | Name | City | 26 | // +---------+-----------+ 27 | // | Alice | London | 28 | // | Bob | Paris | 29 | // | Charlie | New York | 30 | // +---------+-----------+ 31 | 32 | drawTable([ 33 | { gift: 'Doll', quantity: 10 }, 34 | { gift: 'Book', quantity: 5 }, 35 | { gift: 'Music CD', quantity: 1 } 36 | ]) 37 | // +----------+----------+ 38 | // | Gift | Quantity | 39 | // +----------+----------+ 40 | // | Doll | 10 | 41 | // | Book | 5 | 42 | // | Music CD | 1 | 43 | // +----------+----------+ 44 | ``` 45 | -------------------------------------------------------------------------------- /2024/challenge-15/mod.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {Array>} data 3 | * @returns {string} 4 | */ 5 | function drawTable(data) { 6 | let border = "+"; 7 | 8 | const columns = Object.keys(data[0]) 9 | .map((key) => [ 10 | key[0].toUpperCase() + key.slice(1), 11 | ...data.map((row) => row[key].toString()) 12 | ]); 13 | 14 | const tableRows = Array(columns[0].length).fill("|"); 15 | 16 | for (const column of columns) { 17 | const maxCellWidth = Math.max(...column.map(cell => cell.length)); 18 | 19 | let index = 0; 20 | for (const cell of column) { 21 | tableRows[index++] += ` ${cell.padEnd(maxCellWidth)} |`; 22 | } 23 | 24 | border += "-".repeat(maxCellWidth + 2) + "+"; 25 | } 26 | 27 | const [header, ...body] = tableRows; 28 | 29 | return [ 30 | border, 31 | header, 32 | border, 33 | ...body, 34 | border 35 | ].join("\n"); 36 | } 37 | -------------------------------------------------------------------------------- /2024/challenge-15/mod.py: -------------------------------------------------------------------------------- 1 | from typing import List, Dict, Union 2 | 3 | def draw_table(data: List[Dict[str, Union[int, str]]]) -> str: 4 | border = "+" 5 | 6 | columns = [ 7 | [key.capitalize()] + [str(row[key]) for row in data] 8 | for key in data[0].keys() 9 | ] 10 | 11 | table_rows = [""] * len(columns[0]) 12 | 13 | for column in columns: 14 | max_cell_width = max(len(cell) for cell in column) 15 | 16 | for i, cell in enumerate(column): 17 | table_rows[i] += f"| {cell.ljust(max_cell_width)} " 18 | 19 | border += "-" * (max_cell_width + 2) + "+" 20 | 21 | table_rows = [row + "|" for row in table_rows] 22 | 23 | header, *body = table_rows 24 | 25 | return ("\n".join([border, header, border, *body, border])) 26 | -------------------------------------------------------------------------------- /2024/challenge-15/mod.ts: -------------------------------------------------------------------------------- 1 | function drawTable(data: Array<{ [key: string]: number | string }>): string { 2 | let border = "+"; 3 | 4 | const columns = Object.keys(data[0]) 5 | .map((key) => [ 6 | key[0].toUpperCase() + key.slice(1), ...data.map( 7 | (row) => row[key].toString() 8 | ) 9 | ]); 10 | 11 | const tableRows = Array(columns[0].length).fill("|"); 12 | 13 | for (const column of columns) { 14 | const maxCellWidth = Math.max(...column.map(cell => cell.length)); 15 | 16 | let index = 0; 17 | for (const cell of column) { 18 | tableRows[index++] += ` ${cell.padEnd(maxCellWidth)} |`; 19 | } 20 | 21 | border += "-".repeat(maxCellWidth + 2) + "+"; 22 | } 23 | 24 | const [header, ...body] = tableRows; 25 | 26 | return [ 27 | border, 28 | header, 29 | border, 30 | ...body, 31 | border 32 | ].join("\n"); 33 | } 34 | -------------------------------------------------------------------------------- /2024/challenge-16/README.md: -------------------------------------------------------------------------------- 1 | # Challenge #16: ❄️ Cleaning the snow path 2 | 3 | The elves are working hard to clear paths filled with magical snow ❄️. This snow has a special property: if two **identical and adjacent** snow piles are found, **they disappear automatically**. 4 | 5 | Your task is to write a function to help the elves simulate this process. **The path is represented by a string and each snow pile by a character**. 6 | 7 | You need to remove all adjacent snow piles that are the same **until no more moves are possible**. 8 | 9 | The result should be the final path after removing all duplicate piles: 10 | 11 | ```js 12 | removeSnow('zxxzoz') // -> "oz" 13 | // 1. Remove "xx", resulting in "zzoz" 14 | // 2. Remove "zz", resulting in "oz" 15 | 16 | removeSnow('abcdd') // -> "abc" 17 | // 1. Remove "dd", resulting in "abc" 18 | 19 | removeSnow('zzz') // -> "z" 20 | // 1. Remove "zz", resulting in "z" 21 | 22 | removeSnow('a') // -> "a" 23 | // No duplicate piles 24 | ``` 25 | -------------------------------------------------------------------------------- /2024/challenge-16/mod.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {string} s 3 | * @returns {string} 4 | */ 5 | function removeSnow(s) { 6 | const pattern = /(.)\1/g; 7 | 8 | while (pattern.test(s)) { 9 | s = s.replace(pattern, ""); 10 | } 11 | 12 | return s; 13 | } 14 | -------------------------------------------------------------------------------- /2024/challenge-16/mod.py: -------------------------------------------------------------------------------- 1 | import re 2 | 3 | def remove_snow(s: str) -> str: 4 | pattern = re.compile(r"(.)\1") 5 | 6 | while pattern.search(s): 7 | s = pattern.sub("", s) 8 | 9 | return s 10 | -------------------------------------------------------------------------------- /2024/challenge-16/mod.ts: -------------------------------------------------------------------------------- 1 | function removeSnow(s: string): string { 2 | const pattern = /(.)\1/g; 3 | 4 | while (pattern.test(s)) { 5 | s = s.replace(pattern, ""); 6 | } 7 | 8 | return s; 9 | } 10 | -------------------------------------------------------------------------------- /2024/challenge-17/README.md: -------------------------------------------------------------------------------- 1 | # Challenge #17: 💣 Grinch's bombs 2 | 3 | The Grinch has been up to his tricks in the North Pole and has planted **explosive coal bombs** 💣 in the elves' toy factory. He wants all the toys to be rendered useless, and that's why he has left a grid where some cells have explosive coal (`true`) and others are empty (`false`). 4 | 5 | The elves need your help to **map the dangerous areas**. Each empty cell should display a number indicating **how many explosive coal bombs there are in the adjacent positions**, including diagonals. 6 | 7 | ```js 8 | detectBombs([ 9 | [true, false, false], 10 | [false, true, false], 11 | [false, false, false] 12 | ]) 13 | // [ 14 | // [1, 2, 1], 15 | // [2, 1, 1], 16 | // [1, 1, 1] 17 | // ] 18 | 19 | detectBombs([ 20 | [true, false], 21 | [false, false] 22 | ]) 23 | // [ 24 | // [0, 1], 25 | // [1, 1] 26 | // ] 27 | 28 | detectBombs([ 29 | [true, true], 30 | [false, false], 31 | [true, true] 32 | ]) 33 | 34 | // [ 35 | // [1, 1], 36 | // [4, 4], 37 | // [1, 1] 38 | // ] 39 | ``` 40 | 41 | **Note**: Want a hint? You've surely played the Minesweeper game before… 😉 42 | -------------------------------------------------------------------------------- /2024/challenge-17/mod.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {boolean[][]} grid 3 | * @returns {boolean[][]} 4 | */ 5 | function detectBombs(grid) { 6 | const cols = grid[0].length; 7 | const rows = grid.length; 8 | 9 | /** @type {boolean[][]} */ 10 | const result = Array.from( 11 | { length: rows }, () => Array(cols).fill(0) 12 | ); 13 | 14 | const directions = [ 15 | [-1, -1], [-1, +0], [-1, +1], 16 | [+0, -1], [+0, +1], 17 | [+1, -1], [+1, +0], [+1, +1], 18 | ]; 19 | 20 | for (let i = 0; i < rows; i++) { 21 | for (let j = 0; j < cols; j++) { 22 | const delta = [[], directions][+grid[i][j]]; 23 | 24 | for (const [dx, dy] of delta) { 25 | const ni = i + dx; 26 | const nj = j + dy; 27 | 28 | if ( 29 | Number(ni >= 0) 30 | & Number(ni < rows) 31 | & Number(nj >= 0) 32 | & Number(nj < cols) 33 | ) { 34 | result[ni][nj]++; 35 | } 36 | } 37 | } 38 | } 39 | 40 | return result; 41 | } 42 | -------------------------------------------------------------------------------- /2024/challenge-17/mod.py: -------------------------------------------------------------------------------- 1 | def detect_bombs(grid: list[list[bool]]) -> list[list[int]]: 2 | rows = len(grid) 3 | cols = len(grid[0]) 4 | 5 | result = [[0] * cols for _ in range(rows)] 6 | 7 | directions = [ 8 | (-1, -1), (-1, 0), (-1, 1), 9 | (0, -1), (0, 1), 10 | (1, -1), (1, 0), (1, 1), 11 | ] 12 | 13 | for i in range(rows): 14 | for j in range(cols): 15 | delta = directions if grid[i][j] else [] 16 | 17 | for dx, dy in delta: 18 | ni, nj = i + dx, j + dy 19 | 20 | if 0 <= ni < rows and 0 <= nj < cols: 21 | result[ni][nj] += 1 22 | 23 | return result 24 | -------------------------------------------------------------------------------- /2024/challenge-17/mod.ts: -------------------------------------------------------------------------------- 1 | function detectBombs(grid: boolean[][]): number[][] { 2 | const cols = grid[0].length; 3 | const rows = grid.length; 4 | 5 | const result: number[][] = Array.from( 6 | { length: rows }, () => Array(cols).fill(0) 7 | ); 8 | 9 | const directions = [ 10 | [-1, -1], [-1, +0], [-1, +1], 11 | [+0, -1], [+0, +1], 12 | [+1, -1], [+1, +0], [+1, +1], 13 | ]; 14 | 15 | for (let i = 0; i < rows; i++) { 16 | for (let j = 0; j < cols; j++) { 17 | const delta = [[], directions][+grid[i][j]]; 18 | 19 | for (const [dx, dy] of delta) { 20 | const ni = i + dx; 21 | const nj = j + dy; 22 | 23 | if ( 24 | Number(ni >= 0) 25 | & Number(ni < rows) 26 | & Number(nj >= 0) 27 | & Number(nj < cols) 28 | ) { 29 | result[ni][nj]++; 30 | } 31 | } 32 | } 33 | } 34 | 35 | return result; 36 | } 37 | -------------------------------------------------------------------------------- /2024/challenge-18/README.md: -------------------------------------------------------------------------------- 1 | # Challenge #18: 📇 Santa's Magic Agenda 2 | 3 | Santa Claus has a magic diary 📇 where he keeps the addresses of the children to deliver the presents. The problem: **the diary's information is mixed and misformatted**. The lines contain a magic phone number, a child's name, and their address, but everything is surrounded by strange characters. 4 | 5 | Santa needs your help to find specific information from the diary. Write a function that, **given the diary's content and a phone number, returns the child's name and address**. 6 | 7 | Keep in mind that in the diary: 8 | 9 | Phone numbers are formatted as +X-YYY-YYY-YYY (where X is one or two digits, and Y is a digit). 10 | Each child's name is always between < and > 11 | The idea is for you to write a function that, given the full phone number or part of it, returns the child's name and address. **If it doesn't find anything or there is more than one result**, you must return `null`. 12 | 13 | ```js 14 | const agenda = `+34-600-123-456 Calle Gran Via 12 15 | Plaza Mayor 45 Madrid 28013 +34-600-987-654 16 | +1-800-555-0199 Fifth Ave New York` 17 | 18 | findInAgenda(agenda, '34-600-123-456') 19 | // { name: "Juan Perez", address: "Calle Gran Via 12" } 20 | 21 | findInAgenda(agenda, '600-987') 22 | // { name: "Maria Gomez", address: "Plaza Mayor 45 Madrid 28013" } 23 | 24 | findInAgenda(agenda, '111') 25 | // null 26 | // Explanation: No results 27 | 28 | findInAgenda(agenda, '1') 29 | // null 30 | // Explanation: Too many results 31 | ``` 32 | -------------------------------------------------------------------------------- /2024/challenge-18/mod.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @typedef {Object} AgendaEntry 3 | * @property {string} name 4 | * @property {string} address 5 | */ 6 | 7 | /** 8 | * @param {string} agenda 9 | * @param {string} phone 10 | * @returns {AgendaEntry | null} 11 | */ 12 | function findInAgenda(agenda, phone) { 13 | /** @type {AgendaEntry[]} */ 14 | const data = []; 15 | 16 | for (const entry of agenda.split("\n")) { 17 | const [matchedPhone] = entry.match(/\+[-\d]+/) || []; 18 | const [_, name] = entry.match(/<([^>]+)>/) || []; 19 | 20 | if (!matchedPhone.includes(phone)) continue; 21 | 22 | const address = entry 23 | .replace(matchedPhone, "") 24 | .replace(`<${name}>`, ""); 25 | 26 | data.push({ 27 | address: address.trim(), 28 | name: name.trim() 29 | }); 30 | } 31 | 32 | return data.length === 1 33 | ? data[0] 34 | : null; 35 | } 36 | -------------------------------------------------------------------------------- /2024/challenge-18/mod.py: -------------------------------------------------------------------------------- 1 | import re 2 | 3 | def find_in_agenda(agenda: str, phone: str): 4 | data = [] 5 | 6 | for entry in agenda.split("\n"): 7 | matched_phone = re.search(r'\+[-\d]+', entry) 8 | name_match = re.search(r'<([^>]+)>', entry) 9 | 10 | if matched_phone and name_match: 11 | matched_phone = matched_phone.group(0) 12 | name = name_match.group(1) 13 | 14 | if phone not in matched_phone: 15 | continue 16 | 17 | address = entry.replace(matched_phone, "").replace(f"<{name}>", "").strip() 18 | 19 | data.append({ 20 | 'name': name.strip(), 21 | 'address': address 22 | }) 23 | 24 | return data[0] if len(data) == 1 else None 25 | -------------------------------------------------------------------------------- /2024/challenge-18/mod.ts: -------------------------------------------------------------------------------- 1 | function findInAgenda(agenda: string, phone: string): { name: string; address: string } | null { 2 | const data: { name: string; address: string }[] = []; 3 | 4 | for (const entry of agenda.split("\n")) { 5 | const [matchedPhone] = entry.match(/\+[-\d]+/)!; 6 | const [_, name] = entry.match(/<([^>]+)>/)!; 7 | 8 | if (!matchedPhone.includes(phone)) continue; 9 | 10 | const address = entry 11 | .replace(matchedPhone, "") 12 | .replace(`<${name}>`, ""); 13 | 14 | data.push({ 15 | address: address.trim(), 16 | name: name.trim() 17 | }); 18 | } 19 | 20 | return data.length === 1 21 | ? data[0] 22 | : null; 23 | } 24 | -------------------------------------------------------------------------------- /2024/challenge-19/mod.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {number} weight 3 | * @returns {string} 4 | */ 5 | function distributeWeight(weight) { 6 | /** @type {Record} */ 7 | const boxes = { 8 | 1: [ 9 | " _ ", 10 | "|_|", 11 | ], 12 | 2: [ 13 | " ___ ", 14 | "|___|", 15 | ], 16 | 5: [ 17 | " _____ ", 18 | "| |", 19 | "|_____|", 20 | ], 21 | 10: [ 22 | " _________ ", 23 | "| |", 24 | "|_________|", 25 | ], 26 | }; 27 | 28 | const sortedWeights = Object.keys(boxes).map(Number).sort((a, b) => a - b); 29 | 30 | /** @type {string[]} */ 31 | let output = []; 32 | 33 | while (weight > 0) { 34 | const w = sortedWeights.findLast(x => x <= weight); 35 | weight -= w; 36 | 37 | if (!output.length) { 38 | output = boxes[w]; 39 | } else { 40 | const box = boxes[w]; 41 | 42 | const line = box[box.length - 1] + output[0] 43 | .slice(box[box.length - 1].length) 44 | .trim(); 45 | 46 | output = box 47 | .slice(0, -1) 48 | .concat(line, output.slice(1)); 49 | } 50 | } 51 | 52 | return output.join("\n"); 53 | } 54 | -------------------------------------------------------------------------------- /2024/challenge-19/mod.py: -------------------------------------------------------------------------------- 1 | def distribute_weight(weight: int) -> str: 2 | boxes = { 3 | 1: [ 4 | " _ ", 5 | "|_|", 6 | ], 7 | 2: [ 8 | " ___ ", 9 | "|___|", 10 | ], 11 | 5: [ 12 | " _____ ", 13 | "| |", 14 | "|_____|", 15 | ], 16 | 10: [ 17 | " _________ ", 18 | "| |", 19 | "|_________|", 20 | ], 21 | } 22 | 23 | sorted_weights = sorted(boxes.keys(), reverse=True) 24 | 25 | output = [] 26 | 27 | while weight > 0: 28 | w = None 29 | for x in sorted_weights: 30 | if x <= weight: 31 | w = x 32 | break 33 | 34 | weight -= w 35 | 36 | if not output: 37 | output = boxes[w] 38 | else: 39 | box = boxes[w] 40 | 41 | new_box_last_line = box[-1] 42 | 43 | output_first_line = output[0] 44 | slice_start = len(new_box_last_line) 45 | output_slice = output_first_line[slice_start:].strip() 46 | 47 | line = new_box_last_line + output_slice 48 | 49 | output = box[:-1] + [line] + output[1:] 50 | 51 | return "\n".join(output) 52 | -------------------------------------------------------------------------------- /2024/challenge-19/mod.ts: -------------------------------------------------------------------------------- 1 | function distributeWeight(weight: number): string { 2 | const boxes: Record = { 3 | 1: [ 4 | " _ ", 5 | "|_|", 6 | ], 7 | 2: [ 8 | " ___ ", 9 | "|___|", 10 | ], 11 | 5: [ 12 | " _____ ", 13 | "| |", 14 | "|_____|", 15 | ], 16 | 10: [ 17 | " _________ ", 18 | "| |", 19 | "|_________|", 20 | ], 21 | }; 22 | 23 | const sortedWeights = Object.keys(boxes).map(Number).sort((a, b) => a - b); 24 | 25 | let output: string[] = []; 26 | 27 | while (weight > 0) { 28 | const w = sortedWeights.findLast(x => x <= weight)!; 29 | weight -= w; 30 | 31 | if (!output.length) { 32 | output = boxes[w]; 33 | } else { 34 | const box = boxes[w]; 35 | 36 | const line = box[box.length - 1] + output[0] 37 | .slice(box[box.length - 1].length) 38 | .trim(); 39 | 40 | output = box 41 | .slice(0, -1) 42 | .concat(line, output.slice(1)); 43 | } 44 | } 45 | 46 | return output.join("\n"); 47 | } 48 | -------------------------------------------------------------------------------- /2024/challenge-20/mod.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @typedef {Object} Result 3 | * @property {Record} missing 4 | * @property {Record} extra 5 | */ 6 | 7 | /** 8 | * @param {string[]} received 9 | * @param {string[]} expected 10 | * @returns {Result} 11 | */ 12 | function fixGiftList(received, expected) { 13 | /** 14 | * @param {string[]} items 15 | * @returns {Record} 16 | */ 17 | const count = (items) => { 18 | const counter = {}; 19 | 20 | items.forEach((item) => { 21 | counter[item] = (counter[item] || 0) + 1; 22 | }); 23 | 24 | return counter; 25 | }; 26 | 27 | const receivedCount = count(received); 28 | const expectedCount = count(expected); 29 | 30 | const data = { 31 | missing: {}, extra: {} 32 | }; 33 | 34 | const items = new Set([...received, ...expected]); 35 | 36 | for (const item of items) { 37 | const b = receivedCount[item] ?? 0; 38 | const a = expectedCount[item] ?? 0; 39 | 40 | if (a > b) { 41 | data.missing[item] = a - b; 42 | } else if (b > a) { 43 | data.extra[item] = b - a; 44 | } 45 | } 46 | 47 | return data; 48 | } 49 | -------------------------------------------------------------------------------- /2024/challenge-20/mod.py: -------------------------------------------------------------------------------- 1 | from collections import Counter 2 | from typing import List, Dict 3 | 4 | Result = Dict[str, Dict[str, int]] 5 | 6 | def fix_gift_list(received: List[str], expected: List[str]) -> Result: 7 | received_count = Counter(received) 8 | expected_count = Counter(expected) 9 | 10 | data: Result = { 11 | 'missing': {}, 12 | 'extra': {} 13 | } 14 | 15 | items = set(received_count.keys()).union(expected_count.keys()) 16 | 17 | for item in items: 18 | b = received_count.get(item, 0) 19 | a = expected_count.get(item, 0) 20 | 21 | if a > b: 22 | data['missing'][item] = a - b 23 | elif b > a: 24 | data['extra'][item] = b - a 25 | 26 | return data 27 | -------------------------------------------------------------------------------- /2024/challenge-20/mod.ts: -------------------------------------------------------------------------------- 1 | type Result = { 2 | missing: Record, 3 | extra: Record 4 | }; 5 | 6 | function fixGiftList(received: string[], expected: string[]): Result { 7 | const count = (items: string[]) => { 8 | const counter: Record = {}; 9 | 10 | items.forEach((item) => { 11 | counter[item] = (counter[item] || 0) + 1; 12 | }); 13 | 14 | return counter; 15 | }; 16 | 17 | const receivedCount = count(received); 18 | const expectedCount = count(expected); 19 | 20 | const data: Result = { 21 | missing: {}, extra: {} 22 | }; 23 | 24 | const items = new Set([...received, ...expected]); 25 | 26 | for (const item of items) { 27 | const b = receivedCount[item] ?? 0; 28 | const a = expectedCount[item] ?? 0; 29 | 30 | if (a > b) { 31 | data.missing[item] = a - b; 32 | } else if (b > a) { 33 | data.extra[item] = b - a; 34 | } 35 | } 36 | 37 | return data; 38 | } 39 | -------------------------------------------------------------------------------- /2024/challenge-21/README.md: -------------------------------------------------------------------------------- 1 | # Challenge #21: 🎄 Calculate the height of the Christmas tree 2 | 3 | Santa Claus 🎅 is decorating a magical Christmas tree 🪄, which this year has a special structure in the form of a **binary tree**. Each node of the tree represents a gift, and Santa wants to know the **height of the tree** to place the magical star at the top. 4 | 5 | Your task is to write a function that calculates the height of a binary tree. The height of a binary tree is defined as the maximum number of levels from the root to a leaf. An empty tree has a height of `0`. 6 | 7 | ```js 8 | // Tree definition 9 | const tree = { 10 | value: '🎁', 11 | left: { 12 | value: '🎄', 13 | left: { 14 | value: '⭐', 15 | left: null, 16 | right: null 17 | }, 18 | right: { 19 | value: '🎅', 20 | left: null, 21 | right: null 22 | } 23 | }, 24 | right: { 25 | value: '❄️', 26 | left: null, 27 | right: { 28 | value: '🦌', 29 | left: null, 30 | right: null 31 | } 32 | } 33 | } 34 | 35 | // Graphical representation of the tree: 36 | // 🎁 37 | // / \ 38 | // 🎄 ❄️ 39 | // / \ \ 40 | // ⭐ 🎅 🦌 41 | 42 | // Function call 43 | treeHeight(tree) 44 | // Returns: 3 45 | ``` 46 | -------------------------------------------------------------------------------- /2024/challenge-21/mod.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @typedef {Object} TreeNode 3 | * @property {string} value 4 | * @property {TreeNode | null} left 5 | * @property {TreeNode | null} right 6 | */ 7 | 8 | /** 9 | * @param {TreeNode | null} tree 10 | * @returns {number} 11 | */ 12 | function treeHeight(tree) { 13 | if (tree === null) { 14 | return 0; 15 | } 16 | 17 | const rightHeight = treeHeight(tree.right); 18 | const leftHeight = treeHeight(tree.left); 19 | 20 | return Math.max(leftHeight, rightHeight) + 1; 21 | } 22 | -------------------------------------------------------------------------------- /2024/challenge-21/mod.py: -------------------------------------------------------------------------------- 1 | from typing import Optional, Dict, Any 2 | 3 | TreeNode = Optional[Dict[str, Any]] 4 | 5 | def tree_height(tree: TreeNode) -> int: 6 | if tree is None: 7 | return 0 8 | 9 | right_height = tree_height(tree.get('right')) 10 | left_height = tree_height(tree.get('left')) 11 | 12 | return max(left_height, right_height) + 1 13 | -------------------------------------------------------------------------------- /2024/challenge-21/mod.ts: -------------------------------------------------------------------------------- 1 | function treeHeight(tree: { value: string; left: any; right: any } | null): number { 2 | if (tree === null) { 3 | return 0; 4 | } 5 | 6 | const rightHeight = treeHeight(tree.right); 7 | const leftHeight = treeHeight(tree.left); 8 | 9 | return Math.max(leftHeight, rightHeight) + 1; 10 | } 11 | -------------------------------------------------------------------------------- /2024/challenge-22/README.md: -------------------------------------------------------------------------------- 1 | # Challenge #22: 🎁 Generate gift combinations 2 | 3 | Santa Claus 🎅 is checking a **list of unique toys that he might include in his magic gift bag**. He wants to explore all possible combinations of toys. He wants to see all combinations that actually contain at least one toy. 4 | 5 | Your task is to write a function that, given an array of toys, **returns all possible combinations**. 6 | 7 | **Important**: You must return it in the order the toys appear and in combinations from 1 to n toys. 8 | 9 | ```js 10 | generateGiftSets(['car', 'doll', 'puzzle']) 11 | // [ 12 | // ['car'], 13 | // ['doll'], 14 | // ['puzzle'], 15 | // ['car', 'doll'], 16 | // ['car', 'puzzle'], 17 | // ['doll', 'puzzle'], 18 | // ['car', 'doll', 'puzzle'] 19 | // ] 20 | 21 | generateGiftSets(['ball']) 22 | // [ 23 | // ['ball'] 24 | // ] 25 | 26 | generateGiftSets(['game', 'pc']) 27 | // [ 28 | // ['game'], 29 | // ['pc'], 30 | // ['game', 'pc'] 31 | // ] 32 | ``` 33 | 34 | **Note: The input array will always have at least one toy and there will never be duplicate toys.** 35 | 36 | **Tip**: There are many ways to solve this problem, but backtracking might be a good option. 😉 37 | -------------------------------------------------------------------------------- /2024/challenge-22/mod.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {string[]} gifts 3 | * @returns {string[][]} 4 | */ 5 | function generateGiftSets(gifts) { 6 | const result = []; 7 | 8 | /** 9 | * @param {number} start 10 | * @param {string[]} path 11 | * @param {number} k 12 | * @returns {void} 13 | */ 14 | const combine = (start, path, k) => { 15 | if (path.length === k) { 16 | result.push([...path]); 17 | return; 18 | } 19 | 20 | let i = start; 21 | while (i < gifts.length) { 22 | const gift = gifts[i++]; 23 | path.push(gift); 24 | 25 | combine(i, path, k); 26 | path.pop(); 27 | } 28 | }; 29 | 30 | for (let size = 1; size <= gifts.length; size++) { 31 | combine(0, [], size); 32 | } 33 | 34 | return result; 35 | } 36 | -------------------------------------------------------------------------------- /2024/challenge-22/mod.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | 3 | def generate_gift_sets(gifts: List[str]) -> List[List[str]]: 4 | result = [] 5 | 6 | def combine(start: int, path: List[str], k: int) -> None: 7 | if len(path) == k: 8 | result.append(path.copy()) 9 | return 10 | 11 | i = start 12 | while i < len(gifts): 13 | gift = gifts[i] 14 | i += 1 15 | path.append(gift) 16 | 17 | combine(i, path, k) 18 | path.pop() 19 | 20 | for size in range(1, len(gifts) + 1): 21 | combine(0, [], size) 22 | 23 | return result 24 | -------------------------------------------------------------------------------- /2024/challenge-22/mod.ts: -------------------------------------------------------------------------------- 1 | function generateGiftSets(gifts: string[]): string[][] { 2 | const result: string[][] = []; 3 | 4 | const combine = (start: number, path: string[], k: number): unknown => { 5 | if (path.length === k) { 6 | return result.push([...path]); 7 | } 8 | 9 | let i = start; 10 | while (i < gifts.length) { 11 | const gift = gifts[i++]; 12 | path.push(gift); 13 | 14 | combine(i, path, k); 15 | path.pop(); 16 | } 17 | }; 18 | 19 | for (let size = 1; size <= gifts.length; size++) { 20 | combine(0, [], size); 21 | } 22 | 23 | return result; 24 | } 25 | -------------------------------------------------------------------------------- /2024/challenge-23/README.md: -------------------------------------------------------------------------------- 1 | # Challenge #23: 🔢 Find the missing numbers 2 | 3 | The elves are working on a system to verify children's gift lists 👧👦. However, some lists are incomplete and **numbers are missing!** 4 | 5 | Your task is to write **a function that, given an array of numbers, finds all the numbers that are missing between 1 and `n`** (where `n` is the size of the array or the highest number in the array). 6 | 7 | Keep in mind that: 8 | 9 | - Numbers may appear more than once and others may be missing 10 | - The array always contains positive integers 11 | - Counting always starts from 1 12 | 13 | ```js 14 | findMissingNumbers([1, 2, 4, 6]) 15 | // [3, 5] 16 | 17 | findMissingNumbers([4, 8, 7, 2]) 18 | // [1, 3, 5, 6] 19 | 20 | findMissingNumbers([3, 2, 1, 1]) 21 | // [] 22 | 23 | findDisappearedNumbers([5, 5, 5, 3, 3, 2, 1]) 24 | // [4] 25 | ``` 26 | -------------------------------------------------------------------------------- /2024/challenge-23/mod.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {number[]} nums 3 | * @returns {number[]} 4 | */ 5 | function findMissingNumbers(nums) { 6 | nums = Array.from(new Set(nums)); 7 | 8 | const n = Math.max(nums.length, Math.max(...nums)); 9 | 10 | const fullSet = new Set(Array.from({ length: n }, (_, i) => i + 1)); 11 | 12 | for (const num of nums) { 13 | fullSet.delete(num); 14 | } 15 | 16 | return Array.from(fullSet); 17 | } 18 | -------------------------------------------------------------------------------- /2024/challenge-23/mod.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | 3 | def find_missing_numbers(nums: List[int]) -> List[int]: 4 | unique_nums = set(nums) 5 | 6 | n = max(len(unique_nums), max(unique_nums, default=0)) 7 | 8 | missing = [num for num in range(1, n + 1) if num not in unique_nums] 9 | 10 | return missing 11 | -------------------------------------------------------------------------------- /2024/challenge-23/mod.ts: -------------------------------------------------------------------------------- 1 | function findMissingNumbers(nums: number[]): number[] { 2 | nums = Array.from(new Set(nums)); 3 | 4 | const n = Math.max(nums.length, Math.max(...nums)); 5 | 6 | const fullSet = new Set(Array.from({ length: n }, (_, i) => i + 1)); 7 | 8 | for (const num of nums) { 9 | fullSet.delete(num); 10 | } 11 | 12 | return Array.from(fullSet); 13 | } 14 | -------------------------------------------------------------------------------- /2024/challenge-24/mod.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @typedef {Object} Tree 3 | * @property {string} value 4 | * @property {Tree | undefined} left 5 | * @property {Tree | undefined} right 6 | */ 7 | 8 | /** 9 | * @param {Tree | undefined} tree1 10 | * @param {Tree | undefined} tree2 11 | * @returns {[boolean, string]} 12 | */ 13 | function isTreesSynchronized(tree1, tree2) { 14 | const { value } = tree1; 15 | 16 | /** 17 | * @param {Tree | undefined} node1 18 | * @param {Tree | undefined} node2 19 | * @returns {boolean} 20 | */ 21 | function isMirror(node1, node2) { 22 | if (!node1 && !node2) { 23 | return true; 24 | } 25 | 26 | if (!node1 || !node2 || node1.value !== node2.value) { 27 | return false; 28 | } 29 | 30 | return ( 31 | isMirror(node1.left, node2.right) && 32 | isMirror(node1.right, node2.left) 33 | ); 34 | } 35 | 36 | const synchronized = isMirror(tree1, tree2); 37 | 38 | return [synchronized, value]; 39 | } 40 | -------------------------------------------------------------------------------- /2024/challenge-24/mod.py: -------------------------------------------------------------------------------- 1 | from typing import Optional, Dict, Any, Tuple 2 | 3 | Tree = Optional[Dict[str, Any]] 4 | 5 | def is_trees_synchronized(tree1: Tree, tree2: Tree) -> Tuple[bool, str]: 6 | value = tree1['value'] 7 | 8 | def is_mirror(node1: Tree, node2: Tree) -> bool: 9 | if not node1 and not node2: 10 | return True 11 | if not node1 or not node2 or node1['value'] != node2['value']: 12 | return False 13 | 14 | return ( 15 | is_mirror(node1.get('left'), node2.get('right')) and is_mirror(node1.get('right'), node2.get('left')) 16 | ) 17 | 18 | synchronized = is_mirror(tree1, tree2) 19 | 20 | return (synchronized, value) 21 | -------------------------------------------------------------------------------- /2024/challenge-24/mod.ts: -------------------------------------------------------------------------------- 1 | type Tree = { value: string; left?: Tree; right?: Tree }; 2 | 3 | function isTreesSynchronized(tree1?: Tree, tree2?: Tree): [boolean, string] { 4 | const { value } = tree1!; 5 | 6 | function isMirror(node1: typeof tree1, node2: typeof tree2): boolean { 7 | if (!node1 && !node2) { 8 | return true; 9 | } 10 | 11 | if (!node1 || !node2 || node1.value !== node2.value) { 12 | return false; 13 | } 14 | 15 | return ( 16 | isMirror(node1.left, node2.right) && 17 | isMirror(node1.right, node2.left) 18 | ); 19 | } 20 | 21 | const synchronized = isMirror(tree1, tree2); 22 | 23 | return [synchronized, value]; 24 | } 25 | -------------------------------------------------------------------------------- /2024/challenge-25/README.md: -------------------------------------------------------------------------------- 1 | # Challenge #25: 🪄 Execute the magical language 2 | 3 | We have already distributed all the gifts! Back at the workshop, preparations for next year are already beginning. 4 | 5 | A genius elf is creating a magical programming language 🪄 that will help streamline the delivery of gifts to children in 2025. 6 | 7 | Programs always start with the value `0`, and the language is a string where each character represents an instruction: 8 | 9 | - `>` Moves to the next instruction 10 | - `+` Increments the current value by 1 11 | - `-` Decrements the current value by 1 12 | - `[` and `]`: Loop. If the current value is `0`, jump to the instruction after `]`. If it is not `0`, go back to the instruction after `[` 13 | - `{` and `}`: Conditional. If the current value is `0`, jump to the instruction after `}`. If it is not `0`, continue to the instruction after `{` 14 | 15 | You need to return the value of the program after executing all the instructions. 16 | 17 | ```js 18 | execute('+++') // 3 19 | execute('+--') // -1 20 | execute('>+++[-]') // 0 21 | execute('>>>+{++}') // 3 22 | execute('+{[-]+}+') // 2 23 | execute('{+}{+}{+}') // 0 24 | execute('------[+]++') // 2 25 | execute('-[++{-}]+{++++}') // 5 26 | ``` 27 | 28 | **Note: A conditional can have a loop inside, and a loop can also have a conditional inside. But two loops or two conditionals are never nested.** 29 | -------------------------------------------------------------------------------- /2024/challenge-25/mod.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {string} code 3 | * @returns {number} 4 | */ 5 | function execute(code) { 6 | let pos = 0; 7 | let val = 0; 8 | 9 | let loopIndex = 0; 10 | 11 | /** @type {Record} */ 12 | const delta = { 13 | '-': -1, 14 | '+': +1, 15 | }; 16 | 17 | const pairs = { 18 | '{': '}', 19 | '[': ']' 20 | }; 21 | 22 | while (pos < code.length) { 23 | const char = code[pos]; 24 | 25 | switch (char) { 26 | case '{': 27 | case '[': { 28 | if (val === 0) { 29 | const pair = pairs[char]; 30 | 31 | pos += code 32 | .slice(pos) 33 | .indexOf(pair); 34 | } else if (char === '[') { 35 | loopIndex = pos; 36 | } 37 | 38 | } 39 | break; 40 | 41 | case ']': { 42 | if (char === ']' && val !== 0) { 43 | pos = loopIndex; 44 | } 45 | } 46 | break; 47 | 48 | default: 49 | val += delta[char] ?? 0; 50 | } 51 | 52 | pos++; 53 | } 54 | 55 | return val; 56 | } 57 | -------------------------------------------------------------------------------- /2024/challenge-25/mod.py: -------------------------------------------------------------------------------- 1 | def execute(code: str) -> int: 2 | pos = 0 3 | val = 0 4 | 5 | loop_index = 0 6 | 7 | delta = { 8 | '-': -1, 9 | '+': 1, 10 | } 11 | 12 | pairs = { 13 | '{': '}', 14 | '[': ']' 15 | } 16 | 17 | while pos < len(code): 18 | char = code[pos] 19 | 20 | if char in pairs: 21 | if val == 0: 22 | pair = pairs.get(char) 23 | pos += code[pos:].index(pair) - 1 24 | elif char == '[': 25 | loop_index = pos 26 | 27 | elif char == ']': 28 | if val != 0: 29 | pos = loop_index 30 | 31 | else: 32 | val += delta.get(char, 0) 33 | 34 | pos += 1 35 | 36 | return val 37 | -------------------------------------------------------------------------------- /2024/challenge-25/mod.ts: -------------------------------------------------------------------------------- 1 | function execute(code: string): number { 2 | let pos = 0; 3 | let val = 0; 4 | 5 | let loopIndex = 0; 6 | 7 | const delta: Record = { 8 | '-': -1, 9 | '+': +1, 10 | }; 11 | 12 | const pairs = { 13 | '{': '}', 14 | '[': ']' 15 | }; 16 | 17 | while (pos < code.length) { 18 | const char = code[pos]; 19 | 20 | switch (char) { 21 | case '{': 22 | case '[': { 23 | if (val === 0) { 24 | const pair = pairs[char]; 25 | 26 | pos += code 27 | .slice(pos) 28 | .indexOf(pair); 29 | } else if (char === '[') { 30 | loopIndex = pos; 31 | } 32 | 33 | } 34 | break; 35 | 36 | case ']': { 37 | if (char === ']' && val !== 0) { 38 | pos = loopIndex; 39 | } 40 | } 41 | break; 42 | 43 | default: 44 | val += delta[char] ?? 0; 45 | } 46 | 47 | pos++; 48 | } 49 | 50 | return val; 51 | } 52 | -------------------------------------------------------------------------------- /2024/challenge-26/README.md: -------------------------------------------------------------------------------- 1 | # Challenge #26: 🎯 Calculate the completed percentage 2 | 3 | Santa Claus has already delivered all the presents! Now he's reviewing the productivity reports of the elves. But there's a problem: **the Product Owner, Mrs. Claus** 🧑‍🎄✨, needs to quickly understand if the elves met the estimated times. They are doing **Agile Scream**. 4 | 5 | To help **Mrs. Claus**, your task is to calculate the completed percentage of each task and return it rounded to the nearest whole number. This will allow her to better plan for the next Christmas and keep everyone happy. 6 | 7 | This is the function she's expecting: 8 | 9 | ```js 10 | getCompleted('01:00:00', '03:00:00') // 33% 11 | getCompleted('02:00:00', '04:00:00') // 50% 12 | getCompleted('01:00:00', '01:00:00') // 100% 13 | getCompleted('00:10:00', '01:00:00') // 17% 14 | getCompleted('01:10:10', '03:30:30') // 33% 15 | getCompleted('03:30:30', '05:50:50') // 60% 16 | ``` 17 | 18 | **🎁 Now Santa Claus and the elves deserve a break. We hope they enjoyed AdventJS and will recommend it to their friends!** 19 | -------------------------------------------------------------------------------- /2024/challenge-26/mod.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * @param {string} timeWorked 4 | * @param {string} totalTime 5 | * @returns {string} 6 | */ 7 | function getCompleted(timeWorked, totalTime) { 8 | /** 9 | * @param {string} time 10 | * @returns {number} 11 | */ 12 | function parseTime(time) { 13 | const [hh, mm, ss] = time.split(':').map(Number); 14 | return hh * 3600 + mm * 60 + ss; 15 | } 16 | 17 | const workedSeconds = parseTime(timeWorked); 18 | const totalSeconds = parseTime(totalTime); 19 | 20 | const percentage = Math.round((workedSeconds / totalSeconds) * 100); 21 | return `${percentage}%`; 22 | } 23 | -------------------------------------------------------------------------------- /2024/challenge-26/mod.py: -------------------------------------------------------------------------------- 1 | def get_completed(time_worked, total_time): 2 | def parse_time(time): 3 | hh, mm, ss = map(int, time.split(':')) 4 | return hh * 3600 + mm * 60 + ss 5 | 6 | worked_seconds = parse_time(time_worked) 7 | total_seconds = parse_time(total_time) 8 | 9 | percentage = round(worked_seconds / total_seconds * 100) 10 | 11 | return f"{percentage}%" 12 | -------------------------------------------------------------------------------- /2024/challenge-26/mod.ts: -------------------------------------------------------------------------------- 1 | function getCompleted(timeWorked: string, totalTime: string): string { 2 | function parseTime(time: string): number { 3 | const [hh, mm, ss] = time.split(':').map(Number); 4 | return hh * 3600 + mm * 60 + ss; 5 | } 6 | 7 | const workedSeconds = parseTime(timeWorked); 8 | const totalSeconds = parseTime(totalTime); 9 | 10 | const percentage = Math.round((workedSeconds / totalSeconds) * 100); 11 | return `${percentage}%`; 12 | } 13 | --------------------------------------------------------------------------------