├── 2021 ├── challenge-11 │ ├── challenge-11.ts │ ├── challenge-11.test.ts │ └── README.md ├── challenge-20 │ ├── challenge-20.ts │ ├── challenge-20.test.ts │ └── README.md ├── challenge-02 │ ├── challenge-02.ts │ ├── challenge-02.test.ts │ └── README.md ├── challenge-08 │ ├── challenge-08.ts │ ├── challenge-08.test.ts │ └── README.md ├── challenge-12 │ ├── challenge-12.ts │ ├── challenge-12.test.ts │ └── README.md ├── challenge-07 │ ├── challenge-07.ts │ ├── challenge-07.test.ts │ └── README.md ├── challenge-05 │ ├── challenge-05.ts │ ├── challenge-05.test.ts │ └── README.md ├── challenge-14 │ ├── challenge-14.ts │ ├── challenge-14.test.ts │ └── README.md ├── challenge-18 │ ├── challenge-18.ts │ ├── challenge-18.test.ts │ └── README.md ├── challenge-17 │ ├── challenge-17.ts │ └── challenge-17.test.ts ├── challenge-01 │ ├── challenge-01.ts │ ├── challenge-01.test.ts │ └── README.md ├── challenge-13 │ ├── challenge-13.ts │ ├── challenge-13.test.ts │ └── README.md ├── challenge-15 │ ├── challenge-15.ts │ ├── challenge-15.test.ts │ └── README.md ├── challenge-10 │ ├── challenge-10.ts │ ├── challenge-10.test.ts │ └── README.md ├── challenge-21 │ ├── challenge-21.ts │ └── challenge-21.test.ts ├── challenge-09 │ ├── challenge-09.ts │ ├── challenge-09.test.ts │ └── README.md ├── challenge-16 │ ├── challenge-16.ts │ ├── challenge-16.test.ts │ └── README.md ├── challenge-22 │ ├── challenge-22.ts │ ├── challenge-22.test.ts │ └── README.md ├── challenge-19 │ ├── challenge-19.ts │ └── challenge-19.test.ts ├── challenge-04 │ ├── challenge-04.ts │ ├── challenge-04.test.ts │ └── README.md ├── challenge-03 │ ├── challenge-03.ts │ ├── challenge-03.test.ts │ └── README.md ├── challenge-23 │ ├── challenge-23.ts │ ├── challenge-23.test.ts │ └── README.md ├── challenge-06 │ ├── challenge-06.test.ts │ ├── challenge-06.ts │ └── README.md ├── challenge-24 │ ├── challenge-24.ts │ ├── challenge-24.test.ts │ └── README.md └── challenge-25 │ ├── challenge-25.ts │ ├── challenge-25.test.ts │ └── README.md ├── 2022 ├── challenge-18 │ ├── challenge-18.ts │ ├── challenge-18.test.ts │ └── README.md ├── challenge-01 │ ├── challenge-01.ts │ ├── challenge-01.test.ts │ └── README.md ├── challenge-07 │ ├── challenge-07.ts │ ├── challenge-07.test.ts │ └── README.md ├── challenge-19 │ ├── challenge-19.ts │ ├── README.md │ └── challenge-19.test.ts ├── challenge-09 │ ├── challenge-09.ts │ ├── challenge-09.test.ts │ └── README.md ├── challenge-03 │ ├── challenge-03.ts │ ├── README.md │ └── challenge-03.test.ts ├── challenge-14 │ ├── challenge-14.ts │ ├── challenge-14.test.ts │ └── README.md ├── challenge-02 │ ├── challenge-02.ts │ ├── challenge-02.test.ts │ └── README.md ├── challenge-13 │ ├── challenge-13.ts │ ├── challenge-13.test.ts │ └── README.md ├── challenge-04 │ ├── challenge-04.ts │ └── challenge-04.test.ts ├── challenge-08 │ ├── challenge-08.ts │ ├── challenge-08.test.ts │ └── README.md ├── challenge-16 │ ├── challenge-16.ts │ ├── challenge-16.test.ts │ └── README.md ├── challenge-22 │ ├── challenge-22.ts │ ├── challenge-22.test.ts │ └── README.md ├── challenge-12 │ ├── challenge-12.ts │ ├── challenge-12.test.ts │ └── README.md ├── challenge-10 │ ├── challenge-10.ts │ ├── challenge-10.test.ts │ └── README.md ├── challenge-05 │ ├── challenge-05.ts │ ├── challenge-05.test.ts │ └── README.md ├── challenge-06 │ ├── challenge-06.ts │ ├── README.md │ └── challenge-06.test.ts ├── challenge-17 │ ├── challenge-17.ts │ ├── challenge-17.test.ts │ └── README.md ├── challenge-11 │ ├── challenge-11.ts │ ├── challenge-11.test.ts │ └── README.md ├── challenge-15 │ ├── challenge-15.ts │ ├── challenge-15.test.ts │ └── README.md ├── challenge-24 │ ├── challenge-24.ts │ └── README.md ├── challenge-23 │ ├── challenge-23.test.ts │ └── challenge-23.ts ├── challenge-21 │ ├── challenge-21.ts │ └── challenge-21.test.ts └── challenge-20 │ └── challenge-20.ts ├── 2023 ├── challenge-02 │ ├── challenge-02.ts │ ├── challenge-02.test.ts │ └── README.md ├── challenge-01 │ ├── challenge-01.ts │ ├── challenge-01.test.ts │ └── README.md ├── challenge-04 │ ├── challenge-04.ts │ ├── challenge-04.test.ts │ └── README.md ├── challenge-14 │ ├── challenge-14.ts │ ├── challenge-14.test.ts │ └── README.md ├── challenge-03 │ ├── challenge-03.ts │ ├── challenge-03.test.ts │ └── README.md ├── challenge-06 │ ├── challenge-06.ts │ ├── challenge-06.test.ts │ └── README.md ├── challenge-09 │ ├── challenge-09.ts │ ├── challenge-09.test.ts │ └── README.md ├── challenge-17 │ ├── challenge-17.ts │ ├── README.md │ └── challenge-17.test.ts ├── challenge-24 │ ├── challenge-24.ts │ └── README.md ├── challenge-08 │ ├── challenge-08.test.ts │ ├── challenge-08.ts │ └── README.md ├── challenge-10 │ ├── challenge-10.ts │ ├── challenge-10.test.ts │ └── README.md ├── challenge-22 │ ├── challenge-22.ts │ ├── challenge-22.test.ts │ └── README.md ├── challenge-05 │ ├── challenge-05.ts │ └── README.md ├── challenge-23 │ ├── challenge-23.ts │ └── README.md ├── challenge-13 │ ├── challenge-13.ts │ ├── challenge-13.test.ts │ └── README.md ├── challenge-11 │ ├── challenge-11.test.ts │ ├── challenge-11.ts │ └── README.md ├── challenge-21 │ ├── challenge-21.ts │ ├── challenge-21.test.ts │ └── README.md ├── challenge-19 │ ├── challenge-19.ts │ └── README.md ├── challenge-07 │ ├── README.md │ ├── challenge-07.ts │ └── challenge-07.test.ts ├── challenge-20 │ ├── challenge-20.ts │ └── challenge-20.test.ts ├── challenge-25 │ ├── challenge-25.ts │ ├── challenge-25.test.ts │ └── README.md ├── challenge-16 │ ├── challenge-16.ts │ └── README.md ├── challenge-12 │ ├── challenge-12.ts │ └── challenge-12.test.ts ├── challenge-15 │ ├── challenge-15.ts │ ├── challenge-15.test.ts │ └── README.md └── challenge-18 │ ├── challenge-18.ts │ └── README.md ├── .husky ├── pre-push └── pre-commit ├── vitest.config.ts ├── .gitignore ├── .eslintignore ├── .prettierignore ├── tsconfig.json ├── types.d.ts ├── .github └── workflows │ └── results.yml ├── vitest.setup.ts └── LICENSE /.husky/pre-push: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | . "$(dirname -- "$0")/_/husky.sh" 3 | 4 | npm run check 5 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | . "$(dirname -- "$0")/_/husky.sh" 3 | 4 | npx lint-staged 5 | -------------------------------------------------------------------------------- /vitest.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vitest/config' 2 | 3 | export default defineConfig({ 4 | test: { 5 | setupFiles: ['vitest.setup.ts'], 6 | }, 7 | }) 8 | -------------------------------------------------------------------------------- /2022/challenge-18/challenge-18.ts: -------------------------------------------------------------------------------- 1 | export function dryNumber(dry: number, numbers: number) { 2 | return [...Array(numbers)] 3 | .map((_, i) => i + 1) 4 | .filter(v => v.toString().includes(dry.toString())) 5 | } 6 | -------------------------------------------------------------------------------- /2022/challenge-01/challenge-01.ts: -------------------------------------------------------------------------------- 1 | // score: 131 2 | export function wrapping(gifts: string[]) { 3 | return gifts.map(g => { 4 | const wrapper = '*'.repeat(g.length + 2) 5 | return `${wrapper}\n*${g}*\n${wrapper}` 6 | }) 7 | } 8 | -------------------------------------------------------------------------------- /2023/challenge-02/challenge-02.ts: -------------------------------------------------------------------------------- 1 | export function manufacture(gifts: string[], materials: string) { 2 | return gifts.filter(gift => { 3 | const words = gift.split('') 4 | return words.every(word => materials.includes(word)) 5 | }) 6 | } 7 | -------------------------------------------------------------------------------- /2021/challenge-11/challenge-11.ts: -------------------------------------------------------------------------------- 1 | export function shouldBuyFidelity(times: number) { 2 | const PRICE = 12 3 | let total = 0 4 | 5 | for (let i = 1; i <= times; i++) total += PRICE * 0.75 ** i 6 | 7 | return times * PRICE > 250 + total 8 | } 9 | -------------------------------------------------------------------------------- /2023/challenge-01/challenge-01.ts: -------------------------------------------------------------------------------- 1 | export function findFirstRepeated(gifts: number[]) { 2 | const firstRepeatedGift = gifts.find((gift, index) => { 3 | return gifts.indexOf(gift) !== index 4 | }) 5 | 6 | return firstRepeatedGift ?? -1 7 | } 8 | -------------------------------------------------------------------------------- /2021/challenge-20/challenge-20.ts: -------------------------------------------------------------------------------- 1 | export function pangram(letter: string) { 2 | const alphabet = 'abcdefghijklmnñopqrstuvwxyz'.split('') 3 | const lowerLetter = letter.toLowerCase() 4 | return alphabet.every(letter => lowerLetter.includes(letter)) 5 | } 6 | -------------------------------------------------------------------------------- /2021/challenge-02/challenge-02.ts: -------------------------------------------------------------------------------- 1 | export function listGifts(letter: string) { 2 | return letter.split(' ').reduce>((acc, gift) => { 3 | if (gift && !gift.startsWith('_')) acc[gift] = acc[gift] + 1 || 1 4 | return acc 5 | }, {}) 6 | } 7 | -------------------------------------------------------------------------------- /2023/challenge-04/challenge-04.ts: -------------------------------------------------------------------------------- 1 | export function decode(message: string) { 2 | const regexp = /\(([^()]*)\)/g 3 | const replacer = (_: string, r: string) => [...r].reverse().join('') 4 | return message.replace(regexp, replacer).replace(regexp, replacer) 5 | } 6 | -------------------------------------------------------------------------------- /2022/challenge-07/challenge-07.ts: -------------------------------------------------------------------------------- 1 | // score: 400 2 | export function getGiftsToRefill(a1: string[], a2: string[], a3: string[]) { 3 | return [...new Set(a1.concat(a2, a3))].filter( 4 | g => (a1.includes(g) as any) + a2.includes(g) + a3.includes(g) === 1, 5 | ) 6 | } 7 | -------------------------------------------------------------------------------- /2022/challenge-19/challenge-19.ts: -------------------------------------------------------------------------------- 1 | export function sortToys(toys: string[], positions: number[]) { 2 | const min = Math.min(...positions) 3 | 4 | return toys.reduce((acc, toy, i) => { 5 | acc[positions[i] - min] = toy 6 | return acc 7 | }, []) 8 | } 9 | -------------------------------------------------------------------------------- /2021/challenge-08/challenge-08.ts: -------------------------------------------------------------------------------- 1 | export function maxProfit(prices: number[]) { 2 | return prices.reduce((maxProfit, price, i) => { 3 | const profit = Math.max(...prices.slice(i + 1)) - price 4 | return profit > 0 && profit > maxProfit ? profit : maxProfit 5 | }, -1) 6 | } 7 | -------------------------------------------------------------------------------- /2022/challenge-09/challenge-09.ts: -------------------------------------------------------------------------------- 1 | // score: 300 2 | export function countTime(leds: number[]) { 3 | const newLeds = leds.join('').split('1') 4 | newLeds[0] += newLeds.pop() 5 | 6 | return Math.max(...newLeds.map(led => led.length)) * 7 7 | } 8 | 9 | countTime([0, 0, 1, 0, 0]) 10 | -------------------------------------------------------------------------------- /2021/challenge-12/challenge-12.ts: -------------------------------------------------------------------------------- 1 | export function getMinJump(obstacles: number[]) { 2 | let minJump = 1 3 | 4 | for (let i = 1; i <= obstacles.length; i++) { 5 | if (obstacles.includes(minJump * i)) { 6 | minJump++ 7 | i = 0 8 | } 9 | } 10 | 11 | return minJump 12 | } 13 | -------------------------------------------------------------------------------- /2022/challenge-03/challenge-03.ts: -------------------------------------------------------------------------------- 1 | // score: 175 2 | export function distributeGifts(packOfGifts: string[], reindeers: string[]) { 3 | const reindeersWight = reindeers.join('').length * 2 4 | const packWeight = packOfGifts.join('').length 5 | 6 | return Math.floor(reindeersWight / packWeight) 7 | } 8 | -------------------------------------------------------------------------------- /2022/challenge-14/challenge-14.ts: -------------------------------------------------------------------------------- 1 | // score: 400 2 | export function getOptimalPath(path: number[][]) { 3 | return path.reduceRight((previous, current) => { 4 | return current.map((value, index) => { 5 | return value + Math.min(previous[index], previous[index + 1]) 6 | }) 7 | })[0] 8 | } 9 | -------------------------------------------------------------------------------- /2021/challenge-07/challenge-07.ts: -------------------------------------------------------------------------------- 1 | export function contains( 2 | store: string | Record, 3 | product: string, 4 | ): boolean { 5 | if (typeof store === 'object') { 6 | return Object.values(store).some(value => contains(value, product)) 7 | } 8 | 9 | return store === product 10 | } 11 | -------------------------------------------------------------------------------- /2021/challenge-05/challenge-05.ts: -------------------------------------------------------------------------------- 1 | export function daysToXmas(date: Date) { 2 | const getDays = (time: number) => Math.floor(time / 1000 / 60 / 60 / 24) 3 | 4 | const xmasDays = getDays(new Date('Dec 25, 2021').getTime()) 5 | const dateDays = getDays((date.setHours(0), date.getTime())) 6 | 7 | return xmasDays - dateDays 8 | } 9 | -------------------------------------------------------------------------------- /2022/challenge-02/challenge-02.ts: -------------------------------------------------------------------------------- 1 | // score: 121 2 | export function countHours(year: number, holidays: string[]) { 3 | const daysOfWeek = holidays.filter(holiday => { 4 | const isDayOfWeek = new Date(`${year}/${holiday}`).getDay() % 6 !== 0 5 | return isDayOfWeek 6 | }) 7 | 8 | return daysOfWeek.length * 2 9 | } 10 | -------------------------------------------------------------------------------- /2022/challenge-13/challenge-13.ts: -------------------------------------------------------------------------------- 1 | // score: 360 2 | export function getFilesToBackup( 3 | lastBackup: number, 4 | changes: Array<[number, number]>, 5 | ) { 6 | const ids = changes.filter(([, time]) => time > lastBackup).map(([id]) => id) 7 | const uniqueIds = [...new Set(ids)] 8 | 9 | return uniqueIds.sort((a, b) => a - b) 10 | } 11 | -------------------------------------------------------------------------------- /2021/challenge-14/challenge-14.ts: -------------------------------------------------------------------------------- 1 | export function missingReindeer(ids: number[]) { 2 | ids.sort((a, b) => a - b) 3 | 4 | let lostReindeer 5 | 6 | for (let i = 0; i < ids.length; i++) { 7 | if (!lostReindeer && ids[i] !== i) lostReindeer = i 8 | } 9 | 10 | return lostReindeer !== undefined ? lostReindeer : Math.max(...ids) + 1 11 | } 12 | -------------------------------------------------------------------------------- /2021/challenge-18/challenge-18.ts: -------------------------------------------------------------------------------- 1 | export function fixFiles(files: string[]) { 2 | const count: Record = {} 3 | 4 | return files.map(file => { 5 | const nameCount = count[file] 6 | let name = file 7 | 8 | if (nameCount) name += `(${nameCount})` 9 | count[file] = nameCount + 1 || 1 10 | 11 | return name 12 | }) 13 | } 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | coverage 12 | dist 13 | dist-ssr 14 | *.local 15 | 16 | # Editor directories and files 17 | .vscode/* 18 | !.vscode/extensions.json 19 | .idea 20 | .DS_Store 21 | *.suo 22 | *.ntvs* 23 | *.njsproj 24 | *.sln 25 | *.sw? 26 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | coverage 12 | dist 13 | dist-ssr 14 | *.local 15 | 16 | # Editor directories and files 17 | .vscode/* 18 | !.vscode/extensions.json 19 | .idea 20 | .DS_Store 21 | *.suo 22 | *.ntvs* 23 | *.njsproj 24 | *.sln 25 | *.sw? 26 | -------------------------------------------------------------------------------- /2021/challenge-17/challenge-17.ts: -------------------------------------------------------------------------------- 1 | export function countPackages(carriers: Carrier[], carrierID: string): number { 2 | const carrier = carriers.find(carrier => carrier[0] === carrierID) as Carrier 3 | 4 | return carrier[2].reduce( 5 | (acc, carrier) => (acc += countPackages(carriers, carrier)), 6 | carrier[1], 7 | ) 8 | } 9 | 10 | type Carrier = [string, number, string[]] 11 | -------------------------------------------------------------------------------- /2022/challenge-04/challenge-04.ts: -------------------------------------------------------------------------------- 1 | // score: 170 2 | export function fitsInOneBox(boxes: Box[]) { 3 | return boxes 4 | .sort((a, b) => a.l - b.l) 5 | .every((box, i) => { 6 | const next = boxes[i - 1] 7 | return i < 1 || (box.h > next.h && box.w > next.w) 8 | }) 9 | } 10 | 11 | export interface Box { 12 | w: number 13 | h: number 14 | l: number 15 | } 16 | -------------------------------------------------------------------------------- /2022/challenge-08/challenge-08.ts: -------------------------------------------------------------------------------- 1 | // score: 360 2 | export function checkPart(part: string) { 3 | return [...part.slice(0, part.length / 2)].every((word, i) => { 4 | const next = part[i + 1] 5 | const nextLeft = part[part.length - i - 1] 6 | const nextRight = part[part.length - i - 2] 7 | 8 | return word === nextLeft || word === nextRight || next === nextLeft 9 | }) 10 | } 11 | -------------------------------------------------------------------------------- /2023/challenge-14/challenge-14.ts: -------------------------------------------------------------------------------- 1 | export function maxGifts(houses: number[]) { 2 | const dp = [] 3 | 4 | dp[0] = houses[0] 5 | dp[1] = Math.max(houses[0], houses[1]) 6 | 7 | let index = 2 8 | 9 | for (const gifts of houses.slice(2)) { 10 | dp[index] = Math.max(dp[index - 1], dp[index - 2] + gifts) 11 | 12 | index++ 13 | } 14 | 15 | return dp[houses.length - 1] 16 | } 17 | -------------------------------------------------------------------------------- /2022/challenge-16/challenge-16.ts: -------------------------------------------------------------------------------- 1 | // score: 300 2 | export function fixLetter(letter: string) { 3 | return letter 4 | .trim() 5 | .replace(/([.,!])(.*)/g, '$1 $2') 6 | .replace(/([\s.,!?])(?=\1)/g, '') 7 | .replace(/\s+([.,!?])/g, '$1') 8 | .replace(/\b([.?!] \w)|(^\w)/g, str => str.toUpperCase()) 9 | .replace(/^.*\w$/, str => `${str}.`) 10 | .replace(/santa claus/gi, 'Santa Claus') 11 | } 12 | -------------------------------------------------------------------------------- /2021/challenge-01/challenge-01.ts: -------------------------------------------------------------------------------- 1 | export function contarOvejas(ovejas: Oveja[]) { 2 | return ovejas.filter(oveja => { 3 | const name = oveja.name.toLowerCase() 4 | 5 | const isColorRed = oveja.color === 'rojo' 6 | const hasAandN = name.includes('a') && name.includes('n') 7 | 8 | return isColorRed && hasAandN 9 | }) 10 | } 11 | 12 | export interface Oveja { 13 | color: string 14 | name: string 15 | } 16 | -------------------------------------------------------------------------------- /2022/challenge-22/challenge-22.ts: -------------------------------------------------------------------------------- 1 | export function checkStepNumbers(systemNames: string[], stepNumbers: number[]) { 2 | const bag: Record = {} 3 | 4 | return systemNames.every((systemName, i) => { 5 | const number = stepNumbers[i] 6 | const prevNumber = bag[systemName] || number 7 | const valid = prevNumber <= number 8 | bag[systemName] = number 9 | 10 | return valid 11 | }) 12 | } 13 | -------------------------------------------------------------------------------- /2021/challenge-13/challenge-13.ts: -------------------------------------------------------------------------------- 1 | export function wrapGifts(gifts: string[]) { 2 | const wrappedGiftsLength = gifts.map(gift => `*${gift}*`) 3 | 4 | const giftsLength = Array.from(wrappedGiftsLength[0]).length 5 | 6 | const topBottom = '*'.repeat(giftsLength + (giftsLength % 2 === 0 ? 2 : 1)) 7 | 8 | wrappedGiftsLength.unshift(topBottom) 9 | wrappedGiftsLength.push(topBottom) 10 | 11 | return wrappedGiftsLength 12 | } 13 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | coverage 12 | dist 13 | dist-ssr 14 | *.local 15 | package-lock.json 16 | pnpm-lock.yaml 17 | yarn.lock 18 | 19 | # Editor directories and files 20 | .vscode/* 21 | !.vscode/extensions.json 22 | .idea 23 | .DS_Store 24 | *.suo 25 | *.ntvs* 26 | *.njsproj 27 | *.sln 28 | *.sw? 29 | -------------------------------------------------------------------------------- /2021/challenge-15/challenge-15.ts: -------------------------------------------------------------------------------- 1 | export function checkSledJump(heights: number[]) { 2 | const maxIndex = heights.indexOf(Math.max(...heights)) 3 | const before = heights.slice(0, maxIndex) 4 | const after = heights.slice(maxIndex + 1, heights.length) 5 | 6 | return ( 7 | after.length > 0 && 8 | maxIndex > 0 && 9 | after.slice(1).every((h, i) => h < after[i]) && 10 | before.slice(1).every((h, i) => h > before[i]) 11 | ) 12 | } 13 | -------------------------------------------------------------------------------- /2021/challenge-11/challenge-11.test.ts: -------------------------------------------------------------------------------- 1 | import { describe } from 'vitest' 2 | import { shouldBuyFidelity } from './challenge-11' 3 | 4 | const TEST_CASES: TestCases = [ 5 | { args: 1, expected: false }, 6 | { args: 100, expected: true }, 7 | ] 8 | 9 | describe('Challenge #11: ¿Vale la pena la tarjeta fidelidad del cine?', () => { 10 | buildChallengeTestCases({ 11 | cases: TEST_CASES, 12 | fn: shouldBuyFidelity, 13 | }) 14 | }) 15 | -------------------------------------------------------------------------------- /2022/challenge-12/challenge-12.ts: -------------------------------------------------------------------------------- 1 | // score: 320 2 | export function selectSleigh(distance: number, sleighs: Sleight[]) { 3 | let maxW = 20 4 | return sleighs.reduce((acc, s) => { 5 | if (s.consumption * distance > maxW) { 6 | maxW = s.consumption * distance 7 | return acc 8 | } 9 | 10 | return s.name 11 | }, null) 12 | } 13 | 14 | export interface Sleight { 15 | name: string 16 | consumption: number 17 | } 18 | -------------------------------------------------------------------------------- /2022/challenge-10/challenge-10.ts: -------------------------------------------------------------------------------- 1 | // score: 260 2 | export function checkJump(heights: number[]) { 3 | const maxIndex = heights.indexOf(Math.max(...heights)) 4 | const before = heights.slice(0, maxIndex) 5 | const after = heights.slice(maxIndex + 1, heights.length) 6 | return ( 7 | after.length > 0 && 8 | maxIndex > 0 && 9 | after.slice(1).every((h, i) => h <= after[i]) && 10 | before.slice(1).every((h, i) => h >= before[i]) 11 | ) 12 | } 13 | -------------------------------------------------------------------------------- /2021/challenge-10/challenge-10.ts: -------------------------------------------------------------------------------- 1 | export function getCoins(change: number) { 2 | const coins = [50, 20, 10, 5, 2, 1] 3 | 4 | return coins 5 | .reduce( 6 | (acc, coin, i) => { 7 | if (change >= coin) { 8 | const nCoins = Math.floor(change / coin) 9 | acc[i] = nCoins 10 | change -= coin * nCoins 11 | } 12 | 13 | return acc 14 | }, 15 | [0, 0, 0, 0, 0, 0], 16 | ) 17 | .reverse() 18 | } 19 | -------------------------------------------------------------------------------- /2022/challenge-05/challenge-05.ts: -------------------------------------------------------------------------------- 1 | // score: 217 2 | export function getMaxGifts( 3 | giftsCities: number[], 4 | maxGifts: number, 5 | maxCities: number, 6 | ) { 7 | return giftsCities 8 | .sort((a, b) => b - a) 9 | .reduce((acc, gift) => { 10 | const total = acc + gift 11 | if (maxCities === 0 || total > maxGifts || total === maxGifts - 1) { 12 | return acc 13 | } 14 | 15 | maxCities-- 16 | return total 17 | }, 0) 18 | } 19 | -------------------------------------------------------------------------------- /2021/challenge-21/challenge-21.ts: -------------------------------------------------------------------------------- 1 | export function canCarry(capacity: number, trip: number[][]) { 2 | const path: number[] = [] 3 | const sum = (arr: number[]) => arr.reduce((acc, n) => acc + n, 0) 4 | 5 | return trip.every(([gifts, point, place]) => { 6 | path[point] = gifts + (path[point] || 0) 7 | path[place] = -gifts 8 | 9 | const prevPointGifts = path.slice(0, point + 1) 10 | 11 | return gifts <= capacity && sum(prevPointGifts) <= capacity 12 | }) 13 | } 14 | -------------------------------------------------------------------------------- /2023/challenge-03/challenge-03.ts: -------------------------------------------------------------------------------- 1 | export function findNaughtyStep(original: string, modified: string) { 2 | const originalLen = original.length 3 | const modifiedLen = modified.length 4 | 5 | const sequences = { 6 | [originalLen]: [original, modified], 7 | [modifiedLen]: [modified, original], 8 | } 9 | 10 | const [steps, reference] = sequences[Math.max(originalLen, modifiedLen)] 11 | 12 | return [...steps].find((step, index) => step !== reference[index]) ?? '' 13 | } 14 | -------------------------------------------------------------------------------- /2021/challenge-09/challenge-09.ts: -------------------------------------------------------------------------------- 1 | export function groupBy( 2 | collection: T[], 3 | it: keyof T | ((value: T) => string | number), 4 | ): Record { 5 | const group: Record = {} 6 | const isItFunction = typeof it === 'function' 7 | 8 | for (const value of collection) { 9 | const key = isItFunction ? it(value) : (value[it] as string) 10 | group[key] ??= [] 11 | group[key].push(value) 12 | } 13 | 14 | return group 15 | } 16 | -------------------------------------------------------------------------------- /2021/challenge-16/challenge-16.ts: -------------------------------------------------------------------------------- 1 | export function decodeNumber(symbols: string) { 2 | const symbolValues: Record = { 3 | '.': 1, 4 | ',': 5, 5 | ':': 10, 6 | ';': 50, 7 | '!': 100, 8 | } 9 | 10 | return [...symbols].reduce((total, symbol, i) => { 11 | const value = symbolValues[symbol] 12 | const nextValue = symbolValues[symbols[i + 1]] 13 | total += (nextValue > value ? -value : value) || NaN 14 | 15 | return total 16 | }, 0) 17 | } 18 | -------------------------------------------------------------------------------- /2023/challenge-06/challenge-06.ts: -------------------------------------------------------------------------------- 1 | export function maxDistance(movements: string) { 2 | let maxLeft = 0 3 | let maxRight = 0 4 | 5 | for (const movement of movements) { 6 | const rightPoint = +(movement === '>') 7 | const leftPoint = +(movement === '<') 8 | const extraPoint = +(movement === '*') 9 | 10 | maxRight += rightPoint - leftPoint + extraPoint 11 | maxLeft += leftPoint - rightPoint + extraPoint 12 | } 13 | 14 | return Math.max(maxLeft, maxRight) 15 | } 16 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "useDefineForClassFields": true, 5 | "module": "ESNext", 6 | "moduleResolution": "Node", 7 | "strict": true, 8 | "resolveJsonModule": true, 9 | "isolatedModules": true, 10 | "esModuleInterop": true, 11 | "noEmit": true, 12 | "noUnusedLocals": true, 13 | "noUnusedParameters": true, 14 | "noImplicitReturns": true, 15 | "skipLibCheck": true, 16 | "baseUrl": "." 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /2021/challenge-22/challenge-22.ts: -------------------------------------------------------------------------------- 1 | export function countDecorations(bigTree: Tree): number { 2 | return Object.values(bigTree).reduce((acc, value) => { 3 | if (typeof value === 'number') { 4 | acc += value 5 | } 6 | 7 | if (typeof value === 'object' && value !== null) { 8 | acc += countDecorations(value) 9 | } 10 | 11 | return acc 12 | }, 0) 13 | } 14 | 15 | export interface Tree { 16 | value: number 17 | left: Tree | null 18 | right: Tree | null 19 | } 20 | -------------------------------------------------------------------------------- /2023/challenge-09/challenge-09.ts: -------------------------------------------------------------------------------- 1 | export function adjustLights(lights: string[]) { 2 | const start = lights[0] 3 | const colors = ['🟢', '🔴'] 4 | const isStartColorGreen = lights[0] === '🟢' 5 | const next = colors[+isStartColorGreen] 6 | 7 | let count = 0 8 | let index = 0 9 | 10 | for (const light of lights) { 11 | const values = [light !== start, light !== next] 12 | 13 | count += +values[index++ % 2] 14 | } 15 | 16 | return Math.min(lights.length - count, count) 17 | } 18 | -------------------------------------------------------------------------------- /2021/challenge-19/challenge-19.ts: -------------------------------------------------------------------------------- 1 | export function learn(time: number, courses: number[]) { 2 | let coursesSum = 0 3 | let takenCourses: number[] = [] 4 | for (let i = 0; i < courses.length; i++) { 5 | for (let j = i + 1; j < courses.length; j++) { 6 | const sum = courses[i] + courses[j] 7 | if (sum <= time && coursesSum < sum) { 8 | coursesSum = sum 9 | takenCourses = [i, j] 10 | } 11 | } 12 | } 13 | return takenCourses.length ? takenCourses : null 14 | } 15 | -------------------------------------------------------------------------------- /2021/challenge-04/challenge-04.ts: -------------------------------------------------------------------------------- 1 | export function createXmasTree(height: number) { 2 | const leaves = [...Array(height)] 3 | .map((_, i) => 4 | '*' 5 | .repeat(2 * i + 1) 6 | .padStart(i + height, '_') 7 | .padEnd(height * 2 - 1, '_') 8 | .concat('\n'), 9 | ) 10 | .join('') 11 | 12 | const base = '#' 13 | .padStart(height, '_') 14 | .padEnd(height * 2 - 1, '_') 15 | .concat('\n') 16 | .repeat(2) 17 | 18 | return leaves.concat(base).trimEnd() 19 | } 20 | -------------------------------------------------------------------------------- /2022/challenge-06/challenge-06.ts: -------------------------------------------------------------------------------- 1 | // score: 200 2 | export function createCube(size: number) { 3 | const cube: string[] = [] 4 | const topRight = '_\\'.repeat(size) 5 | const bottomRight = '_/'.repeat(size) 6 | 7 | for (let i = 0; i < size; i++) { 8 | const space = ' '.repeat(i) 9 | const borderSize = size - i 10 | 11 | cube.unshift(`${space}${'/\\'.repeat(borderSize)}${topRight}`) 12 | cube.push(`${space}${'\\/'.repeat(borderSize)}${bottomRight}`) 13 | } 14 | 15 | return cube.join('\n') 16 | } 17 | -------------------------------------------------------------------------------- /2022/challenge-17/challenge-17.ts: -------------------------------------------------------------------------------- 1 | export function carryGifts(gifts: string[], maxWeight: number) { 2 | return gifts 3 | .filter(g => g.length <= maxWeight) 4 | .reduce((bags, bag) => { 5 | const lastBag = bags.at(-1) 6 | const lastBagLength = (lastBag && lastBag.replace(/\s/g, '').length) || 0 7 | 8 | if (bags.length > 0 && lastBagLength + bag.length <= maxWeight) { 9 | bags[bags.length - 1] += ` ${bag}` 10 | } else bags.push(bag) 11 | 12 | return bags 13 | }, []) 14 | } 15 | -------------------------------------------------------------------------------- /2022/challenge-11/challenge-11.ts: -------------------------------------------------------------------------------- 1 | // score: 360 2 | export function getCompleted(part: string, total: string) { 3 | const [h1, m1, s1] = part.split(':') 4 | const [h2, m2, s2] = total.split(':') 5 | 6 | const partSeconds = (+h1 * 60 + +m1) * 60 + +s1 7 | const totalSeconds = (+h2 * 60 + +m2) * 60 + +s2 8 | 9 | let gcd = partSeconds 10 | let b = totalSeconds 11 | 12 | while (b) { 13 | const aux = b 14 | b = gcd % b 15 | gcd = aux 16 | } 17 | 18 | return `${partSeconds / gcd}/${totalSeconds / gcd}` 19 | } 20 | -------------------------------------------------------------------------------- /2023/challenge-06/challenge-06.test.ts: -------------------------------------------------------------------------------- 1 | import { describe } from 'vitest' 2 | import { maxDistance } from './challenge-06' 3 | 4 | const TEST_CASES: TestCases = [ 5 | { args: '>>*<', expected: 2 }, 6 | { args: '<<<<<', expected: 5 }, 7 | { args: '>***>', expected: 5 }, 8 | { args: '**********', expected: 10 }, 9 | { args: '<<**>>', expected: 2 }, 10 | ] 11 | 12 | describe('Challenge #6: The reindeer on trial', () => { 13 | buildChallengeTestCases({ 14 | cases: TEST_CASES, 15 | fn: maxDistance, 16 | }) 17 | }) 18 | -------------------------------------------------------------------------------- /2022/challenge-08/challenge-08.test.ts: -------------------------------------------------------------------------------- 1 | import { describe } from 'vitest' 2 | import { checkPart } from './challenge-08' 3 | 4 | const TEST_CASES: TestCases = [ 5 | { args: 'uwu', expected: true }, 6 | { args: 'will', expected: false }, 7 | { args: 'lolol', expected: true }, 8 | { args: 'yolooloy', expected: true }, 9 | { args: 'zzzoonzzz', expected: true }, 10 | ] 11 | 12 | describe('Challenge #8: We need a mechanic!', () => { 13 | buildChallengeTestCases({ 14 | cases: TEST_CASES, 15 | fn: checkPart, 16 | }) 17 | }) 18 | -------------------------------------------------------------------------------- /types.d.ts: -------------------------------------------------------------------------------- 1 | declare global { 2 | interface TestCase { 3 | args: Args 4 | expected: Expected 5 | } 6 | 7 | interface BuildChallengeTestOptions { 8 | cases: TestCases 9 | fn?: (args: Args) => Expected 10 | spreadFn?: (...args: Args) => Expected 11 | } 12 | 13 | type TestCases = Array> 14 | 15 | function buildChallengeTestCases( 16 | options: BuildChallengeTestOptions, 17 | ): void 18 | } 19 | 20 | export {} 21 | -------------------------------------------------------------------------------- /2021/challenge-02/challenge-02.test.ts: -------------------------------------------------------------------------------- 1 | import { describe } from 'vitest' 2 | import { listGifts } from './challenge-02' 3 | 4 | const TEST_CASES: TestCases> = [ 5 | { 6 | args: 'bici coche balón _playstation bici coche peluche', 7 | expected: { 8 | bici: 2, 9 | coche: 2, 10 | balón: 1, 11 | peluche: 1, 12 | }, 13 | }, 14 | ] 15 | 16 | describe('Challenge #2: ¡Ayuda al elfo a listar los regalos!', () => { 17 | buildChallengeTestCases({ 18 | cases: TEST_CASES, 19 | fn: listGifts, 20 | }) 21 | }) 22 | -------------------------------------------------------------------------------- /2021/challenge-08/challenge-08.test.ts: -------------------------------------------------------------------------------- 1 | import { describe } from 'vitest' 2 | import { maxProfit } from './challenge-08' 3 | 4 | const TEST_CASES: TestCases = [ 5 | { args: [39, 18, 29, 25, 34, 32, 5], expected: 16 }, 6 | { args: [10, 20, 30, 40, 50, 60, 70], expected: 60 }, 7 | { args: [18, 15, 12, 11, 9, 7], expected: -1 }, 8 | { args: [3, 3, 3, 3, 3], expected: -1 }, 9 | ] 10 | 11 | describe('Challenge #8: La locura de las criptomonedas', () => { 12 | buildChallengeTestCases({ 13 | cases: TEST_CASES, 14 | fn: maxProfit, 15 | }) 16 | }) 17 | -------------------------------------------------------------------------------- /2021/challenge-03/challenge-03.ts: -------------------------------------------------------------------------------- 1 | export function isValid(letter: string) { 2 | for (const str of ['{', '}', '[', ']']) if (letter.includes(str)) return false 3 | 4 | const containers: Record = {} 5 | const stack: number[] = [] 6 | 7 | letter.split('').forEach((char, i) => { 8 | if (char === ')') containers[i] = stack.pop() as number 9 | if (char === '(') stack.push(i) 10 | }) 11 | 12 | for (const value in containers) { 13 | if (!letter.substring(containers[value] + 1, +value)) return false 14 | } 15 | 16 | return !stack.length 17 | } 18 | -------------------------------------------------------------------------------- /2021/challenge-12/challenge-12.test.ts: -------------------------------------------------------------------------------- 1 | import { describe } from 'vitest' 2 | import { getMinJump } from './challenge-12' 3 | 4 | const TEST_CASES: TestCases = [ 5 | { args: [5, 3, 6, 7, 9], expected: 4 }, 6 | { args: [2, 4, 6, 8, 10], expected: 7 }, 7 | { args: [1, 2, 3, 5], expected: 4 }, 8 | { args: [3, 7, 5], expected: 2 }, 9 | { args: [9, 5, 1], expected: 2 }, 10 | ] 11 | 12 | describe('Challenge #12: La ruta perfecta para dejar los regalos', () => { 13 | buildChallengeTestCases({ 14 | cases: TEST_CASES, 15 | fn: getMinJump, 16 | }) 17 | }) 18 | -------------------------------------------------------------------------------- /2021/challenge-23/challenge-23.ts: -------------------------------------------------------------------------------- 1 | export function canReconfigure(from: string, to: string) { 2 | if (from.length !== to.length) return false 3 | const store: Record = {} 4 | 5 | for (const [i, fromLetter] of [...from].entries()) { 6 | const toLetter = to[i] 7 | 8 | const isFromInvalid = store[toLetter] && store[toLetter] !== fromLetter 9 | const isToInvalid = store[fromLetter] && store[fromLetter] !== toLetter 10 | 11 | if (isFromInvalid || isToInvalid) return false 12 | 13 | store[toLetter] = fromLetter 14 | } 15 | return true 16 | } 17 | -------------------------------------------------------------------------------- /2021/challenge-10/challenge-10.test.ts: -------------------------------------------------------------------------------- 1 | import { describe } from 'vitest' 2 | import { getCoins } from './challenge-10' 3 | 4 | const TEST_CASES: TestCases = [ 5 | { args: 51, expected: [1, 0, 0, 0, 0, 1] }, 6 | { args: 3, expected: [1, 1, 0, 0, 0, 0] }, 7 | { args: 5, expected: [0, 0, 1, 0, 0, 0] }, 8 | { args: 16, expected: [1, 0, 1, 1, 0, 0] }, 9 | { args: 100, expected: [0, 0, 0, 0, 0, 2] }, 10 | ] 11 | 12 | describe('Challenge #10: La máquina de cambio', () => { 13 | buildChallengeTestCases({ 14 | cases: TEST_CASES, 15 | fn: getCoins, 16 | }) 17 | }) 18 | -------------------------------------------------------------------------------- /2023/challenge-17/challenge-17.ts: -------------------------------------------------------------------------------- 1 | export function optimizeIntervals(intervals: Array<[number, number]>) { 2 | intervals.sort((a, b) => a[0] - b[0]) 3 | 4 | const result = [intervals[0]] 5 | 6 | for (const currentInterval of intervals) { 7 | const lastMergedInterval = result[result.length - 1] 8 | 9 | if (currentInterval[0] <= lastMergedInterval[1]) { 10 | lastMergedInterval[1] = Math.max( 11 | lastMergedInterval[1], 12 | currentInterval[1], 13 | ) 14 | } else { 15 | result.push(currentInterval) 16 | } 17 | } 18 | 19 | return result 20 | } 21 | -------------------------------------------------------------------------------- /2023/challenge-24/challenge-24.ts: -------------------------------------------------------------------------------- 1 | export function getStaircasePaths(steps: number, maxJump: number) { 2 | function generatePaths(remainingSteps: number, path: number[]): number[][] { 3 | const result = [] 4 | 5 | if (remainingSteps === 0) { 6 | result.push(path) 7 | } 8 | 9 | for (let jump = 1; jump <= Math.min(remainingSteps, maxJump); jump++) { 10 | result.push(...generatePaths(remainingSteps - jump, [...path, jump])) 11 | } 12 | 13 | return result 14 | } 15 | 16 | const staircasePaths = generatePaths(steps, []) 17 | 18 | return staircasePaths 19 | } 20 | -------------------------------------------------------------------------------- /2021/challenge-13/challenge-13.test.ts: -------------------------------------------------------------------------------- 1 | import { describe } from 'vitest' 2 | import { wrapGifts } from './challenge-13' 3 | 4 | const TEST_CASES: TestCases = [ 5 | { args: ['📷', '⚽️'], expected: ['****', '*📷*', '*⚽️*', '****'] }, 6 | { 7 | args: ['🏈🎸', '🎮🧸'], 8 | expected: ['******', '*🏈🎸*', '*🎮🧸*', '******'], 9 | }, 10 | { args: ['📷'], expected: ['****', '*📷*', '****'] }, 11 | ] 12 | 13 | describe('Challenge #13: Envuelve regalos con asteriscos', () => { 14 | buildChallengeTestCases({ 15 | cases: TEST_CASES, 16 | fn: wrapGifts, 17 | }) 18 | }) 19 | -------------------------------------------------------------------------------- /2021/challenge-23/challenge-23.test.ts: -------------------------------------------------------------------------------- 1 | import { describe } from 'vitest' 2 | import { canReconfigure } from './challenge-23' 3 | 4 | const TEST_CASES: TestCases<[string, string], boolean> = [ 5 | { args: ['BAL', 'LIB'], expected: true }, 6 | { args: ['CON', 'JUU'], expected: false }, 7 | { args: ['MMM', 'MID'], expected: false }, 8 | { args: ['BR', 'BAD'], expected: false }, 9 | ] 10 | 11 | describe('Challenge #23: ¿Puedes reconfigurar las fábricas para no parar de crear regalos?', () => { 12 | buildChallengeTestCases({ 13 | cases: TEST_CASES, 14 | spreadFn: canReconfigure, 15 | }) 16 | }) 17 | -------------------------------------------------------------------------------- /2022/challenge-09/challenge-09.test.ts: -------------------------------------------------------------------------------- 1 | import { describe } from 'vitest' 2 | import { countTime } from './challenge-09' 3 | 4 | const TEST_CASES: TestCases = [ 5 | { args: [0, 1, 1, 0, 1], expected: 7 }, 6 | { args: [0, 0, 0, 1], expected: 21 }, 7 | { args: [0, 0, 1, 0, 0], expected: 28 }, 8 | { args: [1, 0, 0, 1, 0, 0], expected: 14 }, 9 | { args: [1, 1, 0, 0, 0, 0], expected: 28 }, 10 | { args: [1, 1, 1], expected: 0 }, 11 | ] 12 | 13 | describe('Challenge #9: Crazy Xmas lights', () => { 14 | buildChallengeTestCases({ 15 | cases: TEST_CASES, 16 | fn: countTime, 17 | }) 18 | }) 19 | -------------------------------------------------------------------------------- /2021/challenge-14/challenge-14.test.ts: -------------------------------------------------------------------------------- 1 | import { describe } from 'vitest' 2 | import { missingReindeer } from './challenge-14' 3 | 4 | const TEST_CASES: TestCases = [ 5 | { args: [0, 2, 3], expected: 1 }, 6 | { args: [5, 6, 1, 2, 3, 7, 0], expected: 4 }, 7 | { args: [0, 1], expected: 2 }, 8 | { args: [3, 0, 1], expected: 2 }, 9 | { args: [9, 2, 3, 5, 6, 4, 7, 0, 1], expected: 8 }, 10 | { args: [0], expected: 1 }, 11 | ] 12 | 13 | describe('Challenge #14: En busca del reno perdido', () => { 14 | buildChallengeTestCases({ 15 | cases: TEST_CASES, 16 | fn: missingReindeer, 17 | }) 18 | }) 19 | -------------------------------------------------------------------------------- /2023/challenge-08/challenge-08.test.ts: -------------------------------------------------------------------------------- 1 | import { describe } from 'vitest' 2 | import { organizeGifts } from './challenge-08' 3 | 4 | const TEST_CASES: TestCases = [ 5 | { args: '76a11b', expected: '[a]{a}{a}(aaaaaa){b}(b)' }, 6 | { args: '20a', expected: '{a}{a}' }, 7 | { args: '70b120a4c', expected: '[b]{b}{b}[a][a]{a}{a}(cccc)' }, 8 | { args: '9c', expected: '(ccccccccc)' }, 9 | { args: '19d51e', expected: '{d}(ddddddddd)[e](e)' }, 10 | ] 11 | 12 | describe('Challenge #8: Sorting the warehouse', () => { 13 | buildChallengeTestCases({ 14 | cases: TEST_CASES, 15 | fn: organizeGifts, 16 | }) 17 | }) 18 | -------------------------------------------------------------------------------- /2021/challenge-06/challenge-06.test.ts: -------------------------------------------------------------------------------- 1 | import { describe } from 'vitest' 2 | import { sumPairs } from './challenge-06' 3 | 4 | const TEST_CASES: TestCases<[number[], number], number[] | null> = [ 5 | { args: [[3, 5, 7, 2], 10], expected: [3, 7] }, 6 | { args: [[-3, -2, 7, -5], 10], expected: null }, 7 | { args: [[2, 2, 3, 1], 4], expected: [2, 2] }, 8 | { args: [[6, 7, 1, 2], 8], expected: [6, 2] }, 9 | { args: [[0, 2, 2, 3, -1, 1, 5], 6], expected: [1, 5] }, 10 | ] 11 | 12 | describe('Challenge #6: Rematando los exámenes finales', () => { 13 | buildChallengeTestCases({ 14 | cases: TEST_CASES, 15 | spreadFn: sumPairs, 16 | }) 17 | }) 18 | -------------------------------------------------------------------------------- /2021/challenge-04/challenge-04.test.ts: -------------------------------------------------------------------------------- 1 | import { describe } from 'vitest' 2 | import { createXmasTree } from './challenge-04' 3 | 4 | const TEST_CASES: TestCases = [ 5 | { 6 | args: 5, 7 | expected: ` 8 | ____*____ 9 | ___***___ 10 | __*****__ 11 | _*******_ 12 | ********* 13 | ____#____ 14 | ____#____`.trimStart(), 15 | }, 16 | { 17 | args: 3, 18 | expected: ` 19 | __*__ 20 | _***_ 21 | ***** 22 | __#__ 23 | __#__`.trimStart(), 24 | }, 25 | ] 26 | 27 | describe('Challenge #4: ¡Es hora de poner la navidad en casa!', () => { 28 | buildChallengeTestCases({ 29 | cases: TEST_CASES, 30 | fn: createXmasTree, 31 | }) 32 | }) 33 | -------------------------------------------------------------------------------- /2021/challenge-24/challenge-24.ts: -------------------------------------------------------------------------------- 1 | import type { Tree } from '../challenge-22/challenge-22' 2 | 3 | export function checkIsSameTree(treeA: Tree, treeB: Tree) { 4 | const countDecorations = (bigTree: Tree) => 5 | Object.values(bigTree).reduce((acc, value) => { 6 | if (typeof value === 'number') { 7 | acc += value 8 | } 9 | if (typeof value === 'object' && value !== null) { 10 | acc += countDecorations(value) 11 | } 12 | return acc 13 | }, 0) 14 | 15 | const total = countDecorations(treeA) 16 | const total2 = countDecorations(treeB) 17 | return total === total2 18 | } 19 | 20 | export type { Tree } 21 | -------------------------------------------------------------------------------- /2023/challenge-10/challenge-10.ts: -------------------------------------------------------------------------------- 1 | export function createChristmasTree(ornaments: string, height: number) { 2 | let result = '' 3 | const spaces = ' '.repeat(height - 1) 4 | 5 | const ornamentsArray = [...ornaments.repeat(height)].join(' ') 6 | let currentPosition = 0 7 | 8 | for (const index of new Array(height).keys()) { 9 | const length = index + 1 10 | const total = currentPosition + length 11 | 12 | result += 13 | spaces.slice(index) + 14 | ornamentsArray.slice(currentPosition * 2, total * 2 - 1) + 15 | '\n' 16 | 17 | currentPosition = total % ornaments.length 18 | } 19 | 20 | return result + spaces + '|\n' 21 | } 22 | -------------------------------------------------------------------------------- /2023/challenge-03/challenge-03.test.ts: -------------------------------------------------------------------------------- 1 | import { describe } from 'vitest' 2 | import { findNaughtyStep } from './challenge-03' 3 | 4 | const TEST_CASES: TestCases<[string, string], string> = [ 5 | { args: ['abcd', 'abcde'], expected: 'e' }, 6 | { args: ['abcde', 'abcd'], expected: 'e' }, 7 | { args: ['xxxx', 'xxoxx'], expected: 'o' }, 8 | { args: ['stepfor', 'stepor'], expected: 'f' }, 9 | { args: ['iiiii', 'iiiii'], expected: '' }, 10 | { args: ['iiii', 'iiiii'], expected: 'i' }, 11 | ] 12 | 13 | describe('Challenge #3: The naughty elf', () => { 14 | buildChallengeTestCases({ 15 | cases: TEST_CASES, 16 | spreadFn: findNaughtyStep, 17 | }) 18 | }) 19 | -------------------------------------------------------------------------------- /2023/challenge-04/challenge-04.test.ts: -------------------------------------------------------------------------------- 1 | import { describe } from 'vitest' 2 | import { decode } from './challenge-04' 3 | 4 | const TEST_CASES: TestCases = [ 5 | { args: 'hola (odnum)', expected: 'hola mundo' }, 6 | { args: '(olleh) (dlrow)!', expected: 'hello world!' }, 7 | { args: 'sa(u(cla)atn)s', expected: 'santaclaus' }, 8 | { args: '((nta)(sa))', expected: 'santa' }, 9 | { args: '(ab(dc)(fe))', expected: 'fedcba' }, 10 | { args: 'ab(cd)ef', expected: 'abdcef' }, 11 | ] 12 | 13 | describe('Challenge #4: Turn the parentheses around', () => { 14 | buildChallengeTestCases({ 15 | cases: TEST_CASES, 16 | fn: decode, 17 | }) 18 | }) 19 | -------------------------------------------------------------------------------- /2021/challenge-15/challenge-15.test.ts: -------------------------------------------------------------------------------- 1 | import { describe } from 'vitest' 2 | import { checkSledJump } from './challenge-15' 3 | 4 | const TEST_CASES: TestCases = [ 5 | { args: [1, 2, 3, 2, 1], expected: true }, 6 | { args: [0, 1, 0], expected: true }, 7 | { args: [0, 3, 2, 1], expected: true }, 8 | { args: [0, 1000, 1], expected: true }, 9 | { args: [2, 4, 4, 6, 2], expected: false }, 10 | { args: [1, 2, 3], expected: false }, 11 | { args: [1, 2, 3, 2, 1, 2, 3], expected: false }, 12 | ] 13 | 14 | describe('Challenge #15: La máquina de cambio', () => { 15 | buildChallengeTestCases({ 16 | cases: TEST_CASES, 17 | fn: checkSledJump, 18 | }) 19 | }) 20 | -------------------------------------------------------------------------------- /2022/challenge-14/challenge-14.test.ts: -------------------------------------------------------------------------------- 1 | import { describe } from 'vitest' 2 | import { getOptimalPath } from './challenge-14' 3 | 4 | const TEST_CASES: TestCases = [ 5 | { args: [[0], [7, 4], [2, 4, 6]], expected: 8 }, 6 | { args: [[0], [2, 3]], expected: 2 }, 7 | { args: [[0], [3, 4], [9, 8, 1]], expected: 5 }, 8 | { args: [[1], [1, 5], [7, 5, 8], [9, 4, 1, 3]], expected: 8 }, 9 | { 10 | args: [[1], [1, 5], [7, 5, 8], [9, 4, 1, 3], [-1, -1, -1, -1, -1]], 11 | expected: 7, 12 | }, 13 | ] 14 | 15 | describe('Challenge #14: The best path', () => { 16 | buildChallengeTestCases({ 17 | cases: TEST_CASES, 18 | fn: getOptimalPath, 19 | }) 20 | }) 21 | -------------------------------------------------------------------------------- /2023/challenge-08/challenge-08.ts: -------------------------------------------------------------------------------- 1 | export function organizeGifts(gifts: string) { 2 | let organizedGifts = '' 3 | const groups = gifts.match(/\d+\w/g) ?? [] 4 | 5 | for (const group of groups) { 6 | const total = +group.slice(0, -1) 7 | const gift = group.slice(-1) 8 | 9 | const pallets = total / 50 10 | const boxes = (total / 10) % 5 11 | const bags = total % 10 12 | 13 | const palletGifts = `[${gift}]`.repeat(pallets) 14 | const boxGifts = `{${gift}}`.repeat(boxes) 15 | const bagGifts = `(${gift.repeat(bags)})`.repeat(+(bags > 0)) 16 | 17 | organizedGifts += `${palletGifts}${boxGifts}${bagGifts}` 18 | } 19 | 20 | return organizedGifts 21 | } 22 | -------------------------------------------------------------------------------- /2023/challenge-22/challenge-22.ts: -------------------------------------------------------------------------------- 1 | export function compile(code: string) { 2 | let counter = 0 3 | let stack = -1 4 | 5 | for (let i = 0; i < code.length; i++) { 6 | const instruction = code[i] 7 | 8 | if (instruction === '+') counter++ 9 | else if (instruction === '-') counter-- 10 | else if (instruction === '*') counter *= 2 11 | else if (instruction === '%') stack = i 12 | else if (instruction === '<') { 13 | if (stack !== -1) { 14 | i = stack 15 | stack = -1 16 | } 17 | } else if (instruction === '¿') { 18 | if (counter <= 0) { 19 | i = code.indexOf('?', i) 20 | } 21 | } 22 | } 23 | 24 | return counter 25 | } 26 | -------------------------------------------------------------------------------- /2021/challenge-06/challenge-06.ts: -------------------------------------------------------------------------------- 1 | export function sumPairs(numbers: number[], result: number) { 2 | const sum = (pairs: number[]) => pairs.reduce((acc, pair) => acc + pair, 0) 3 | 4 | const [, pairs] = numbers.slice(1).reduce<[number, number[]]>( 5 | ([acc, pairs], number, i) => { 6 | if (pairs.length < 2 && acc + number === result) { 7 | acc += number 8 | pairs.push(number) 9 | } 10 | 11 | if (i === numbers.length - 2 && sum(pairs) !== result) { 12 | pairs = [numbers.at(-2) as number, number] 13 | } 14 | 15 | return [acc, pairs] 16 | }, 17 | [numbers[0], [numbers[0]]], 18 | ) 19 | 20 | return sum(pairs) === result ? pairs : null 21 | } 22 | -------------------------------------------------------------------------------- /2022/challenge-01/challenge-01.test.ts: -------------------------------------------------------------------------------- 1 | import { describe } from 'vitest' 2 | import { wrapping } from './challenge-01' 3 | 4 | const TEST_CASES: TestCases = [ 5 | { 6 | args: ['cat', 'game', 'socks'], 7 | expected: [ 8 | '*****\n*cat*\n*****', 9 | '******\n*game*\n******', 10 | '*******\n*socks*\n*******', 11 | ], 12 | }, 13 | { args: ['will'], expected: ['******\n*will*\n******'] }, 14 | { args: ['a'], expected: ['***\n*a*\n***'] }, 15 | { args: [], expected: [] }, 16 | ] 17 | 18 | describe('Challenge #1: Automating Christmas gift wrapping!', () => { 19 | buildChallengeTestCases({ 20 | cases: TEST_CASES, 21 | fn: wrapping, 22 | }) 23 | }) 24 | -------------------------------------------------------------------------------- /2023/challenge-10/challenge-10.test.ts: -------------------------------------------------------------------------------- 1 | import { describe } from 'vitest' 2 | import { createChristmasTree } from './challenge-10' 3 | 4 | const TEST_CASES: TestCases<[string, number], string> = [ 5 | { args: ['x', 3], expected: ` x\n x x\nx x x\n |\n` }, 6 | { args: ['xo', 4], expected: ` x\n o x\n o x o\nx o x o\n |\n` }, 7 | { 8 | args: ['123', 5], 9 | expected: ` 1\n 2 3\n 1 2 3\n 1 2 3 1\n2 3 1 2 3\n |\n`, 10 | }, 11 | { args: ['*@o', 3], expected: ` *\n @ o\n* @ o\n |\n` }, 12 | ] 13 | 14 | describe('Challenge #10: Create your owm Christmas tree', () => { 15 | buildChallengeTestCases({ 16 | cases: TEST_CASES, 17 | spreadFn: createChristmasTree, 18 | }) 19 | }) 20 | -------------------------------------------------------------------------------- /2021/challenge-03/challenge-03.test.ts: -------------------------------------------------------------------------------- 1 | import { describe } from 'vitest' 2 | import { isValid } from './challenge-03' 3 | 4 | const TEST_CASES: TestCases = [ 5 | { args: 'bici coche (balón) bici coche peluche', expected: true }, 6 | { args: '(muñeca) consola bici', expected: true }, 7 | { args: 'bici coche (balón bici coche', expected: false }, 8 | { args: 'peluche (bici [coche) bici coche balón', expected: false }, 9 | { args: '(peluche {) bici', expected: false }, 10 | { args: '() bici', expected: false }, 11 | ] 12 | 13 | describe('Challenge #3: El Grinch quiere fastidiar la Navidad', () => { 14 | buildChallengeTestCases({ 15 | cases: TEST_CASES, 16 | fn: isValid, 17 | }) 18 | }) 19 | -------------------------------------------------------------------------------- /2022/challenge-02/challenge-02.test.ts: -------------------------------------------------------------------------------- 1 | import { describe } from 'vitest' 2 | import { countHours } from './challenge-02' 3 | 4 | const TEST_CASES: TestCases<[number, string[]], number> = [ 5 | { args: [2023, ['01/06', '04/01', '12/25']], expected: 4 }, 6 | { args: [2022, ['01/06', '04/01', '12/25']], expected: 4 }, 7 | { 8 | args: [ 9 | 1985, 10 | ['01/01', '01/06', '02/02', '02/17', '02/28', '06/03', '12/06', '12/25'], 11 | ], 12 | expected: 10, 13 | }, 14 | { args: [2000, ['01/01']], expected: 0 }, 15 | ] 16 | 17 | describe('Challenge #2: Nobody wants to do extra hours at work', () => { 18 | buildChallengeTestCases({ 19 | cases: TEST_CASES, 20 | spreadFn: countHours, 21 | }) 22 | }) 23 | -------------------------------------------------------------------------------- /2022/challenge-18/challenge-18.test.ts: -------------------------------------------------------------------------------- 1 | import { describe } from 'vitest' 2 | import { dryNumber } from './challenge-18' 3 | 4 | const TEST_CASES: TestCases<[number, number], number[]> = [ 5 | { args: [1, 15], expected: [1, 10, 11, 12, 13, 14, 15] }, 6 | { args: [2, 20], expected: [2, 12, 20] }, 7 | { args: [3, 33], expected: [3, 13, 23, 30, 31, 32, 33] }, 8 | { args: [4, 45], expected: [4, 14, 24, 34, 40, 41, 42, 43, 44, 45] }, 9 | { args: [5, 55], expected: [5, 15, 25, 35, 45, 50, 51, 52, 53, 54, 55] }, 10 | { args: [9, 8], expected: [] }, 11 | ] 12 | 13 | describe('Challenge #18: We ran out of ink!', () => { 14 | buildChallengeTestCases({ 15 | cases: TEST_CASES, 16 | spreadFn: dryNumber, 17 | }) 18 | }) 19 | -------------------------------------------------------------------------------- /2023/challenge-05/challenge-05.ts: -------------------------------------------------------------------------------- 1 | export function cyberReindeer(road: string, time: number) { 2 | let standingOnBarricade = false 3 | const finalRoad = [road] 4 | 5 | for (let i = 1; i < time; i++) { 6 | if (i === 5) { 7 | road = road.replaceAll('|', '*') 8 | } 9 | 10 | const sledPos = road.indexOf('S') 11 | const canGoForward = sledPos !== road.indexOf('|') - 1 12 | 13 | const goToStandOnBarricade = sledPos === road.indexOf('*', sledPos) - 1 14 | 15 | road = canGoForward 16 | ? road.replace(/S(\.|\*)/, `${standingOnBarricade ? '*' : '.'}S`) 17 | : road 18 | 19 | standingOnBarricade = goToStandOnBarricade 20 | finalRoad.push(road) 21 | } 22 | 23 | return finalRoad 24 | } 25 | -------------------------------------------------------------------------------- /2023/challenge-02/challenge-02.test.ts: -------------------------------------------------------------------------------- 1 | import { describe } from 'vitest' 2 | import { manufacture } from './challenge-02' 3 | 4 | const TEST_CASES: TestCases<[string[], string], string[]> = [ 5 | { 6 | args: [['tren', 'oso', 'pelota'], 'tronesa'], 7 | expected: ['tren', 'oso'], 8 | }, 9 | { args: [['pelicula', 'serie', 'tv'], 'svtlir'], expected: ['tv'] }, 10 | { args: [['coche', 'muñeca', 'balon'], 'ocmuñalb'], expected: [] }, 11 | { args: [['patineta', 'robot', 'libro'], 'nopor'], expected: [] }, 12 | { args: [[], 'letras'], expected: [] }, 13 | ] 14 | 15 | describe('Challenge #2: We start the factory', () => { 16 | buildChallengeTestCases({ 17 | cases: TEST_CASES, 18 | spreadFn: manufacture, 19 | }) 20 | }) 21 | -------------------------------------------------------------------------------- /2022/challenge-15/challenge-15.ts: -------------------------------------------------------------------------------- 1 | // score: 300 2 | export function decorateTree(base: string) { 3 | const dict = { 4 | PP: 'P', 5 | PR: 'B', 6 | PB: 'R', 7 | RR: 'R', 8 | RP: 'B', 9 | RB: 'P', 10 | BB: 'B', 11 | BP: 'R', 12 | BR: 'P', 13 | } 14 | 15 | return base 16 | .split(' ') 17 | .slice(1) 18 | .reduce( 19 | tree => { 20 | const topTree = tree[0].split(' ') 21 | const top = topTree.slice(0, -1).reduce((acc, letter, i) => { 22 | const key = (letter + topTree[i + 1]) as keyof typeof dict 23 | return acc.concat(dict[key]) 24 | }, []) 25 | 26 | return [top.join(' '), ...tree] 27 | }, 28 | [base], 29 | ) 30 | } 31 | -------------------------------------------------------------------------------- /2023/challenge-23/challenge-23.ts: -------------------------------------------------------------------------------- 1 | export function organizeChristmasDinner(dishes: string[][]) { 2 | const ingredients = new Map() 3 | 4 | for (const dish of dishes) { 5 | const dishName = dish[0] 6 | const dishIngredients = dish.slice(1) 7 | 8 | for (const ingredient of dishIngredients) { 9 | if (!ingredients.has(ingredient)) { 10 | ingredients.set(ingredient, []) 11 | } 12 | 13 | ingredients.get(ingredient)?.push(dishName) 14 | } 15 | } 16 | 17 | const organizedDishes = [...ingredients.entries()] 18 | .filter(([, dishes]) => dishes.length >= 2) 19 | .map(([ingredient, dishes]) => [ingredient, ...dishes.sort()]) 20 | .sort() 21 | 22 | return organizedDishes 23 | } 24 | -------------------------------------------------------------------------------- /2021/challenge-19/challenge-19.test.ts: -------------------------------------------------------------------------------- 1 | import { describe } from 'vitest' 2 | import { learn } from './challenge-19' 3 | 4 | const TEST_CASES: TestCases<[number, number[]], number[] | null> = [ 5 | { args: [10, [2, 3, 8, 1, 4]], expected: [0, 2] }, 6 | { args: [15, [2, 10, 4, 1]], expected: [1, 2] }, 7 | { args: [25, [10, 15, 20, 5]], expected: [0, 1] }, 8 | { args: [8, [8, 2, 1]], expected: [1, 2] }, 9 | { args: [8, [8, 2, 1, 4, 3]], expected: [3, 4] }, 10 | { args: [4, [10, 14, 20]], expected: null }, 11 | { args: [5, [5, 5, 5]], expected: null }, 12 | ] 13 | 14 | describe('Challenge #19: ¿Qué deberíamos aprender en Platzi?', () => { 15 | buildChallengeTestCases({ 16 | cases: TEST_CASES, 17 | spreadFn: learn, 18 | }) 19 | }) 20 | -------------------------------------------------------------------------------- /2023/challenge-13/challenge-13.ts: -------------------------------------------------------------------------------- 1 | export function calculateTime(deliveries: string[]) { 2 | let seconds = -(7 * 3600) 3 | 4 | for (const delivery of deliveries) { 5 | const [hours, minutes, secs] = delivery.split(':') 6 | seconds += +hours * 3600 + +minutes * 60 + +secs 7 | } 8 | 9 | const sign = seconds < 0 ? '-' : '' 10 | 11 | seconds = Math.abs(seconds) 12 | 13 | const hours = Math.floor(seconds / 3600) 14 | .toString() 15 | .padStart(2, '0') 16 | 17 | seconds %= 3600 18 | 19 | const minutes = Math.floor(seconds / 60) 20 | .toString() 21 | .padStart(2, '0') 22 | 23 | seconds %= 60 24 | 25 | const secs = seconds.toString().padStart(2, '0') 26 | 27 | return `${sign}${hours}:${minutes}:${secs}` 28 | } 29 | -------------------------------------------------------------------------------- /2022/challenge-24/challenge-24.ts: -------------------------------------------------------------------------------- 1 | export function canExit(maze: string[][]) { 2 | function replace(str: string) { 3 | return str 4 | .replace(/[S][\sE]/g, 'SS') 5 | .replace(/[\sE][S]/g, 'SS') 6 | .split('') 7 | } 8 | 9 | const length = Math.min(Math.floor((maze.length * maze[0].length) / 2), 9) 10 | 11 | const checks: string[][][] = [] 12 | new Array(length).fill(0).forEach(() => { 13 | const check = [...maze] 14 | maze.forEach((horizontal, i) => { 15 | maze[i] = replace(horizontal.join('')) 16 | maze[i] = maze[0].map((_, j) => replace(maze.map(x => x[j]).join(''))[i]) 17 | }) 18 | check.join('') === maze.join('') && checks.push(check) 19 | }) 20 | 21 | return !checks.flat(2).includes('E') 22 | } 23 | -------------------------------------------------------------------------------- /2023/challenge-11/challenge-11.test.ts: -------------------------------------------------------------------------------- 1 | import { describe } from 'vitest' 2 | import { getIndexsForPalindrome } from './challenge-11' 3 | 4 | const TEST_CASES: TestCases = [ 5 | { args: 'anna', expected: [] }, 6 | { args: 'abab', expected: [0, 1] }, 7 | { args: 'abac', expected: null }, 8 | { args: 'aaaaaaaa', expected: [] }, 9 | { args: 'aaababa', expected: [1, 3] }, 10 | { args: 'caababa', expected: null }, 11 | { args: 'rotavator', expected: [] }, 12 | { args: 'rotaratov', expected: [4, 8] }, 13 | { args: 'saippuakivikauppias', expected: [] }, 14 | ] 15 | 16 | describe('Challenge #11: The studious elves', () => { 17 | buildChallengeTestCases({ 18 | cases: TEST_CASES, 19 | fn: getIndexsForPalindrome, 20 | }) 21 | }) 22 | -------------------------------------------------------------------------------- /2023/challenge-21/challenge-21.ts: -------------------------------------------------------------------------------- 1 | export function findBalancedSegment(message: number[]) { 2 | const segment = [0, 0] 3 | 4 | for (const [i, binary] of message.entries()) { 5 | let ones = binary 6 | let zeros = +(binary === 0) 7 | const currentSegment = [i, i] 8 | 9 | for (const [j, number] of message.slice(i + 1).entries()) { 10 | ones += number 11 | zeros += +(number === 0) 12 | 13 | if (ones === zeros) { 14 | currentSegment[1] = j + i + 1 15 | } 16 | } 17 | 18 | if (currentSegment[1] - currentSegment[0] > segment[1] - segment[0]) { 19 | segment[0] = currentSegment[0] 20 | segment[1] = currentSegment[1] 21 | } 22 | } 23 | 24 | if (segment[1] === 0) return [] 25 | 26 | return segment 27 | } 28 | -------------------------------------------------------------------------------- /2021/challenge-20/challenge-20.test.ts: -------------------------------------------------------------------------------- 1 | import { describe } from 'vitest' 2 | import { pangram } from './challenge-20' 3 | 4 | const TEST_CASES: TestCases = [ 5 | { 6 | args: 'Extraño pan de col y kiwi se quemó bajo fugaz vaho', 7 | expected: true, 8 | }, 9 | { 10 | args: 'Jovencillo emponzoñado y con walkman: ¡qué figurota exhibes!', 11 | expected: true, 12 | }, 13 | { 14 | args: 'Esto es una frase larga pero no tiene todas las letras del abecedario', 15 | expected: false, 16 | }, 17 | { args: 'De la a a la z, nos faltan letras', expected: false }, 18 | ] 19 | 20 | describe('Challenge #20: ¿Una carta de pangramas? ¡QUÉ!', () => { 21 | buildChallengeTestCases({ 22 | cases: TEST_CASES, 23 | fn: pangram, 24 | }) 25 | }) 26 | -------------------------------------------------------------------------------- /2022/challenge-11/challenge-11.test.ts: -------------------------------------------------------------------------------- 1 | import { describe } from 'vitest' 2 | import { getCompleted } from './challenge-11' 3 | 4 | const TEST_CASES: TestCases<[string, string], string> = [ 5 | { args: ['01:00:00', '03:00:00'], expected: '1/3' }, 6 | { args: ['02:00:00', '04:00:00'], expected: '1/2' }, 7 | { args: ['01:00:00', '01:00:00'], expected: '1/1' }, 8 | { args: ['00:10:00', '01:00:00'], expected: '1/6' }, 9 | { args: ['01:10:10', '03:30:30'], expected: '1/3' }, 10 | { args: ['02:20:20', '03:30:30'], expected: '2/3' }, 11 | { args: ['03:30:30', '05:50:50'], expected: '3/5' }, 12 | ] 13 | 14 | describe('Challenge #11: Santa Claus is Scrum Master', () => { 15 | buildChallengeTestCases({ 16 | cases: TEST_CASES, 17 | spreadFn: getCompleted, 18 | }) 19 | }) 20 | -------------------------------------------------------------------------------- /2023/challenge-21/challenge-21.test.ts: -------------------------------------------------------------------------------- 1 | import { describe } from 'vitest' 2 | import { findBalancedSegment } from './challenge-21' 3 | 4 | const TEST_CASES: TestCases = [ 5 | { args: [1, 1, 0, 1, 1, 0, 1, 1], expected: [2, 5] }, 6 | { args: [1, 1, 0], expected: [1, 2] }, 7 | { args: [1, 1, 1], expected: [] }, 8 | { args: [1, 0, 1], expected: [0, 1] }, 9 | { args: [1, 0, 1, 0], expected: [0, 3] }, 10 | { args: [1, 1, 0, 1, 0, 1], expected: [1, 4] }, 11 | { args: [1, 0, 0, 0, 1, 1, 1, 0, 0, 0], expected: [0, 7] }, 12 | { args: [0, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 1], expected: [5, 10] }, 13 | ] 14 | 15 | describe('Challenge #21: Binary message', () => { 16 | buildChallengeTestCases({ 17 | cases: TEST_CASES, 18 | fn: findBalancedSegment, 19 | }) 20 | }) 21 | -------------------------------------------------------------------------------- /2021/challenge-16/challenge-16.test.ts: -------------------------------------------------------------------------------- 1 | import { describe } from 'vitest' 2 | import { decodeNumber } from './challenge-16' 3 | 4 | const TEST_CASES: TestCases = [ 5 | { args: '...', expected: 3 }, 6 | { args: '.,', expected: 4 }, 7 | { args: ',.', expected: 6 }, 8 | { args: ',...', expected: 8 }, 9 | { args: '.........!', expected: 107 }, 10 | { args: '.;', expected: 49 }, 11 | { args: '..,', expected: 5 }, 12 | { args: '..,!', expected: 95 }, 13 | { args: '.;!', expected: 49 }, 14 | { args: '!!!', expected: 300 }, 15 | { args: ';!', expected: 50 }, 16 | { args: ';.W', expected: NaN }, 17 | ] 18 | 19 | describe('Challenge #16: Descifrando los números...', () => { 20 | buildChallengeTestCases({ 21 | cases: TEST_CASES, 22 | fn: decodeNumber, 23 | }) 24 | }) 25 | -------------------------------------------------------------------------------- /2023/challenge-22/challenge-22.test.ts: -------------------------------------------------------------------------------- 1 | import { describe } from 'vitest' 2 | import { compile } from './challenge-22' 3 | 4 | const TEST_CASES: TestCases = [ 5 | { args: '++*-', expected: 3 }, 6 | { args: '++¿+?', expected: 3 }, 7 | { args: '-+¿+?', expected: 0 }, 8 | { args: '++*¿-?', expected: 3 }, 9 | { args: '++%++<', expected: 6 }, 10 | { args: '++%++<++¿*?', expected: 16 }, 11 | { args: '++¿+?¿+?¿+?', expected: 5 }, 12 | { args: '--¿+++?', expected: -2 }, 13 | { args: '--¿+++?+++¿--?', expected: -1 }, 14 | { args: '<%+¿++%++ { 19 | buildChallengeTestCases({ 20 | cases: TEST_CASES, 21 | fn: compile, 22 | }) 23 | }) 24 | -------------------------------------------------------------------------------- /2021/challenge-18/challenge-18.test.ts: -------------------------------------------------------------------------------- 1 | import { describe } from 'vitest' 2 | import { fixFiles } from './challenge-18' 3 | 4 | const TEST_CASES: TestCases = [ 5 | { 6 | args: ['photo', 'postcard', 'photo', 'photo', 'video'], 7 | expected: ['photo', 'postcard', 'photo(1)', 'photo(2)', 'video'], 8 | }, 9 | { 10 | args: ['file', 'file', 'file', 'game', 'game'], 11 | expected: ['file', 'file(1)', 'file(2)', 'game', 'game(1)'], 12 | }, 13 | { 14 | args: ['file', 'file(1)', 'icon', 'icon(1)', 'icon(1)'], 15 | expected: ['file', 'file(1)', 'icon', 'icon(1)', 'icon(1)(1)'], 16 | }, 17 | ] 18 | 19 | describe('Challenge #18: El sistema operativo de Santa Claus', () => { 20 | buildChallengeTestCases({ 21 | cases: TEST_CASES, 22 | fn: fixFiles, 23 | }) 24 | }) 25 | -------------------------------------------------------------------------------- /2021/challenge-01/challenge-01.test.ts: -------------------------------------------------------------------------------- 1 | import { describe } from 'vitest' 2 | import { contarOvejas, type Oveja } from './challenge-01' 3 | 4 | const TEST_CASES: TestCases = [ 5 | { 6 | args: [ 7 | { name: 'Noa', color: 'azul' }, 8 | { name: 'Euge', color: 'rojo' }, 9 | { name: 'Navidad', color: 'rojo' }, 10 | { name: 'Ki Na Ma', color: 'rojo' }, 11 | { name: 'AAAAAaaaaa', color: 'rojo' }, 12 | { name: 'Nnnnnnnn', color: 'rojo' }, 13 | ], 14 | expected: [ 15 | { name: 'Navidad', color: 'rojo' }, 16 | { name: 'Ki Na Ma', color: 'rojo' }, 17 | ], 18 | }, 19 | ] 20 | 21 | describe('Challenge #1: Contando Ovejas para dormir', () => { 22 | buildChallengeTestCases({ 23 | cases: TEST_CASES, 24 | fn: contarOvejas, 25 | }) 26 | }) 27 | -------------------------------------------------------------------------------- /2022/challenge-15/challenge-15.test.ts: -------------------------------------------------------------------------------- 1 | import { describe } from 'vitest' 2 | import { decorateTree } from './challenge-15' 3 | 4 | const TEST_CASES: TestCases = [ 5 | { args: 'B P R P', expected: ['R', 'P B', 'R B B', 'B P R P'] }, 6 | { args: 'B B', expected: ['B', 'B B'] }, 7 | { 8 | args: 'B B P R P R R', 9 | expected: [ 10 | 'B', 11 | 'R P', 12 | 'B P P', 13 | 'P R B R', 14 | 'P P B B P', 15 | 'B R B B B R', 16 | 'B B P R P R R', 17 | ], 18 | }, 19 | { 20 | args: 'R R P R R', 21 | expected: ['R', 'R R', 'P B P', 'R B B R', 'R R P R R'], 22 | }, 23 | ] 24 | 25 | describe('Challenge #15: Decorating the Christmas tree', () => { 26 | buildChallengeTestCases({ 27 | cases: TEST_CASES, 28 | fn: decorateTree, 29 | }) 30 | }) 31 | -------------------------------------------------------------------------------- /2022/challenge-05/challenge-05.test.ts: -------------------------------------------------------------------------------- 1 | import { describe } from 'vitest' 2 | import { getMaxGifts } from './challenge-05' 3 | 4 | const TEST_CASES: TestCases<[number[], number, number], number> = [ 5 | { args: [[12, 3, 11, 5, 7], 20, 3], expected: 20 }, 6 | { args: [[50], 15, 1], expected: 0 }, 7 | { args: [[50], 100, 1], expected: 50 }, 8 | { args: [[50], 100, 1], expected: 50 }, 9 | { args: [[50, 70], 100, 1], expected: 70 }, 10 | { args: [[50, 70, 30], 100, 3], expected: 100 }, 11 | { args: [[50, 10, 40, 1000, 500, 200], 199, 4], expected: 100 }, 12 | { args: [[50, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11], 1000, 1000], expected: 115 }, 13 | ] 14 | 15 | describe("Challenge #5: Optimizing Santa's trips", () => { 16 | buildChallengeTestCases({ 17 | cases: TEST_CASES, 18 | spreadFn: getMaxGifts, 19 | }) 20 | }) 21 | -------------------------------------------------------------------------------- /2023/challenge-19/challenge-19.ts: -------------------------------------------------------------------------------- 1 | export function revealSabotage(store: string[][]) { 2 | for (const [i, row] of store.entries()) { 3 | for (const [j, col] of row.entries()) { 4 | if (col === '*') continue 5 | 6 | const nextRow = store[i + 1] 7 | const prevRow = store[i - 1] 8 | 9 | const adjacentCells = [ 10 | prevRow?.[j - 1], 11 | prevRow?.[j], 12 | prevRow?.[j + 1], 13 | row[j - 1], 14 | row[j + 1], 15 | nextRow?.[j - 1], 16 | nextRow?.[j], 17 | nextRow?.[j + 1], 18 | ] 19 | 20 | const count = adjacentCells.reduce( 21 | (acc, curr) => acc + +(curr === '*'), 22 | 0, 23 | ) 24 | 25 | if (count !== 0) { 26 | row[j] = `${count}` 27 | } 28 | } 29 | } 30 | 31 | return store 32 | } 33 | -------------------------------------------------------------------------------- /2021/challenge-05/challenge-05.test.ts: -------------------------------------------------------------------------------- 1 | import { describe } from 'vitest' 2 | import { daysToXmas } from './challenge-05' 3 | 4 | const TEST_CASES: TestCases = [ 5 | { args: 'Dec 1, 2021', expected: 24 }, 6 | { args: 'Dec 24, 2021 00:00:01', expected: 1 }, 7 | { args: 'Dec 24, 2021 23:59:59', expected: 1 }, 8 | { args: 'December 20, 2021 03:24:00', expected: 5 }, 9 | { args: 'Dec 25, 2021', expected: 0 }, 10 | { args: 'Dec 26, 2021', expected: -1 }, 11 | { args: 'Dec 31, 2021', expected: -6 }, 12 | { args: 'Jan 1, 2022 00:00:01', expected: -7 }, 13 | { args: 'Jan 1, 2022 23:59:59', expected: -7 }, 14 | ] 15 | 16 | describe('Challenge #5: Contando los días para los regalos', () => { 17 | buildChallengeTestCases({ 18 | cases: TEST_CASES, 19 | fn: date => daysToXmas(new Date(date)), 20 | }) 21 | }) 22 | -------------------------------------------------------------------------------- /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 = ['train', 'bear', 'ball'] 11 | const materials = 'tronesa' 12 | 13 | manufacture(gifts, materials) // ["train", "bear"] 14 | 15 | const gifts = ['game', 'puzzle'] 16 | const materials = 'jlepuz' 17 | 18 | manufacture(gifts, materials) // ["puzzle"] 19 | 20 | const gifts = ['book', 'ps5'] 21 | const materials = 'psli' 22 | 23 | manufacture(gifts, materials) // [] 24 | ``` 25 | -------------------------------------------------------------------------------- /2023/challenge-14/challenge-14.test.ts: -------------------------------------------------------------------------------- 1 | import { describe } from 'vitest' 2 | import { maxGifts } from './challenge-14' 3 | 4 | const TEST_CASES: TestCases = [ 5 | { args: [2, 4, 2], expected: 4 }, 6 | { args: [5, 1, 1, 5], expected: 10 }, 7 | { args: [4, 1, 1, 4, 2, 1], expected: 9 }, 8 | { args: [1, 3, 1, 3, 100], expected: 103 }, 9 | { args: [1, 2, 3, 1], expected: 4 }, 10 | { args: [2, 7, 9, 3, 1], expected: 12 }, 11 | { args: [0, 0, 0, 0, 1], expected: 1 }, 12 | { args: [100], expected: 100 }, 13 | { args: [1, 1, 1, 1], expected: 2 }, 14 | { args: [1, 1, 1], expected: 2 }, 15 | { args: [3, 4, 5], expected: 8 }, 16 | { args: [99], expected: 99 }, 17 | ] 18 | 19 | describe('Challenge #14: Avoid the alarm', () => { 20 | buildChallengeTestCases({ 21 | cases: TEST_CASES, 22 | fn: maxGifts, 23 | }) 24 | }) 25 | -------------------------------------------------------------------------------- /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-09/challenge-09.test.ts: -------------------------------------------------------------------------------- 1 | import { describe } from 'vitest' 2 | import { adjustLights } from './challenge-09' 3 | 4 | const TEST_CASES: TestCases = [ 5 | { args: ['🔴', '🔴', '🔴'], expected: 1 }, 6 | { args: ['🔴', '🔴', '🔴', '🔴'], expected: 2 }, 7 | { args: ['🟢', '🔴', '🟢', '🟢', '🟢'], expected: 1 }, 8 | { args: ['🔴', '🔴', '🟢', '🟢', '🔴'], expected: 2 }, 9 | { args: ['🟢', '🔴', '🟢', '🔴', '🟢'], expected: 0 }, 10 | { args: ['🔴', '🔴', '🟢', '🟢', '🟢'], expected: 2 }, 11 | { args: ['🟢', '🟢', '🟢', '🔴', '🔴'], expected: 2 }, 12 | { 13 | args: ['🔴', '🔴', '🟢', '🟢', '🟢', '🔴', '🔴', '🟢', '🟢', '🟢', '🟢'], 14 | expected: 5, 15 | }, 16 | ] 17 | 18 | describe('Challenge #9: Switch the light', () => { 19 | buildChallengeTestCases({ 20 | cases: TEST_CASES, 21 | fn: adjustLights, 22 | }) 23 | }) 24 | -------------------------------------------------------------------------------- /2023/challenge-20/challenge-20.ts: -------------------------------------------------------------------------------- 1 | export function distributeGifts(weights: Array>) { 2 | const averages: number[][] = [] 3 | 4 | for (const [i, row] of weights.entries()) { 5 | averages[i] = [] 6 | 7 | for (const [j, col] of row.entries()) { 8 | const nextCol = row[j + 1] 9 | const prevCol = row[j - 1] 10 | const prevRow = weights[i - 1]?.[j] 11 | const nextRow = weights[i + 1]?.[j] 12 | const values = [col, prevCol, nextCol, prevRow, nextRow] 13 | 14 | const total = values.reduce((acc, value) => acc + (value ?? 0), 0) 15 | const divisor = values.reduce( 16 | (acc, value) => acc + +(value != null), 17 | 0, 18 | ) 19 | 20 | const average = Math.round(total / divisor) 21 | 22 | averages[i][j] = average 23 | } 24 | } 25 | 26 | return averages 27 | } 28 | -------------------------------------------------------------------------------- /2023/challenge-25/challenge-25.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-unused-vars */ 2 | 3 | export function travelDistance(map: string) { 4 | const roadmapArr = map.split('\n') 5 | const roadmap = roadmapArr.join('') 6 | const cols = roadmapArr[0].length 7 | const santaPos = roadmap.indexOf('S') 8 | 9 | let gift = 1 10 | let movements = 0 11 | let santaCol = santaPos % cols 12 | let santaRow = Math.floor(santaPos / cols) 13 | 14 | const numbers = roadmap.replace(/\.|S/g, '') 15 | 16 | for (const _number of numbers) { 17 | const giftPos = roadmap.indexOf(`${gift}`) 18 | const giftCol = giftPos % cols 19 | const giftRow = Math.floor(giftPos / cols) 20 | 21 | movements += Math.abs(santaRow - giftRow) + Math.abs(santaCol - giftCol) 22 | 23 | santaCol = giftCol 24 | santaRow = giftRow 25 | 26 | gift++ 27 | } 28 | 29 | return movements 30 | } 31 | -------------------------------------------------------------------------------- /2023/challenge-16/challenge-16.ts: -------------------------------------------------------------------------------- 1 | export function transformTree(tree: Array) { 2 | if (!tree.length) return null 3 | 4 | const rootTree: Array | null> = [ 5 | { 6 | value: tree[0], 7 | left: null, 8 | right: null, 9 | }, 10 | ] 11 | 12 | let index = 1 13 | 14 | for (const node of tree.slice(1)) { 15 | let subTree = null 16 | 17 | if (node != null) { 18 | const parentIndex = index - Math.trunc(index / 2) - 1 19 | const side = index % 2 === 0 ? 'right' : 'left' 20 | const parent = rootTree[parentIndex] 21 | 22 | subTree = { 23 | value: node, 24 | left: null, 25 | right: null, 26 | } 27 | 28 | if (parent) { 29 | parent[side] = subTree 30 | } 31 | } 32 | 33 | index++ 34 | rootTree.push(subTree) 35 | } 36 | 37 | return rootTree[0] 38 | } 39 | -------------------------------------------------------------------------------- /2021/challenge-24/challenge-24.test.ts: -------------------------------------------------------------------------------- 1 | import { describe } from 'vitest' 2 | import { checkIsSameTree, type Tree } from './challenge-24' 3 | 4 | const tree: Tree = { 5 | value: 1, 6 | left: { value: 2, left: null, right: null }, 7 | right: { value: 3, left: null, right: null }, 8 | } 9 | const tree2: Tree = { 10 | value: 1, 11 | left: { value: 3, left: { value: 2, left: null, right: null }, right: null }, 12 | right: { value: 5, left: null, right: { value: 4, left: null, right: null } }, 13 | } 14 | 15 | const TEST_CASES: TestCases<[Tree, Tree], boolean> = [ 16 | { args: [tree, tree], expected: true }, 17 | { args: [tree, tree2], expected: false }, 18 | { args: [tree2, tree2], expected: true }, 19 | ] 20 | 21 | describe('Challenge #24: Comparando árboles de Navidad', () => { 22 | buildChallengeTestCases({ 23 | cases: TEST_CASES, 24 | spreadFn: checkIsSameTree, 25 | }) 26 | }) 27 | -------------------------------------------------------------------------------- /2022/challenge-08/README.md: -------------------------------------------------------------------------------- 1 | # Challenge #8: We need a mechanic! 2 | 3 | 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. 4 | 5 | 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**. 6 | 7 | **A palindrome is a word or phrase that reads the same from left to right as it does from right to left**. 8 | 9 | Our function should return a boolean that indicates whether the spare part is valid or not with that rule: 10 | 11 | ```js 12 | checkPart('uwu') // true 13 | // "uwu" is a palindrome without removing any character 14 | 15 | checkPart('miidim') // true 16 | // "miidim" can be a palindrome after removing the first "i" 17 | 18 | checkPart('midu') // false 19 | // "midu" cannot be a palindrome after removing a character 20 | ``` 21 | -------------------------------------------------------------------------------- /2023/challenge-13/challenge-13.test.ts: -------------------------------------------------------------------------------- 1 | import { describe } from 'vitest' 2 | import { calculateTime } from './challenge-13' 3 | 4 | const TEST_CASES: TestCases = [ 5 | { args: ['00:10:00', '01:00:00', '03:30:00'], expected: '-02:20:00' }, 6 | { args: ['01:00:00', '05:00:00', '00:30:00'], expected: '-00:30:00' }, 7 | { args: ['02:00:00', '03:00:00', '02:00:00'], expected: '00:00:00' }, 8 | { args: ['01:01:01', '09:59:59', '01:01:01'], expected: '05:02:01' }, 9 | { args: ['02:00:00', '05:00:00', '00:30:00'], expected: '00:30:00' }, 10 | { 11 | args: ['00:45:00', '00:45:00', '00:00:30', '00:00:30'], 12 | expected: '-05:29:00', 13 | }, 14 | { 15 | args: ['01:01:01', '03:59:59', '01:01:01', '00:57:58'], 16 | expected: '-00:00:01', 17 | }, 18 | ] 19 | 20 | describe('Challenge #13: Calculating the time', () => { 21 | buildChallengeTestCases({ 22 | cases: TEST_CASES, 23 | fn: calculateTime, 24 | }) 25 | }) 26 | -------------------------------------------------------------------------------- /2023/challenge-01/challenge-01.test.ts: -------------------------------------------------------------------------------- 1 | import { describe } from 'vitest' 2 | import { findFirstRepeated } from './challenge-01' 3 | 4 | const TEST_CASES: TestCases = [ 5 | { 6 | args: [2, 1, 3, 5, 3, 2], 7 | expected: 3, 8 | }, 9 | { args: [2, 2], expected: 2 }, 10 | { args: [2, 4, 3, 5, 1], expected: -1 }, 11 | { args: [1], expected: -1 }, 12 | { args: [], expected: -1 }, 13 | { args: [10, 20, 30], expected: -1 }, 14 | { args: [5, 1, 2, 3, 2, 5, 1], expected: 2 }, 15 | { args: [12, 20, 30, 11, 20, 12], expected: 20 }, 16 | { args: [12, 10, 13, 12, 13], expected: 12 }, 17 | { args: [0, 10, 13, 0, 13], expected: 0 }, 18 | { args: [1, 3, 4, 5, 0, 1, 3, 0, 7], expected: 1 }, 19 | { args: [1, 10, 2, 10, 20, 50, 7, 0, 0, 7], expected: 10 }, 20 | ] 21 | 22 | describe('Challenge #1: First gift repeated!', () => { 23 | buildChallengeTestCases({ 24 | cases: TEST_CASES, 25 | fn: findFirstRepeated, 26 | }) 27 | }) 28 | -------------------------------------------------------------------------------- /2023/challenge-12/challenge-12.ts: -------------------------------------------------------------------------------- 1 | export function checkIsValidCopy(original: string, copy: string) { 2 | let index = 0 3 | let isValidCopy = true 4 | const symbolSequence = '#+:. ' 5 | 6 | for (const letter of original) { 7 | const copyLetter = copy[index++] 8 | const symbolIndex = symbolSequence.indexOf(letter) 9 | 10 | const symbols = [ 11 | symbolSequence, 12 | symbolSequence.slice(symbolIndex, symbolSequence.length), 13 | ][+(symbolIndex !== -1)] 14 | 15 | const isValidLetter = `${letter}${letter.toLowerCase()}${symbols}`.includes( 16 | copyLetter, 17 | ) 18 | 19 | const isLetterBlankSpace = letter === ' ' 20 | const isCopyLetterBlankSpace = copyLetter === ' ' 21 | 22 | const isValidCharacter = [isValidLetter, isCopyLetterBlankSpace][ 23 | +isLetterBlankSpace 24 | ] 25 | 26 | isValidCopy = [isValidCopy, isValidCharacter][+isValidCopy] 27 | } 28 | 29 | return isValidCopy 30 | } 31 | -------------------------------------------------------------------------------- /2023/challenge-25/challenge-25.test.ts: -------------------------------------------------------------------------------- 1 | import { describe } from 'vitest' 2 | import { travelDistance } from './challenge-25' 3 | 4 | const TEST_CASES: TestCases = [ 5 | { 6 | args: `S1`, 7 | expected: 1, 8 | }, 9 | { 10 | args: `1....S`, 11 | expected: 5, 12 | }, 13 | { 14 | args: `..S.1...`, 15 | expected: 2, 16 | }, 17 | { 18 | args: `S12....3`, 19 | expected: 7, 20 | }, 21 | { 22 | args: `.....1....\n..S.......\n..........\n....3.....\n......2...`, 23 | expected: 12, 24 | }, 25 | { 26 | args: `.....2....\n..S.......\n..........\n....1.....\n......3...`, 27 | expected: 13, 28 | }, 29 | { 30 | args: `3....1....\n..S.......\n.........2\n..........\n......4...`, 31 | expected: 31, 32 | }, 33 | ] 34 | 35 | describe('Challenge #25: Calculating distances', () => { 36 | buildChallengeTestCases({ 37 | cases: TEST_CASES, 38 | fn: travelDistance, 39 | }) 40 | }) 41 | -------------------------------------------------------------------------------- /2021/challenge-25/challenge-25.ts: -------------------------------------------------------------------------------- 1 | export function canMouseEat(direction: Direction, game: string[][]) { 2 | const isDirectionUp = direction === 'up' 3 | const isDirectionDown = direction === 'down' 4 | const isDirectionLeft = direction === 'left' 5 | const isDirectionRight = direction === 'right' 6 | 7 | const mouseRowIndex = game.findIndex(row => row.includes('m')) 8 | const mouseRow = game[mouseRowIndex] 9 | const mouseIndex = mouseRow.findIndex(str => str === 'm') 10 | 11 | let canEat = false 12 | 13 | if (isDirectionUp || isDirectionDown) { 14 | const upOrDown = 15 | game[mouseRowIndex + (isDirectionUp ? -1 : 1)]?.[mouseIndex] 16 | canEat = upOrDown === '*' 17 | } 18 | 19 | if (isDirectionLeft || isDirectionRight) { 20 | const leftOrRight = mouseRow[mouseIndex + (isDirectionLeft ? -1 : 1)] 21 | canEat = leftOrRight === '*' 22 | } 23 | 24 | return canEat 25 | } 26 | 27 | export type Direction = 'up' | 'right' | 'down' | 'left' 28 | -------------------------------------------------------------------------------- /2022/challenge-23/challenge-23.test.ts: -------------------------------------------------------------------------------- 1 | import { describe } from 'vitest' 2 | import { executeCommands } from './challenge-23' 3 | 4 | const TEST_CASES: TestCases = [ 5 | { 6 | args: ['MOV 5,V00', 'MOV 10,V01', 'DEC V00', 'ADD V00,V01'], 7 | expected: [14, 10, 0, 0, 0, 0, 0, 0], 8 | }, 9 | { 10 | args: ['MOV 255,V00', 'INC V00', 'DEC V01', 'DEC V01'], 11 | expected: [0, 254, 0, 0, 0, 0, 0, 0], 12 | }, 13 | { 14 | args: ['MOV 10,V00', 'DEC V00', 'INC V01', 'JMP 1', 'INC V06'], 15 | expected: [0, 10, 0, 0, 0, 0, 1, 0], 16 | }, 17 | { 18 | args: [ 19 | 'MOV 10,V00', 20 | 'MOV V00,V01', 21 | 'MOV V01,V02', 22 | 'MOV V02,V03', 23 | 'MOV V03,V04', 24 | ], 25 | expected: [10, 10, 10, 10, 10, 0, 0, 0], 26 | }, 27 | ] 28 | 29 | describe('Challenge #23: Santa Claus Compiler', () => { 30 | buildChallengeTestCases({ 31 | cases: TEST_CASES, 32 | fn: executeCommands, 33 | }) 34 | }) 35 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /2022/challenge-10/challenge-10.test.ts: -------------------------------------------------------------------------------- 1 | import { describe } from 'vitest' 2 | import { checkJump } from './challenge-10' 3 | 4 | const TEST_CASES: TestCases = [ 5 | { args: [1, 3, 8, 5, 2], expected: true }, 6 | { args: [1, 7, 3, 5], expected: false }, 7 | { args: [1, 2, 3, 2, 1], expected: true }, 8 | { args: [1, 2, 2, 2, 1], expected: true }, 9 | { args: [0, 1, 0], expected: true }, 10 | { args: [2, 2, 2, 2], expected: false }, 11 | { args: [1, 2, 3], expected: false }, 12 | { args: [1, 2, 3, 2, 1, 2, 3], expected: false }, 13 | { args: [1, 1000, 900, 800], expected: true }, 14 | { args: [1, 1000, 100, 800], expected: false }, 15 | { args: [1, 1, 1, 1, 1, 1, 1, 1, 2, 1], expected: true }, 16 | { args: [1, 2, 3, 1, 3, 1], expected: false }, 17 | { args: [1, 3, 2, 5, 4, 3, 2, 1], expected: false }, 18 | ] 19 | 20 | describe('Challenge #10: The Santa Claus sleigh jump', () => { 21 | buildChallengeTestCases({ 22 | cases: TEST_CASES, 23 | fn: checkJump, 24 | }) 25 | }) 26 | -------------------------------------------------------------------------------- /2023/challenge-09/README.md: -------------------------------------------------------------------------------- 1 | # Challenge #9: Switch the light 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-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(['00:45:00', '00:45:00', '00:00:30', '00:00:30']) // '-05:29:00' 19 | ``` 20 | -------------------------------------------------------------------------------- /2022/challenge-21/challenge-21.ts: -------------------------------------------------------------------------------- 1 | export function printTable(gifts: Gift[]) { 2 | const giftWidth = Math.max(...gifts.map(gift => gift.name.length), 4) 3 | const quantityWidth = Math.max( 4 | ...gifts.map(gift => `${gift.quantity}`.length), 5 | 8, 6 | ) 7 | 8 | const tableWidth = giftWidth + quantityWidth + 7 9 | 10 | const top = '+'.repeat(tableWidth) 11 | const bottom = '*'.repeat(tableWidth) 12 | const head = `Gift${' '.repeat(giftWidth - 4)} | Quantity${' '.repeat( 13 | quantityWidth - 8, 14 | )}` 15 | const border = `${'-'.repeat(giftWidth)} | ${'-'.repeat(quantityWidth)}` 16 | const body = gifts 17 | .map( 18 | gift => 19 | `| ${gift.name}${' '.repeat(giftWidth - gift.name.length)} | ${ 20 | gift.quantity 21 | }${' '.repeat(quantityWidth - `${gift.quantity}`.length)} |`, 22 | ) 23 | .join('\n') 24 | 25 | return `${top}\n| ${head} |\n| ${border} |\n${body}\n${bottom}` 26 | } 27 | 28 | export interface Gift { 29 | name: string 30 | quantity: number 31 | } 32 | -------------------------------------------------------------------------------- /2021/challenge-25/challenge-25.test.ts: -------------------------------------------------------------------------------- 1 | import { describe } from 'vitest' 2 | import { canMouseEat, type Direction } from './challenge-25' 3 | 4 | const room = [ 5 | [' ', ' ', ' '], 6 | [' ', ' ', 'm'], 7 | [' ', ' ', '*'], 8 | ] 9 | 10 | const room2 = [ 11 | ['*', ' ', ' ', ' '], 12 | [' ', 'm', '*', ' '], 13 | [' ', ' ', ' ', ' '], 14 | [' ', ' ', ' ', '*'], 15 | ] 16 | 17 | const TEST_CASES: TestCases<[Direction, string[][]], boolean> = [ 18 | { args: ['up', room], expected: false }, 19 | { args: ['down', room], expected: true }, 20 | { args: ['right', room], expected: false }, 21 | { args: ['left', room], expected: false }, 22 | { args: ['up', room2], expected: false }, 23 | { args: ['down', room2], expected: false }, 24 | { args: ['right', room2], expected: true }, 25 | { args: ['left', room2], expected: false }, 26 | ] 27 | 28 | describe('Challenge #25: El último juego y hasta el año que viene 👋', () => { 29 | buildChallengeTestCases({ 30 | cases: TEST_CASES, 31 | spreadFn: canMouseEat, 32 | }) 33 | }) 34 | -------------------------------------------------------------------------------- /2022/challenge-17/challenge-17.test.ts: -------------------------------------------------------------------------------- 1 | import { describe } from 'vitest' 2 | import { carryGifts } from './challenge-17' 3 | 4 | const TEST_CASES: TestCases<[string[], number], string[]> = [ 5 | { 6 | args: [['game', 'bike', 'book', 'toy'], 10], 7 | expected: ['game bike', 'book toy'], 8 | }, 9 | { 10 | args: [['game', 'bike', 'book', 'toy'], 7], 11 | expected: ['game', 'bike', 'book toy'], 12 | }, 13 | { 14 | args: [['game', 'bike', 'book', 'toy'], 4], 15 | expected: ['game', 'bike', 'book', 'toy'], 16 | }, 17 | { 18 | args: [['toy', 'gamme', 'toy', 'bike'], 6], 19 | expected: ['toy', 'gamme', 'toy', 'bike'], 20 | }, 21 | { args: [['toy', 'toy', 'toy', 'toy'], 2], expected: [] }, 22 | { 23 | args: [['toy', 'toy', 'disk', 'disk', 'toy', 'toy'], 7], 24 | expected: ['toy toy', 'disk', 'disk toy', 'toy'], 25 | }, 26 | ] 27 | 28 | describe('Challenge #17: Carrying gifts in bags', () => { 29 | buildChallengeTestCases({ 30 | cases: TEST_CASES, 31 | spreadFn: carryGifts, 32 | }) 33 | }) 34 | -------------------------------------------------------------------------------- /2023/challenge-11/challenge-11.ts: -------------------------------------------------------------------------------- 1 | export function getIndexsForPalindrome(word: string) { 2 | const _letters = [...word] 3 | const palindrome = word === [..._letters].reverse().join('') 4 | 5 | let initial: number[] | null = [null, []][+palindrome] 6 | let index = 0 7 | let aux = 1 8 | 9 | const letters = [_letters, []][+palindrome] 10 | let auxLetters = letters.slice(1) 11 | 12 | for (const letter of letters) { 13 | for (const auxLetter of auxLetters) { 14 | const w = [...letters] 15 | w[index] = auxLetter 16 | w[aux] = letter 17 | 18 | const isPalindrome = +(w.join('') === w.reverse().join('')) 19 | const isInitialNull = +(initial == null) 20 | const isDifferentIndex = +(index !== aux) 21 | 22 | const values = [initial, initial, initial, [index, aux]] 23 | 24 | initial = values[isInitialNull + isDifferentIndex + isPalindrome] 25 | aux++ 26 | } 27 | 28 | index++ 29 | aux = 1 30 | auxLetters = [[], auxLetters][+(initial == null)] 31 | } 32 | 33 | return initial 34 | } 35 | -------------------------------------------------------------------------------- /2021/challenge-22/challenge-22.test.ts: -------------------------------------------------------------------------------- 1 | import { describe } from 'vitest' 2 | import { countDecorations, type Tree } from './challenge-22' 3 | 4 | const TEST_CASES: TestCases = [ 5 | { 6 | args: { 7 | value: 1, 8 | left: { value: 2, left: null, right: null }, 9 | right: { value: 3, left: null, right: null }, 10 | }, 11 | expected: 6, 12 | }, 13 | { 14 | args: { 15 | value: 1, 16 | left: { 17 | value: 5, 18 | left: { 19 | value: 7, 20 | left: { value: 3, left: null, right: null }, 21 | right: null, 22 | }, 23 | right: null, 24 | }, 25 | right: { 26 | value: 6, 27 | left: { value: 5, left: null, right: null }, 28 | right: { value: 1, left: null, right: null }, 29 | }, 30 | }, 31 | expected: 28, 32 | }, 33 | ] 34 | 35 | describe('Challenge #22: ¿Cuántos adornos necesita el árbol?', () => { 36 | buildChallengeTestCases({ 37 | cases: TEST_CASES, 38 | fn: countDecorations, 39 | }) 40 | }) 41 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /.github/workflows/results.yml: -------------------------------------------------------------------------------- 1 | name: Solutions for AdventJs Challenges 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | 8 | jobs: 9 | lint: 10 | name: Check 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v3 14 | - uses: pnpm/action-setup@v2 15 | with: 16 | version: 8 17 | - uses: actions/setup-node@v3 18 | with: 19 | node-version: 18 20 | cache: pnpm 21 | - name: Install dependencies 22 | run: pnpm install 23 | - name: Type, Lint and Format check 24 | run: pnpm run check 25 | 26 | test: 27 | name: Test 28 | runs-on: ubuntu-latest 29 | steps: 30 | - uses: actions/checkout@v3 31 | - uses: pnpm/action-setup@v2 32 | with: 33 | version: 8 34 | - uses: actions/setup-node@v3 35 | with: 36 | node-version: 18 37 | cache: pnpm 38 | - name: Install dependencies 39 | run: pnpm install 40 | - name: Testing Challenge Solutions 41 | run: pnpm run test 42 | -------------------------------------------------------------------------------- /2022/challenge-20/challenge-20.ts: -------------------------------------------------------------------------------- 1 | export function howManyReindeers( 2 | reindeerTypes: Reindeer[], 3 | gifts: Gift[], 4 | ): Result[] { 5 | reindeerTypes.sort((a, b) => b.weightCapacity - a.weightCapacity) 6 | 7 | return gifts.map(({ country, weight }) => { 8 | const remainingRnds = reindeerTypes.filter(x => x.weightCapacity < weight) 9 | let total = remainingRnds.reduce( 10 | (acc, curr) => (acc += curr.weightCapacity), 11 | 0, 12 | ) 13 | 14 | const reindeers = remainingRnds.map(({ type, weightCapacity }) => { 15 | const num = (weight / total) >> 0 16 | total -= weightCapacity 17 | weight -= num * weightCapacity 18 | return { type, num } 19 | }) 20 | 21 | return { country, reindeers } 22 | }) 23 | } 24 | 25 | export interface Reindeer { 26 | type: string 27 | weightCapacity: number 28 | } 29 | 30 | export interface Gift { 31 | country: string 32 | weight: number 33 | } 34 | 35 | export interface Result { 36 | country: string 37 | reindeers: Array<{ 38 | type: string 39 | num: number 40 | }> 41 | } 42 | -------------------------------------------------------------------------------- /2023/challenge-15/challenge-15.ts: -------------------------------------------------------------------------------- 1 | export function autonomousDrive(store: string[], movements: string[]) { 2 | let row = store.findIndex(line => line.includes('!')) 3 | let col = store[row].indexOf('!') 4 | 5 | function placeRobot(robot: string) { 6 | const line = store[row] 7 | store[row] = line.slice(0, col) + robot + line.slice(col + 1) 8 | } 9 | 10 | placeRobot('.') 11 | 12 | const maxRow = store.length 13 | const maxCol = store[0].length 14 | 15 | for (const move of movements) { 16 | const rowMove = row + +(move === 'D') - +(move === 'U') 17 | const colMove = col + +(move === 'R') - +(move === 'L') 18 | const rowValues = [row, row, rowMove] 19 | const colValues = [col, col, colMove] 20 | 21 | const nextRow = rowValues[+(rowMove >= 0) + +(rowMove < maxRow)] 22 | const nextCol = colValues[+(colMove >= 0) + +(colMove < maxCol)] 23 | 24 | const pos = store[nextRow][nextCol] 25 | 26 | if (pos !== '*') { 27 | col = nextCol 28 | row = nextRow 29 | } 30 | } 31 | 32 | placeRobot('!') 33 | 34 | return store 35 | } 36 | -------------------------------------------------------------------------------- /2021/challenge-17/challenge-17.test.ts: -------------------------------------------------------------------------------- 1 | import { describe } from 'vitest' 2 | import { countPackages } from './challenge-17' 3 | 4 | const TEST_CASES: TestCases< 5 | [Array<[string, number, string[]]>, string], 6 | number 7 | > = [ 8 | { 9 | args: [ 10 | [ 11 | ['dapelu', 5, ['midu', 'jelowing']], 12 | ['midu', 2, []], 13 | ['jelowing', 2, []], 14 | ], 15 | 'dapelu', 16 | ], 17 | expected: 9, 18 | }, 19 | { 20 | args: [ 21 | [ 22 | ['lolivier', 8, ['camila', 'jesuspoleo']], 23 | ['camila', 5, ['sergiomartinez', 'conchaasensio']], 24 | ['jesuspoleo', 4, []], 25 | ['sergiomartinez', 4, []], 26 | ['conchaasensio', 3, ['facundocapua', 'faviola']], 27 | ['facundocapua', 2, []], 28 | ['faviola', 1, []], 29 | ], 30 | 'camila', 31 | ], 32 | expected: 15, 33 | }, 34 | ] 35 | 36 | describe('Challenge #17: La locura de enviar paquetes en esta época', () => { 37 | buildChallengeTestCases({ 38 | cases: TEST_CASES, 39 | spreadFn: countPackages, 40 | }) 41 | }) 42 | -------------------------------------------------------------------------------- /2022/challenge-07/challenge-07.test.ts: -------------------------------------------------------------------------------- 1 | import { describe } from 'vitest' 2 | import { getGiftsToRefill } from './challenge-07' 3 | 4 | const TEST_CASES: TestCases<[string[], string[], string[]], string[]> = [ 5 | { 6 | args: [ 7 | ['bici', 'coche', 'bici', 'bici'], 8 | ['coche', 'bici', 'muñeca', 'coche'], 9 | ['bici', 'pc', 'pc'], 10 | ], 11 | expected: ['muñeca', 'pc'], 12 | }, 13 | { 14 | args: [[], [], []], 15 | expected: [], 16 | }, 17 | { 18 | args: [ 19 | ['a', 'a'], 20 | ['a', 'a'], 21 | ['a', 'a'], 22 | ], 23 | expected: [], 24 | }, 25 | { 26 | args: [ 27 | ['a', 'a'], 28 | ['b', 'b'], 29 | ['c', 'c'], 30 | ], 31 | expected: ['a', 'b', 'c'], 32 | }, 33 | { 34 | args: [ 35 | ['a', 'b'], 36 | ['c', 'd'], 37 | ['e', 'f'], 38 | ], 39 | expected: ['a', 'b', 'c', 'd', 'e', 'f'], 40 | }, 41 | ] 42 | 43 | describe('Challenge #7: Doing gifts inventory', () => { 44 | buildChallengeTestCases({ 45 | cases: TEST_CASES, 46 | spreadFn: getGiftsToRefill, 47 | }) 48 | }) 49 | -------------------------------------------------------------------------------- /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-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 | -------------------------------------------------------------------------------- /2022/challenge-22/challenge-22.test.ts: -------------------------------------------------------------------------------- 1 | import { describe } from 'vitest' 2 | import { checkStepNumbers } from './challenge-22' 3 | 4 | const TEST_CASES: TestCases<[string[], number[]], boolean> = [ 5 | { 6 | args: [ 7 | ['tree_1', 'tree_2', 'house', 'tree_1', 'tree_2', 'house'], 8 | [1, 33, 10, 2, 44, 20], 9 | ], 10 | expected: true, 11 | }, 12 | { 13 | args: [ 14 | ['tree_1', 'tree_1', 'house'], 15 | [2, 1, 10], 16 | ], 17 | expected: false, 18 | }, 19 | { 20 | args: [ 21 | ['tree_1', 'tree_1', 'house'], 22 | [1, 2, 10], 23 | ], 24 | expected: true, 25 | }, 26 | { 27 | args: [ 28 | [ 29 | 'house', 30 | 'house', 31 | 'tree_1', 32 | 'tree_1', 33 | 'house', 34 | 'tree_2', 35 | 'tree_2', 36 | 'tree_3', 37 | ], 38 | [5, 2, 1, 2, 3, 4, 5, 6], 39 | ], 40 | expected: false, 41 | }, 42 | ] 43 | 44 | describe('Challenge #22: The lights in sync', () => { 45 | buildChallengeTestCases({ 46 | cases: TEST_CASES, 47 | spreadFn: checkStepNumbers, 48 | }) 49 | }) 50 | -------------------------------------------------------------------------------- /vitest.setup.ts: -------------------------------------------------------------------------------- 1 | import kindOf from 'kind-of' 2 | import { expect, it } from 'vitest' 3 | 4 | global.buildChallengeTestCases = ({ 5 | cases, 6 | fn, 7 | spreadFn, 8 | }: BuildChallengeTestOptions) => { 9 | const expectedTypeof = kindOf(cases[0].expected) 10 | 11 | const name = fn?.name ?? spreadFn?.name 12 | 13 | const executor = (args: Args) => { 14 | if (fn) return fn(args) 15 | if (spreadFn) return spreadFn(...(args as any)) 16 | 17 | throw Error('buildChallengeTest requires an executor fn') 18 | } 19 | 20 | it(`#T should return ${expectedTypeof} type`, () => { 21 | const result = executor(cases[0].args) 22 | 23 | expect(kindOf(result)).toBe(expectedTypeof) 24 | }) 25 | 26 | const nameTemplate = spreadFn 27 | ? `#%# ${name}(${(cases[0].args as any[]) 28 | .map((_, i) => '$args.' + i) 29 | .join(', ')}) should return $expected` 30 | : `#%# ${name}($args) should return $expected` 31 | 32 | it.each(cases)(nameTemplate, ({ args, expected }) => { 33 | const result = executor(args) 34 | 35 | expect(result).toEqual(expected) 36 | }) 37 | } 38 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2023 Will 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /2021/challenge-04/README.md: -------------------------------------------------------------------------------- 1 | # Challenge #4: ¡Es hora de poner la navidad en casa! 2 | 3 | > Creo que ya podemos sacar el gorro navideño, el turrón... ¡Y el árbol de navidad! 🎄 Vamos a montarlo con JavaScript. 4 | 5 | ¡Es hora de poner el árbol de navidad en casa! 🎄 6 | 7 | Para ello vamos a crear una función que recibe la altura del árbol, que será un entero positivo del 1 a, como máximo, 100. 8 | 9 | Si le pasamos el argumento `5`, se pintaría esto: 10 | 11 | ```js 12 | ____*____ 13 | ___***___ 14 | __*****__ 15 | _*******_ 16 | ********* 17 | ____#____ 18 | ____#____ 19 | ``` 20 | 21 | Creamos un triángulo de asteriscos `*` con la altura proporcionada y, a los lados, usamos el guión bajo `_` para los espacios. Es muy importante que nuestro árbol siempre tenga la misma longitud por cada lado. 22 | 23 | Todos los árboles, por pequeños o grandes que sean, tienen un tronco de dos líneas de `#`. 24 | 25 | Otro ejemplo con un árbol de altura `3`: 26 | 27 | ```js 28 | __*__ 29 | _***_ 30 | ***** 31 | __#__ 32 | __#__ 33 | ``` 34 | 35 | Ten en cuenta que el árbol es un string y necesitas los saltos de línea `\n` para cada línea para que se forme bien el árbol. 36 | -------------------------------------------------------------------------------- /2023/challenge-07/challenge-07.ts: -------------------------------------------------------------------------------- 1 | export function drawGift(size: number, symbol: string) { 2 | if (size === 1) { 3 | return '#\n' 4 | } 5 | 6 | let gift = '' 7 | let spaces = size - 2 8 | let middleRestCount = 0 9 | const symbolSize = spaces 10 | const middleStep = size - 1 11 | const totalLevels = size * 2 - 2 12 | 13 | for (let level = 1; level < totalLevels; level++) { 14 | const isMiddle = level === middleStep 15 | const startSymbolCount = isMiddle ? 0 : symbolSize 16 | const middleWrapCount = isMiddle ? middleStep : 1 17 | 18 | if (level >= size) { 19 | middleRestCount += 2 20 | } 21 | 22 | const endSymbolCount = level - middleRestCount - 1 23 | 24 | const wrap = `${' '.repeat(spaces)}#${symbol.repeat( 25 | startSymbolCount, 26 | )}${'#'.repeat(middleWrapCount)}${symbol.repeat(endSymbolCount)}#\n` 27 | 28 | gift += wrap 29 | 30 | if (level < middleStep) { 31 | spaces-- 32 | } 33 | } 34 | 35 | const topWrap = `${' '.repeat(middleStep)}${'#'.repeat(size)}\n` 36 | const bottomWrap = `${'#'.repeat(size)}\n` 37 | 38 | return `${topWrap}${gift}${bottomWrap}` 39 | } 40 | -------------------------------------------------------------------------------- /2022/challenge-23/challenge-23.ts: -------------------------------------------------------------------------------- 1 | export function executeCommands(commands: string[]) { 2 | const registers: number[] = Array(8).fill(0) 3 | 4 | const actions: Record< 5 | string, 6 | (first: number, second: number, command: string) => void 7 | > = { 8 | MOV: (fst, snd, command) => { 9 | const moveValue = command.slice(4).startsWith('V') ? registers[fst] : fst 10 | registers[snd] = moveValue 11 | }, 12 | ADD: (fst, snd) => 13 | (registers[fst] = (registers[fst] + registers[snd]) % 256), 14 | DEC: number => (registers[number] = (registers[number] - 1 + 256) % 256), 15 | INC: number => (registers[number] = (registers[number] + 1) % 256), 16 | } 17 | 18 | for (let i = 0; i < commands.length; i++) { 19 | const command = commands[i] 20 | const [commandName, args] = command.split(' ') 21 | const values = args.split(',').map(v => +v.replace(/V/g, '')) as [ 22 | number, 23 | number, 24 | ] 25 | 26 | if (commandName === 'JMP' && registers[0] !== 0) i = values[0] - 1 27 | if (commandName !== 'JMP') actions[commandName](...values, command) 28 | } 29 | 30 | return registers 31 | } 32 | -------------------------------------------------------------------------------- /2021/challenge-03/README.md: -------------------------------------------------------------------------------- 1 | # Challenge #3: El Grinch quiere fastidiar la Navidad 2 | 3 | > ¡El Grinch anda suelto y quiere fastidiar la Navidad! 😱 Vamos a arreglar el lío que ha montado en la fábrica de regalos de Santa Claus 4 | 5 | El Grinch está abriendo las cartas que iban a Santa Claus y las está dejando hechas un lío. 😱 6 | 7 | Las cartas son una cadena de texto que incluyen regalos y paréntesis `()`. 8 | 9 | Para saber si una carta es válida ✅, debes comprobar que los paréntesis cierran correctamente y que, además, no vayan vacíos. 10 | 11 | **¡Pero ojo!** Porque el Grinch ha dejado llaves `{` y corchetes `[` dentro de los paréntesis que **hacen que no sean válidas**. Por suerte sólo los ha dejado en medio de los paréntesis... 12 | 13 | Ejemplos: 14 | 15 | ```js 16 | 'bici coche (balón) bici coche peluche' // -> ✅ 17 | '(muñeca) consola bici' // ✅ 18 | 19 | 'bici coche (balón bici coche' // -> ❌ 20 | 'peluche (bici [coche) bici coche balón' // -> ❌ 21 | '(peluche {) bici' // -> ❌ 22 | '() bici' // ❌ 23 | ``` 24 | 25 | Crea una función que pasándole el texto de la carta, devuelva true si es válida y false si no lo es. ¡Y acaba con la travesura del Grinch! 26 | -------------------------------------------------------------------------------- /2021/challenge-13/README.md: -------------------------------------------------------------------------------- 1 | # Challenge #13: Envuelve regalos con asteriscos 2 | 3 | > Estamos a fuego envolviendo regalos... ¡pero necesitamos automatizar esto antes de que los elfos decidan ponerse en huelga! ¡Salva la Navidad (otra vez)! 4 | 5 | ¡Hay demasiados regalos 🎁! Y envolverlos es una locura... 6 | 7 | Vamos a crear una función que pasándole un array de regalos, nos devuelva otro array pero donde todos los regalos han sido envueltos con asteriscos tanto por arriba como por los lados. 8 | 9 | Sólo tienes que tener en cuenta unas cosillas ✌️: 10 | 11 | - Si el array está vacío, devuelve un array vacío 12 | - Los regalos son emojis 🎁... por lo que tenlo en cuenta a la hora de contar su longitud... 13 | - Por suerte, cada posición del array siempre tiene la misma longitud... 14 | 15 | ```js 16 | wrapGifts(['📷', '⚽️']) 17 | /* Resultado: 18 | [ '****', 19 | '*📷*', 20 | '*⚽️*', 21 | '****' 22 | ] 23 | */ 24 | 25 | wrapGifts(['🏈🎸', '🎮🧸']) 26 | /* Resultado: 27 | [ '******', 28 | '*🏈🎸*', 29 | '*🎮🧸*', 30 | '******' 31 | ] 32 | */ 33 | 34 | wrapGifts(['📷']) 35 | /* Resultado: 36 | [ '****', 37 | '*📷*', 38 | '****' 39 | ] 40 | */ 41 | ``` 42 | -------------------------------------------------------------------------------- /2021/challenge-14/README.md: -------------------------------------------------------------------------------- 1 | # Challenge #14: En busca del reno perdido 2 | 3 | > En el pueblo de Santa Claus han ido a pasear a los renos y se les ha escapado uno. ¡Madre mía! Ahora a buscarlo. 😿 4 | 5 | ¡Hemos perdido a un reno y falta poco más de una semana para Navidad! 😱 6 | 7 | Lo peor es que son tantos que no sabemos cuál es el que nos falta... ¡Qué lío! A ver, Elfon Musk ha hecho inventario y nos pasa un array con los ids de cada reno. 8 | 9 | 👍 **Lo bueno**: los ids son números del 0 al 100, no están repetidos y sólo se ha perdido un reno. 10 | 11 | 👎 **Lo malo**: la lista está desordenada y podría faltar el último... 12 | 13 | Necesitamos una función que al pasarle la lista de ids de renos nos diga inmediatamente cuál es el que falta: 14 | 15 | ```js 16 | missingReindeer([0, 2, 3]) // -> 1 17 | missingReindeer([5, 6, 1, 2, 3, 7, 0]) // -> 4 18 | missingReindeer([0, 1]) // -> 2 (¡es el último el que falta!) 19 | missingReindeer([3, 0, 1]) // -> 2 20 | missingReindeer([9, 2, 3, 5, 6, 4, 7, 0, 1]) // -> 8 21 | missingReindeer([0]) // -> 1 (¡es el último el que falta!) 22 | ``` 23 | 24 | Parece fácil con una complejidad de O(n)... ¿crees que podrías hacerlo mejor? 25 | -------------------------------------------------------------------------------- /2022/challenge-19/README.md: -------------------------------------------------------------------------------- 1 | # Challenge #19: Sorting the toys! 2 | 3 | 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. 4 | 5 | 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. 6 | 7 | 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. 8 | 9 | We have to **return an array where each toy is in the position it corresponds to**. 10 | 11 | ```js 12 | const toys = ['ball', 'doll', 'car', 'puzzle'] 13 | const positions = [2, 3, 1, 0] 14 | 15 | sortToys(toys, positions) 16 | // ['puzzle', 'car', 'ball', 'doll'] 17 | 18 | const moreToys = ['pc', 'xbox', 'ps4', 'switch', 'nintendo'] 19 | const morePositions = [8, 6, 5, 7, 9] 20 | 21 | sortToys(moreToys, morePositions) 22 | // ['ps4', 'xbox', 'switch', 'pc', 'nintendo'] 23 | ``` 24 | 25 | **Things to keep in mind**: 26 | 27 | - There will always be the same number of toys as positions. 28 | - Neither the toys nor the positions are repeated. 29 | -------------------------------------------------------------------------------- /2022/challenge-19/challenge-19.test.ts: -------------------------------------------------------------------------------- 1 | import { describe } from 'vitest' 2 | import { sortToys } from './challenge-19' 3 | 4 | const TEST_CASES: TestCases<[string[], number[]], string[]> = [ 5 | { 6 | args: [ 7 | ['ball', 'doll', 'car', 'puzzle'], 8 | [2, 3, 1, 0], 9 | ], 10 | expected: ['puzzle', 'car', 'ball', 'doll'], 11 | }, 12 | { 13 | args: [ 14 | ['pc', 'xbox', 'ps4', 'switch', 'nintendo'], 15 | [3, 1, 0, 2, 4], 16 | ], 17 | expected: ['ps4', 'xbox', 'switch', 'pc', 'nintendo'], 18 | }, 19 | { 20 | args: [ 21 | ['pc', 'xbox', 'ps4', 'switch', 'nintendo'], 22 | [8, 6, 5, 7, 9], 23 | ], 24 | expected: ['ps4', 'xbox', 'switch', 'pc', 'nintendo'], 25 | }, 26 | { 27 | args: [ 28 | ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'j', 'k', 'l'], 29 | [1112, 1113, 1114, 1115, 1116, 1117, 1118, 1119, 1120, 1121, 1111], 30 | ], 31 | expected: ['l', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'j', 'k'], 32 | }, 33 | ] 34 | 35 | describe('Challenge #19: Sorting the toys!', () => { 36 | buildChallengeTestCases({ 37 | cases: TEST_CASES, 38 | spreadFn: sortToys, 39 | }) 40 | }) 41 | -------------------------------------------------------------------------------- /2022/challenge-04/challenge-04.test.ts: -------------------------------------------------------------------------------- 1 | import { describe } from 'vitest' 2 | import { fitsInOneBox, type Box } from './challenge-04' 3 | 4 | const TEST_CASES: TestCases = [ 5 | { 6 | args: [ 7 | { l: 1, w: 1, h: 10 }, 8 | { l: 3, w: 3, h: 12 }, 9 | { l: 2, w: 2, h: 1 }, 10 | ], 11 | expected: false, 12 | }, 13 | { 14 | args: [ 15 | { l: 1, w: 1, h: 1 }, 16 | { l: 2, w: 2, h: 2 }, 17 | ], 18 | expected: true, 19 | }, 20 | { 21 | args: [ 22 | { l: 1, w: 1, h: 1 }, 23 | { l: 2, w: 2, h: 2 }, 24 | { l: 3, w: 1, h: 3 }, 25 | ], 26 | expected: false, 27 | }, 28 | { 29 | args: [ 30 | { l: 1, w: 1, h: 1 }, 31 | { l: 2, w: 2, h: 2 }, 32 | { l: 2, w: 10, h: 2 }, 33 | ], 34 | expected: false, 35 | }, 36 | { 37 | args: [ 38 | { l: 1, w: 1, h: 1 }, 39 | { l: 3, w: 3, h: 3 }, 40 | { l: 2, w: 2, h: 2 }, 41 | ], 42 | expected: true, 43 | }, 44 | ] 45 | 46 | describe('Challenge #4: Box inside a box and another...', () => { 47 | buildChallengeTestCases({ 48 | cases: TEST_CASES, 49 | fn: fitsInOneBox, 50 | }) 51 | }) 52 | -------------------------------------------------------------------------------- /2023/challenge-07/challenge-07.test.ts: -------------------------------------------------------------------------------- 1 | import { describe } from 'vitest' 2 | import { drawGift } from './challenge-07' 3 | 4 | const TEST_CASES: TestCases<[number, `${string}`], string> = [ 5 | { 6 | args: [4, '+'], 7 | expected: ` ####\n #++##\n #++#+#\n####++#\n#++#+#\n#++##\n####\n`, 8 | }, 9 | { 10 | args: [5, '*'], 11 | expected: ` #####\n #***##\n #***#*#\n #***#**#\n#####***#\n#***#**#\n#***#*#\n#***##\n#####\n`, 12 | }, 13 | { 14 | args: [1, '^'], 15 | expected: `#\n`, 16 | }, 17 | { 18 | args: [2, '&'], 19 | expected: ` ##\n###\n##\n`, 20 | }, 21 | { 22 | args: [10, '%'], 23 | expected: ` ##########\n #%%%%%%%%##\n #%%%%%%%%#%#\n #%%%%%%%%#%%#\n #%%%%%%%%#%%%#\n #%%%%%%%%#%%%%#\n #%%%%%%%%#%%%%%#\n #%%%%%%%%#%%%%%%#\n #%%%%%%%%#%%%%%%%#\n##########%%%%%%%%#\n#%%%%%%%%#%%%%%%%#\n#%%%%%%%%#%%%%%%#\n#%%%%%%%%#%%%%%#\n#%%%%%%%%#%%%%#\n#%%%%%%%%#%%%#\n#%%%%%%%%#%%#\n#%%%%%%%%#%#\n#%%%%%%%%##\n##########\n`, 24 | }, 25 | ] 26 | 27 | describe('Challenge #7: The 3D boxes', () => { 28 | buildChallengeTestCases({ 29 | cases: TEST_CASES, 30 | spreadFn: drawGift, 31 | }) 32 | }) 33 | -------------------------------------------------------------------------------- /2022/challenge-01/README.md: -------------------------------------------------------------------------------- 1 | # Challenge #1: Automating Christmas gift wrapping! 2 | 3 | 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**. 4 | 5 | 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. 6 | 7 | The wrapping paper is the `*` symbol, and in order to wrap a gift, you need to place it surrounding the string. For example: 8 | 9 | ```js 10 | const gifts = ['cat', 'game', 'socks'] 11 | const wrapped = wrapping(gifts) 12 | 13 | console.log(wrapped) 14 | /* [ 15 | "*****\n*cat*\n*****", 16 | "******\n*game*\n******", 17 | "*******\n*socks*\n*******" 18 | ] */ 19 | ``` 20 | 21 | 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. 22 | 23 | **Note**: `\n` represents a line break. 24 | 25 | **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. 26 | 27 | Ah, **and do not mutate the original array!** 28 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /2021/challenge-07/challenge-07.test.ts: -------------------------------------------------------------------------------- 1 | import { describe } from 'vitest' 2 | import { contains } from './challenge-07' 3 | 4 | const TEST_CASES: TestCases<[Record, string], boolean> = [ 5 | { 6 | args: [ 7 | { 8 | estanteria1: { 9 | cajon1: { 10 | producto1: 'coca-cola', 11 | producto2: 'fanta', 12 | producto3: 'sprite', 13 | }, 14 | }, 15 | estanteria2: { 16 | cajon1: 'vacio', 17 | cajon2: { 18 | producto1: 'pantalones', 19 | producto2: 'camiseta', 20 | }, 21 | }, 22 | }, 23 | 'camiseta', 24 | ], 25 | expected: true, 26 | }, 27 | { 28 | args: [ 29 | { 30 | baul: { 31 | fondo: { 32 | objeto: 'cd-rom', 33 | 'otro-objeto': 'disquette', 34 | 'otra-cosa': 'mando', 35 | }, 36 | }, 37 | }, 38 | 'gameboy', 39 | ], 40 | expected: false, 41 | }, 42 | ] 43 | 44 | describe('Challenge #7: Buscando en el almacén...', () => { 45 | buildChallengeTestCases({ 46 | cases: TEST_CASES, 47 | spreadFn: contains, 48 | }) 49 | }) 50 | -------------------------------------------------------------------------------- /2021/challenge-21/challenge-21.test.ts: -------------------------------------------------------------------------------- 1 | import { describe } from 'vitest' 2 | import { canCarry } from './challenge-21' 3 | 4 | const TEST_CASES: TestCases<[number, number[][]], boolean> = [ 5 | { 6 | args: [ 7 | 4, 8 | [ 9 | [2, 5, 8], 10 | [3, 6, 10], 11 | ], 12 | ], 13 | expected: false, 14 | }, 15 | { 16 | args: [ 17 | 3, 18 | [ 19 | [1, 1, 5], 20 | [2, 2, 10], 21 | ], 22 | ], 23 | expected: true, 24 | }, 25 | { 26 | args: [ 27 | 3, 28 | [ 29 | [2, 1, 5], 30 | [3, 5, 7], 31 | ], 32 | ], 33 | expected: true, 34 | }, 35 | { 36 | args: [ 37 | 4, 38 | [ 39 | [2, 3, 8], 40 | [2, 5, 7], 41 | ], 42 | ], 43 | expected: true, 44 | }, 45 | { args: [1, [[2, 3, 8]]], expected: false }, 46 | { 47 | args: [ 48 | 2, 49 | [ 50 | [1, 2, 4], 51 | [2, 3, 8], 52 | ], 53 | ], 54 | expected: false, 55 | }, 56 | ] 57 | 58 | describe('Challenge #21: La ruta con los regalos', () => { 59 | buildChallengeTestCases({ 60 | cases: TEST_CASES, 61 | spreadFn: canCarry, 62 | }) 63 | }) 64 | -------------------------------------------------------------------------------- /2022/challenge-03/README.md: -------------------------------------------------------------------------------- 1 | # Challenge #3: How many packs of gifts can Santa carry? 2 | 3 | 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**. 4 | 5 | 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**. 6 | 7 | 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**. 8 | 9 | ```js 10 | const packOfGifts = ['book', 'doll', 'ball'] 11 | const reindeers = ['dasher', 'dancer'] 12 | 13 | // pack weights 4 + 4 + 4 = 12 14 | // reindeers can carry (2 _ 6) + (2 _ 6) = 24 15 | distributeGifts(packOfGifts, reindeers) // 2 16 | ``` 17 | 18 | Things to keep in mind: 19 | 20 | - The gifts pack can't be splitted. 21 | - Gifts and reindeers' names length will always be greater than 0. 22 | -------------------------------------------------------------------------------- /2022/challenge-13/challenge-13.test.ts: -------------------------------------------------------------------------------- 1 | import { describe } from 'vitest' 2 | import { getFilesToBackup } from './challenge-13' 3 | 4 | const TEST_CASES: TestCases<[number, Array<[number, number]>], number[]> = [ 5 | { 6 | args: [ 7 | 1546300800, 8 | [ 9 | [3, 1546301100], 10 | [2, 1546300800], 11 | [1, 1546300800], 12 | [1, 1546300900], 13 | [1, 1546301000], 14 | ], 15 | ], 16 | expected: [1, 3], 17 | }, 18 | { 19 | args: [ 20 | 1546300600, 21 | [ 22 | [1, 1546300800], 23 | [2, 1546300800], 24 | [1, 1546300900], 25 | [1, 1546301000], 26 | [3, 1546301100], 27 | ], 28 | ], 29 | expected: [1, 2, 3], 30 | }, 31 | { 32 | args: [ 33 | 1556300600, 34 | [ 35 | [1, 1546300800], 36 | [2, 1546300800], 37 | [1, 1546300900], 38 | [1, 1546301000], 39 | [3, 1546301100], 40 | ], 41 | ], 42 | expected: [], 43 | }, 44 | { args: [1556300600, []], expected: [] }, 45 | ] 46 | 47 | describe('Challenge #13: Backups for Santa Claus files', () => { 48 | buildChallengeTestCases({ 49 | cases: TEST_CASES, 50 | spreadFn: getFilesToBackup, 51 | }) 52 | }) 53 | -------------------------------------------------------------------------------- /2021/challenge-06/README.md: -------------------------------------------------------------------------------- 1 | # Challenge #6: Rematando los exámenes finales 2 | 3 | > Buffff! Ya huelo las vacaciones pero todavía falta terminar los exámenes finales. ¡Y toca un poco de matemáticas! 😱 ¡Ayúdame! 4 | 5 | Antes de poder disfrutar de la navidad... nos toca terminar de rematar los exámenes finales. ¡Y toca un poco de matemáticas! 😱 6 | 7 | A una función se le pasan dos parámetros: un Array con números y el resultado que se espera. 8 | 9 | La función debe devolver los dos valores del Array que sumen el resultado esperado. **Como a veces pueden haber más de dos valores** que sumen, se devolverá el primero empezando por la izquierda que encuentre otro par, **sin importar lo lejos que esté a la derecha**. 10 | 11 | Si no se encuentra, se devuelve `null`. 12 | 13 | Veamos unos ejemplos: 14 | 15 | ```js 16 | sumPairs([3, 5, 7, 2], 10) // [3, 7] 17 | sumPairs([-3, -2, 7, -5], 10) // null 18 | sumPairs([2, 2, 3, 1], 4) // [2, 2] 19 | sumPairs([6, 7, 1, 2], 8) // [6, 2] 20 | sumPairs([0, 2, 2, 3, -1, 1, 5], 6) // [1, 5] 21 | ``` 22 | 23 | El resultado tiene que ser **un array con dos números**. 24 | 25 | Una vez que tengas el resultado... ¿cómo podrías hacer que fuese lo más óptimo posible para **no tener que recorrer las mismas situaciones dos veces** 🤔? 26 | -------------------------------------------------------------------------------- /2021/challenge-01/README.md: -------------------------------------------------------------------------------- 1 | # Challenge #1: Contando ovejas para dormir 2 | 3 | > Con la emoción de que llega la navidad, nos está costando dormir bastante últimamente. Vamos a intentar usar este pequeño truco que nos ayudará a dormir más rápido 🐑. 4 | 5 | Considera una lista/array de ovejas. Cada oveja tiene un nombre y un color. Haz una función que devuelva una lista con todas las ovejas que sean de color `rojo` y que además su nombre contenga tanto las letras `n` Y `a`, sin importar el orden, las mayúsculas o espacios. 6 | 7 | Por ejemplo, si tenemos las ovejas: 8 | 9 | ```js 10 | const ovejas = [ 11 | { name: 'Noa', color: 'azul' }, 12 | { name: 'Euge', color: 'rojo' }, 13 | { name: 'Navidad', color: 'rojo' }, 14 | { name: 'Ki Na Ma', color: 'rojo' }, 15 | { name: 'AAAAAaaaaa', color: 'rojo' }, 16 | { name: 'Nnnnnnnn', color: 'rojo' }, 17 | ] 18 | ``` 19 | 20 | Al ejecutar el método debería devolver lo siguiente: 21 | 22 | ```js 23 | const ovejasFiltradas = contarOvejas(ovejas) 24 | 25 | console.log(ovejasFiltradas) 26 | 27 | // [{ name: 'Navidad', color: 'rojo' }, 28 | // { name: 'Ki Na Ma', color: 'rojo' }] 29 | ``` 30 | 31 | Recuerda. **Debe contener las dos letras 'a' y 'n' en el nombre**. No cuentes ovejas que sólo tenga una de las letras, debe tener ambas. 32 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /2022/challenge-10/README.md: -------------------------------------------------------------------------------- 1 | # Challenge #10: The Santa Claus sleigh jump 2 | 3 | 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. 4 | 5 | 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: 6 | 7 | ```js 8 | const heights = [1, 3, 8, 5, 2] 9 | checkJump(heights) // true 10 | 11 | /* 12 | It's `true`. 13 | The jump goes up-down. 14 | 15 | 8 (highest point) 16 | ↗ ↘ 17 | 3 5 18 | ↗ ↘ 19 | 1 2 20 | */ 21 | 22 | const heights = [1, 7, 3, 5] 23 | checkJump(heights) // false 24 | 25 | /* 26 | It's `false`. 27 | It goes up-down-up. 28 | 29 | 7 5 30 | ↗ ↘ ↗ 31 | 1 3 32 | ``` 33 | 34 | We need the program to return a `boolean` that indicates whether the sleigh makes a parabola or not. 35 | 36 | **Things to keep in mind**: 37 | 38 | - 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. 39 | - It is not necessary for the starting and ending points to be the same (cities can be at different heights). 40 | -------------------------------------------------------------------------------- /2021/challenge-02/README.md: -------------------------------------------------------------------------------- 1 | # Challenge #2: ¡Ayuda al elfo a listar los regalos! 2 | 3 | > ¡Menudo lío 😵! Un elfo está ayudando a Santa Claus. Pensaba que le vendría ya ordenado de cada regalo cuantas unidades debe conseguir... ¡y le ha llegado una carta ✉️! ¡Ayúdale! 4 | 5 | Te ha llegado una carta ✉️ con todos los regalos que debes preparar. El tema es que es una cadena de texto y es muy difícil de leer 😱. ¡Menos mal que han puesto cada regalo separado por espacio! (aunque **ten cuidado**, porque al ser niños, igual han colado más espacios de la cuenta) 6 | 7 | Encima nos hemos dado cuenta que algunas palabras vienen con un `_` delante de la palabra, por ejemplo `_playstation`, que significa que **está tachado y no se tiene que contar**. 8 | 9 | Transforma el texto a un objeto que contenga el nombre de cada regalo y las veces que aparece. Por ejemplo, si tenemos el texto: 10 | 11 | ```js 12 | const carta = 'bici coche balón _playstation bici coche peluche' 13 | ``` 14 | 15 | Al ejecutar el método debería devolver lo siguiente: 16 | 17 | ```js 18 | const regalos = listGifts(carta) 19 | 20 | console.log(regalos) 21 | /* 22 | { 23 | bici: 2, 24 | coche: 2, 25 | balón: 1, 26 | peluche: 1 27 | } 28 | */ 29 | ``` 30 | 31 | Ten en cuenta que los tests pueden ser más exhaustivos... 😝 **¡Cuidado con contar espacios vacíos!** 32 | -------------------------------------------------------------------------------- /2021/challenge-20/README.md: -------------------------------------------------------------------------------- 1 | # Challenge #20: ¿Una carta de pangramas? ¡QUÉ! 2 | 3 | > Los niños de Laponia tienen en su clase de castellano el reto de crear una carta a Santa Claus con todas las letras del alfabeto...¡Ayuda a detectar si lo hacen bien! 4 | 5 | En la clase de español del pueblo de Laponia han creado un reto a la hora de escribir la carta a Papa Noél 🎅: la carta ✉️ tiene que contener todas las letras del alfabeto. 6 | 7 | Desde el taller de Santa 🎅 se han enterado y quieren escribir **una función** que les diga si realmente la cadena de texto que les llega tiene, efectivamente, todas las letras del abecedario español 🔎. 8 | 9 | Hay que tener en cuenta las letras en mayúscula y que las letras con acento y diéresis se consideran iguales. Por ejemplo la `á` y la `ä` cuenta como una `a`. 10 | 11 | Vamos a ver unos ejemplos de frases: 12 | 13 | ```js 14 | pangram('Extraño pan de col y kiwi se quemó bajo fugaz vaho') // true 15 | pangram('Jovencillo emponzoñado y con walkman: ¡qué figurota exhibes!') // true 16 | 17 | pangram('Esto es una frase larga pero no tiene todas las letras del abecedario') // false 18 | pangram('De la a a la z, nos faltan letras') // false 19 | ``` 20 | 21 | Y ya que estás... **¿Cuál es tu pangrama favorito?** ¡Compártelo en nuestra comunidad de [**Discord**](https://discord.com/invite/midudev)! 22 | -------------------------------------------------------------------------------- /2022/challenge-16/challenge-16.test.ts: -------------------------------------------------------------------------------- 1 | import { describe } from 'vitest' 2 | import { fixLetter } from './challenge-16' 3 | 4 | const TEST_CASES: TestCases = [ 5 | { 6 | args: ' hello, how are you?? do you know if santa claus exists? i really hope he does! bye ', 7 | expected: 8 | 'Hello, how are you? Do you know if Santa Claus exists? I really hope he does! Bye.', 9 | }, 10 | { 11 | args: " Hi Santa claus. I'm a girl from Barcelona , Spain . please, send me a bike. Is it possible?", 12 | expected: 13 | "Hi Santa Claus. I'm a girl from Barcelona, Spain. Please, send me a bike. Is it possible?", 14 | }, 15 | { args: ' hi santa claus ', expected: 'Hi Santa Claus.' }, 16 | { 17 | args: ' hi santa claus . santa claus is the best ', 18 | expected: 'Hi Santa Claus. Santa Claus is the best.', 19 | }, 20 | { 21 | args: ' hi,santa claus. are you there ? ', 22 | expected: 'Hi, Santa Claus. Are you there?', 23 | }, 24 | { 25 | args: 'Hey santa Claus . I want a bike. I want a videogame! ', 26 | expected: 'Hey Santa Claus. I want a bike. I want a videogame!', 27 | }, 28 | ] 29 | 30 | describe("Challenge #16: Fixing Santa Claus' letters", () => { 31 | buildChallengeTestCases({ 32 | cases: TEST_CASES, 33 | fn: fixLetter, 34 | }) 35 | }) 36 | -------------------------------------------------------------------------------- /2022/challenge-22/README.md: -------------------------------------------------------------------------------- 1 | # Challenge #22: The lights in sync 2 | 3 | Verify that all independent sequences of Christmas lighting systems are in strictly increasing order. We have two arrays: `systemNames` and `stepNumbers`. 4 | 5 | `systemNames` contains the names of the Christmas lighting systems, and `stepNumbers` contains the step numbers of each system. 6 | 7 | We must verify that the `stepNumbers` of each system are in strictly increasing order. If this is true, return `true`; otherwise, return `false`. 8 | 9 | For example: 10 | 11 | ```js 12 | const systemNames = ['tree_1', 'tree_2', 'house', 'tree_1', 'tree_2', 'house'] 13 | const stepNumbers = [1, 33, 10, 2, 44, 20] 14 | 15 | checkStepNumbers(systemNames, stepNumbers) // => true 16 | 17 | // tree_1 has steps: [1, 2] 18 | // tree_2 has steps: [33, 44] 19 | // house has steps: [10, 20] 20 | 21 | // true: The steps of each system are in strictly increasing order 22 | 23 | checkStepNumbers(['tree_1', 'tree_1', 'house'], [2, 1, 10]) // => false 24 | 25 | // tree_1 has steps: [2, 1] 26 | // house has steps: [10] 27 | 28 | // false: tree_1 has steps in decreasing order 29 | ``` 30 | 31 | Note that: 32 | 33 | - The position of the system name in `systemNames` and the step number in `stepNumbers` correspond to the same system. 34 | - The steps in `stepNumbers` can be repeated for different systems. 35 | -------------------------------------------------------------------------------- /2022/challenge-18/README.md: -------------------------------------------------------------------------------- 1 | # Challenge #18: We ran out of ink! 2 | 3 | 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. 4 | 5 | **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). 6 | 7 | It should **return an array with the numbers that include the number we don't have ink for**. Let's see an example: 8 | 9 | ```js 10 | dryNumber(1, 15) // [1, 10, 11, 12, 13, 14, 15] 11 | 12 | // we don't have ink for the number 1 13 | // we have to print 15 barcodes from 1 to 15 14 | // the barcodes that will be wrong because of the lack of ink are: 15 | // 1, 10, 11, 12, 13, 14, 15 16 | 17 | dryNumber(2, 20) // [2, 12, 20] 18 | 19 | // we don't have ink for the number 2 20 | // we have to print 20 barcodes from 1 to 20 21 | // the barcodes that will be wrong because of the lack of ink are: 22 | 23 | // 2, 12, 20 24 | ``` 25 | 26 | **Keep in mind that**: 27 | 28 | - The number we don't have ink for can only be between 0 and 9. 29 | - The number we don't have ink for can be in any position of the barcode. 30 | - The number of barcodes to print can be very large. 31 | -------------------------------------------------------------------------------- /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(store)) 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 | -------------------------------------------------------------------------------- /2022/challenge-12/challenge-12.test.ts: -------------------------------------------------------------------------------- 1 | import { describe } from 'vitest' 2 | import { selectSleigh, type Sleight } from './challenge-12' 3 | 4 | const TEST_CASES: TestCases<[number, Sleight[]], string | null> = [ 5 | { 6 | args: [ 7 | 1, 8 | [ 9 | { name: 'pheralb', consumption: 0.3 }, 10 | { name: 'TMChein', consumption: 0.5 }, 11 | ], 12 | ], 13 | expected: 'TMChein', 14 | }, 15 | { 16 | args: [ 17 | 10, 18 | [ 19 | { name: 'Pedrosillano', consumption: 1 }, 20 | { name: 'SamarJaffal', consumption: 5 }, 21 | ], 22 | ], 23 | expected: 'Pedrosillano', 24 | }, 25 | { 26 | args: [ 27 | 10, 28 | [ 29 | { name: 'Pedrosillano', consumption: 1 }, 30 | { name: 'SamarJaffal', consumption: 2 }, 31 | { name: 'marcospage', consumption: 3 }, 32 | ], 33 | ], 34 | expected: 'SamarJaffal', 35 | }, 36 | { 37 | args: [ 38 | 50, 39 | [ 40 | { name: 'Pedrosillano', consumption: 1 }, 41 | { name: 'SamarJaffal', consumption: 2 }, 42 | { name: 'marcospage', consumption: 3 }, 43 | ], 44 | ], 45 | expected: null, 46 | }, 47 | ] 48 | 49 | describe('Challenge #12: Electric sleighs, wow!', () => { 50 | buildChallengeTestCases({ 51 | cases: TEST_CASES, 52 | spreadFn: selectSleigh, 53 | }) 54 | }) 55 | -------------------------------------------------------------------------------- /2022/challenge-03/challenge-03.test.ts: -------------------------------------------------------------------------------- 1 | import { describe } from 'vitest' 2 | import { distributeGifts } from './challenge-03' 3 | 4 | const TEST_CASES: TestCases<[string[], string[]], number> = [ 5 | { 6 | args: [ 7 | ['book', 'doll', 'ball'], 8 | ['dasher', 'dancer'], 9 | ], 10 | expected: 2, 11 | }, 12 | { 13 | args: [ 14 | ['a', 'b', 'c'], 15 | ['d', 'e'], 16 | ], 17 | expected: 1, 18 | }, 19 | { 20 | args: [['videogames', 'console'], ['will']], 21 | expected: 0, 22 | }, 23 | { 24 | args: [ 25 | ['game', 'videoconsole', 'computer'], 26 | [ 27 | 'midudev', 28 | 'pheralb', 29 | 'codingwithdani', 30 | 'carlosble', 31 | 'blasco', 32 | 'facundocapua', 33 | 'madeval', 34 | 'memxd', 35 | ], 36 | ], 37 | expected: 5, 38 | }, 39 | { 40 | args: [ 41 | ['music'], 42 | [ 43 | 'midudev', 44 | 'pheralb', 45 | 'codingwithdani', 46 | 'carlosble', 47 | 'blasco', 48 | 'facundocapua', 49 | 'madeval', 50 | 'memxd', 51 | ], 52 | ], 53 | expected: 26, 54 | }, 55 | ] 56 | 57 | describe('Challenge #3: How many packs of gifts can Santa carry?', () => { 58 | buildChallengeTestCases({ 59 | cases: TEST_CASES, 60 | spreadFn: distributeGifts, 61 | }) 62 | }) 63 | -------------------------------------------------------------------------------- /2023/challenge-10/README.md: -------------------------------------------------------------------------------- 1 | # Challenge #10: Create your owm 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 | ```js 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 | ```js 24 | 1 25 | 2 3 26 | 1 2 3 27 | 1 2 3 1 2 28 | | 29 | ``` 30 | 31 | Note: 32 | 33 | - **The tree should always be centered, for that reason add blank spaces to the left of each line.** 34 | - **Create spaces only to the left of each line of the tree. Do not leave blank spaces to the right.** 35 | - **The ornaments have a white space between them for separation.** 36 | -------------------------------------------------------------------------------- /2022/challenge-09/README.md: -------------------------------------------------------------------------------- 1 | # Challenge #9: Crazy Xmas lights 2 | 3 | A company that manufactures Christmas lights has asked us to help them. 4 | 5 | They have led strips that are like an `Array`. Each position is a led and can be on (`1`) or off (`0`). 6 | 7 | **Every 7 seconds**, the leds change state in this way: 8 | 9 | - **If the led is off**, it turns on if the led on its left (`index - 1`) was on before. 10 | - **If the led is on**, it remains on. 11 | 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: 12 | 13 | ```js 14 | const leds = [0, 1, 1, 0, 1] 15 | countTime(leds) // 7 16 | // 7 seconds because in the first change 17 | // all the lights turned on 18 | // 0s: [0, 1, 1, 0, 1] 19 | // 7s: [1, 1, 1, 1, 1] 20 | 21 | countTime([0, 0, 0, 1]) // 21 22 | // 21 seconds because it needs three changes: 23 | // 0s: [0, 0, 0, 1] 24 | // 7s: [1, 0, 0, 1] 25 | // 14s: [1, 1, 0, 1] 26 | // 21s: [1, 1, 1, 1] 27 | 28 | countTime([0, 0, 1, 0, 0]) // 28 29 | // 28 seconds because it needs four changes: 30 | // 0s: [0, 0, 1, 0, 0] 31 | // 7s: [0, 0, 1, 1, 0] 32 | // 14s: [0, 0, 1, 1, 1] 33 | // 21s: [1, 0, 1, 1, 1] 34 | // 28s: [1, 1, 1, 1, 1] 35 | ``` 36 | 37 | Keep in mind 38 | 39 | - The array will always have at least one led on. 40 | - The array can have any length. 41 | - If all the leds are on, the time is 0. 42 | -------------------------------------------------------------------------------- /2021/challenge-11/README.md: -------------------------------------------------------------------------------- 1 | # Challenge #11: ¿Vale la pena la tarjeta fidelidad del cine? 2 | 3 | > ¡Este mes hay un montón de peliculones en el cine! Viendo que voy a tener que pasar bastante por taquilla también en 2022, estoy mirando de optimizar mis gastos. ¡Ayúdame! 4 | 5 | Este mes de diciembre hay películas super interesantes en el cine... y tengo que optimizar cómo gasto el dinero. 6 | 7 | Mi cine favorito tiene dos posibilidades: 8 | 9 | - Entrada de un sólo uso: Cuesta 12$ por cada película. 10 | - Tarjeta de fidelidad: Cuesta 250$ pero que cada vez que vas **pagas sólo el 75% del precio del ticket**. ¡Lo mejor es que se acumula! Y cada vez que vas, se paga el 75% del precio del ticket que pagaste la última vez. 11 | 12 | Ejemplo de cada una al comprar 3 entradas y el precio que pagaría en total: 13 | 14 | ```js 15 | // Entrada normal: 12$ * 3 = 36$ 16 | // Tarjeta fidelidad: 250$ + (12$ * 0,75) + (12$ * 0,75 * 0,75) + (12$ * 0,75 * 0,75 * 0,75) = 270,8125$ 17 | ``` 18 | 19 | Necesito una función que, al pasarle las veces que voy a ir al cine, me diga si vale la pena comprar la tarjeta fidelidad o no. 20 | 21 | ```js 22 | shouldBuyFidelity(1) // false -> Mejor comprar tickets de un sólo uso 23 | shouldBuyFidelity(100) // true -> Mejor comprar tarjeta fidelidad 24 | ``` 25 | 26 | La dificultad del reto está en encontrar una fórmula sencilla que nos diga el precio con descuento acumulado para la tarjeta fidelidad. 😜 27 | -------------------------------------------------------------------------------- /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 | const a = decode('hola (odnum)') 20 | console.log(a) // hola mundo 21 | 22 | const b = decode('(olleh) (dlrow)!') 23 | console.log(b) // hello world! 24 | 25 | const c = decode('sa(u(cla)atn)s') 26 | console.log(c) // santaclaus 27 | 28 | // Step by step: 29 | // 1. Reverse the nested -> sa(ualcatn)s 30 | // 2. Reverse the remaining one -> santaclaus 31 | ``` 32 | 33 | Notes: 34 | 35 | - **The input strings will always be well formed with parentheses that match correctly, you do not need to validate them.** 36 | - **There should not be any parentheses left in the final message.** 37 | - **The maximum nesting level is 2.** 38 | -------------------------------------------------------------------------------- /2022/challenge-16/README.md: -------------------------------------------------------------------------------- 1 | # Challenge #16: Fixing Santa Claus' letters 2 | 3 | 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: 4 | 5 | - Remove spaces at the beginning and end of the prase 6 | - Remove multiple spaces and leave only one 7 | - Leave a space after each comma and point 8 | - Remove spaces before comma or point 9 | - Questions must only end with a question mark 10 | - The first letter of each sentence must be capitalized 11 | - Put the word "Santa Claus" in uppercase if it appears in the letter 12 | - Put a point at the end of the sentence if it does not have punctuation 13 | - Letters are written in English and here we have an example: 14 | 15 | ```js 16 | fixLetter( 17 | ` hello, how are you?? do you know if santa claus exists? i really hope he does! bye `, 18 | ) 19 | // Hello, how are you? Do you know if Santa Claus exists? I really hope he does! Bye. 20 | 21 | fixLetter( 22 | " Hi Santa claus. I'm a girl from Barcelona , Spain . please, send me a bike. Is it possible?", 23 | ) 24 | // Hi Santa Claus. I'm a girl from Barcelona, Spain. Please, send me a bike. Is it possible? 25 | ``` 26 | 27 | Note: 28 | 29 | - You do not have to worry about punctuation marks other than comma, point or question mark. 30 | - Make sure you respect break lines and original whitespaces. 31 | -------------------------------------------------------------------------------- /2022/challenge-02/README.md: -------------------------------------------------------------------------------- 1 | # Challenge #2: Nobody wants to do extra hours at work 2 | 3 | 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**. 4 | 5 | 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. 6 | 7 | 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. 8 | 9 | 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: 10 | 11 | ```js 12 | const year = 2022 13 | const holidays = ['01/06', '04/01', '12/25'] // format MM/DD 14 | 15 | // 01/06 is January 6, Thursday. Count. 16 | // 04/01 is April 1, Friday. Count. 17 | // 12/25 is December 25, Sunday. Do not count. 18 | 19 | countHours(year, holidays) // 2 days -> 4 extra hours in the year 20 | ``` 21 | 22 | Things to keep in mind: 23 | 24 | - The year may be a leap year. Make the checks you need for it, if necessary. 25 | - Although the holiday is December 31, the extra hours will be done the same year. 26 | - `Date.getDay()` method returns the day of the week of a date. 0 is Sunday, 1 is Monday, etc. 27 | -------------------------------------------------------------------------------- /2023/challenge-18/challenge-18.ts: -------------------------------------------------------------------------------- 1 | export function drawClock(time: string) { 2 | const clock: string[][] = [[], [], [], [], [], [], []] 3 | 4 | const full = ['*', '*', '*'] 5 | const open = ['*', ' ', '*'] 6 | const start = ['*', ' ', ' '] 7 | const end = [' ', ' ', '*'] 8 | const spaces = [' ', ' ', ' '] 9 | const middle = [' ', '*', ' '] 10 | 11 | const numbers: Record = { 12 | 0: [full, open, open, open, open, open, full], 13 | 1: [end, end, end, end, end, end, end], 14 | 2: [full, end, end, full, start, start, full], 15 | 3: [full, end, end, full, end, end, full], 16 | 4: [open, open, open, full, end, end, end], 17 | 5: [full, start, start, full, end, end, full], 18 | 6: [full, start, start, full, open, open, full], 19 | 7: [full, end, end, end, end, end, end], 20 | 8: [full, open, open, full, open, open, full], 21 | 9: [full, open, open, full, end, end, full], 22 | } 23 | 24 | const colon = [spaces, spaces, middle, spaces, middle, spaces, spaces] 25 | 26 | const one = numbers[time[0]] 27 | const two = numbers[time[1]] 28 | const four = numbers[time[3]] 29 | const five = numbers[time[4]] 30 | 31 | let i = 0 32 | 33 | for (const item of clock) { 34 | item.push( 35 | ...one[i], 36 | ' ', 37 | ...two[i], 38 | ...colon[i], 39 | ...four[i], 40 | ' ', 41 | ...five[i], 42 | ) 43 | 44 | i++ 45 | } 46 | 47 | return clock 48 | } 49 | -------------------------------------------------------------------------------- /2021/challenge-09/challenge-09.test.ts: -------------------------------------------------------------------------------- 1 | import { describe } from 'vitest' 2 | import { groupBy } from './challenge-09' 3 | 4 | const TEST_CASES: TestCases< 5 | [any[], string | ((value: any) => string | number)], 6 | Record 7 | > = [ 8 | { 9 | args: [[6.1, 4.2, 6.3], Math.floor], 10 | expected: { 6: [6.1, 6.3], 4: [4.2] }, 11 | }, 12 | { 13 | args: [['one', 'two', 'three'], 'length'], 14 | expected: { 3: ['one', 'two'], 5: ['three'] }, 15 | }, 16 | { 17 | args: [[{ age: 23 }, { age: 24 }], 'age'], 18 | expected: { 23: [{ age: 23 }], 24: [{ age: 24 }] }, 19 | }, 20 | { 21 | args: [ 22 | [1397639141184, 1363223700000], 23 | timestamp => new Date(timestamp).getFullYear(), 24 | ], 25 | expected: { 2013: [1363223700000], 2014: [1397639141184] }, 26 | }, 27 | { 28 | args: [ 29 | [ 30 | { title: 'JavaScript: The Good Parts', rating: 8 }, 31 | { title: 'Aprendiendo Git', rating: 10 }, 32 | { title: 'Clean Code', rating: 9 }, 33 | ], 34 | 'rating', 35 | ], 36 | expected: { 37 | 8: [{ title: 'JavaScript: The Good Parts', rating: 8 }], 38 | 9: [{ title: 'Clean Code', rating: 9 }], 39 | 10: [{ title: 'Aprendiendo Git', rating: 10 }], 40 | }, 41 | }, 42 | ] 43 | 44 | describe('Challenge #9: Agrupando cosas automáticamente', () => { 45 | buildChallengeTestCases({ 46 | cases: TEST_CASES, 47 | spreadFn: groupBy, 48 | }) 49 | }) 50 | -------------------------------------------------------------------------------- /2021/challenge-24/README.md: -------------------------------------------------------------------------------- 1 | # Challenge #24: Comparando árboles de Navidad 2 | 3 | > El abuelo y la abuela llevan discutiendo todo el día que si los árboles de Navidad son todos iguales... ¡Salgamos de dudas! 4 | 5 | El abuelo 👴 dice que ve todos los árboles de navidad iguales... La abuela 👵, en cambio, piensa que no. Que todos los árboles de navidad son distintos... 6 | 7 | Vamos a hacer una función que nos diga si dos árboles de navidad son iguales. Para ello, vamos a comparar [los árboles que ya creamos en el reto 22](https://2021.adventjs.dev/challenges/24). 8 | 9 | Tenemos que ver si ambos árboles tienen la misma estructura y los mismos valores en todas las ramas. Aquí tienes unos ejemplos: 10 | 11 | ```js 12 | const tree = { 13 | value: 1, 14 | left: { value: 2, left: null, right: null }, 15 | right: { value: 3, left: null, right: null }, 16 | } 17 | 18 | checkIsSameTree(tree, tree) // true 19 | 20 | const tree2 = { 21 | value: 1, 22 | left: { value: 3, left: { value: 2, left: null, right: null }, right: null }, 23 | right: { value: 5, left: null, right: { value: 4, left: null, right: null } }, 24 | } 25 | 26 | checkIsSameTree(tree, tree2) // false 27 | checkIsSameTree(tree2, tree2) // true 28 | ``` 29 | 30 | El cuñado 🦹‍♂️, que se las sabe todas, me ha dicho que tenga cuidado porque **el truco del JSON.stringify puede no funcionar...** ya que los árboles pueden ser el mismo pero el orden de representación de las ramas izquierda y derecha puede ser inversa... 31 | -------------------------------------------------------------------------------- /2022/challenge-24/README.md: -------------------------------------------------------------------------------- 1 | # Challenge #24: The last challenge is a maze 2 | 3 | It's the day! Today we're going to deliver gifts… but the warehouses are a maze and the elves are lost. 4 | 5 | **Indielfo Jones** wants to **write a program** that, upon receiving a **matrix**, knows if it can quickly exit the maze from its entrance to the exit. 6 | 7 | Mazes have the following positions: 8 | 9 | `W`: It's a wall, you can't pass through there. `S`: Entry point to the warehouse. `E`: Exit point from the warehouse. `\s`: White spaces are where you can pass through. 10 | 11 | Valid movements are from one position up, down, left, or right. You can't move diagonally. 12 | 13 | ```js 14 | canExit([ 15 | [' ', ' ', 'W', ' ', 'S'], 16 | [' ', ' ', ' ', ' ', ' '], 17 | [' ', ' ', ' ', 'W', ' '], 18 | ['W', 'W', ' ', 'W', 'W'], 19 | [' ', ' ', ' ', ' ', 'E'], 20 | ]) // -> true 21 | 22 | // You can exit because you start at [0, 4] 23 | // and there's a path to the exit which is [4, 4] 24 | 25 | canExit([ 26 | [' ', ' ', 'W', 'W', 'S'], 27 | [' ', ' ', ' ', 'W', ' '], 28 | [' ', ' ', ' ', 'W', ' '], 29 | ['W', 'W', ' ', 'W', 'W'], 30 | [' ', ' ', ' ', ' ', 'E'], 31 | ]) // -> false 32 | 33 | // There's no way to get from [0, 4] to [4, 4] 34 | ``` 35 | 36 | **Remember that:** 37 | 38 | - You only have to return whether you can exit the maze with a boolean. 39 | - You can't jump over W walls. 40 | - You can't exit the limits of the matrix, you always have to follow an internal path. 41 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /2021/challenge-08/README.md: -------------------------------------------------------------------------------- 1 | # Challenge #8: La locura de las criptomonedas 2 | 3 | > Hemos invertido en criptomonedas... Y el otro día se pusieron todos los valores en rojo. En lugar de asustarnos, vamos a ver si podemos optimizar nuevas inversiones. 4 | 5 | Invertir en criptomonedas es casi un deporte de riesgo. El otro día hackearon Bitmart y ha hecho que el valor de Bitcoin, y otras monedas, bajase un 25%. 6 | 7 | Vamos a escribir una función que reciba la lista de precios de una criptomoneda en un día y debemos devolver la ganancia máxima que podríamos sacar si compramos y vendemos la inversión el mismo día. 8 | 9 | La lista de precios es un array de números y representa el tiempo de izquierda a derecha. Por lo que ten en cuenta que **no puedes comprar a un precio que esté a la derecha de la venta y no puedes vender a un precio que esté a la izquierda de la compra**. 10 | 11 | Por ejemplo: 12 | 13 | ```js 14 | const pricesBtc = [39, 18, 29, 25, 34, 32, 5] 15 | maxProfit(pricesBtc) // -> 16 (compra a 18, vende a 34) 16 | 17 | const pricesEth = [10, 20, 30, 40, 50, 60, 70] 18 | maxProfit(pricesEth) // -> 60 (compra a 10, vende a 70) 19 | ``` 20 | 21 | **Si ese día no se puede sacar ningún beneficio**, tenemos que devolver `-1` para evitar que hagamos una locura: 22 | 23 | ```js 24 | const pricesDoge = [18, 15, 12, 11, 9, 7] 25 | maxProfit(pricesDoge) = // -> -1 (no hay ganancia posible) 26 | 27 | const pricesAda = [3, 3, 3, 3, 3] 28 | maxProfit(pricesAda) = // -> -1 (no hay ganancia posible) 29 | ``` 30 | -------------------------------------------------------------------------------- /2021/challenge-07/README.md: -------------------------------------------------------------------------------- 1 | # Challenge #7: Buscando en el almacén... 2 | 3 | > Tenemos un amigo que trabaja en una tienda y no es capaz de encontrar en el almacén los productos que tiene... ¿Le ayudamos? 4 | 5 | Mi amigo Dani está trabajando en una tienda y con la llegada de las navidades tiene el almacén hecho un desastre y no encuentra nada. 6 | 7 | Vamos a crear una función `contains` que recibe dos parámetros: un objeto que define el almacén y el producto que buscamos. 8 | 9 | La función debe devolver un booleano que indique si se encuentra el string como valor en algún nivel del objeto. Veamos unos ejemplos: 10 | 11 | ```js 12 | const almacen = { 13 | estanteria1: { 14 | cajon1: { 15 | producto1: 'coca-cola', 16 | producto2: 'fanta', 17 | producto3: 'sprite', 18 | }, 19 | }, 20 | estanteria2: { 21 | cajon1: 'vacio', 22 | cajon2: { 23 | producto1: 'pantalones', 24 | producto2: 'camiseta', // <- ¡Está aquí! 25 | }, 26 | }, 27 | } 28 | 29 | contains(almacen, 'camiseta') // true 30 | 31 | const otroAlmacen = { 32 | baul: { 33 | fondo: { 34 | objeto: 'cd-rom', 35 | 'otro-objeto': 'disquette', 36 | 'otra-cosa': 'mando', 37 | }, 38 | }, 39 | } 40 | 41 | contains(otroAlmacen, 'gameboy') // false 42 | ``` 43 | 44 | Ten en cuenta que la tienda es enorme. Tiene diferentes almacenes y, como has visto en los ejemplos, cada uno puede tener diferentes organizaciones. **Lo importante es buscar que el producto está en los almacenes**. 45 | -------------------------------------------------------------------------------- /2023/challenge-15/challenge-15.test.ts: -------------------------------------------------------------------------------- 1 | import { describe } from 'vitest' 2 | import { autonomousDrive } from './challenge-15' 3 | 4 | const TEST_CASES: TestCases<[string[], string[]], string[]> = [ 5 | { args: [['..!....'], ['R', 'L']], expected: ['..!....'] }, 6 | { 7 | args: [ 8 | ['!..', '***'], 9 | ['U', 'L'], 10 | ], 11 | expected: ['!..', '***'], 12 | }, 13 | { 14 | args: [ 15 | ['!..', '***'], 16 | ['R', 'L'], 17 | ], 18 | expected: ['!..', '***'], 19 | }, 20 | { 21 | args: [ 22 | ['..!....', '......*'], 23 | ['R', 'D', 'L'], 24 | ], 25 | expected: ['.......', '..!...*'], 26 | }, 27 | { 28 | args: [ 29 | ['*..!..*', '*.....*'], 30 | ['R', 'R', 'R', 'D', 'D'], 31 | ], 32 | expected: ['*.....*', '*....!*'], 33 | }, 34 | { 35 | args: [ 36 | ['***', '.!.', '***'], 37 | ['D', 'U', 'R', 'R', 'R'], 38 | ], 39 | expected: ['***', '..!', '***'], 40 | }, 41 | { 42 | args: [ 43 | ['***', '*!*', '***'], 44 | ['D', 'U', 'R', 'L'], 45 | ], 46 | expected: ['***', '*!*', '***'], 47 | }, 48 | { 49 | args: [ 50 | ['.**.*.*.', '.***....', '..!.....'], 51 | ['D', 'U', 'R', 'R', 'R'], 52 | ], 53 | expected: ['.**.*.*.', '.***....', '.....!..'], 54 | }, 55 | ] 56 | 57 | describe('Challenge #15: Autonomous robot', () => { 58 | buildChallengeTestCases({ 59 | cases: TEST_CASES, 60 | spreadFn: autonomousDrive, 61 | }) 62 | }) 63 | -------------------------------------------------------------------------------- /2022/challenge-06/README.md: -------------------------------------------------------------------------------- 1 | # Challenge #6: Creating xmas decorations 2 | 3 | A couple of Christmas enthusiasts have created a Christmas decoration company. The first decoration they want to manufacture is a cube that is placed on the trees. 4 | 5 | The problem is that they have to program the machine and they don't know how to do it. They have asked us for help to achieve it. 6 | 7 | To **create the cubes**, a number with the desired size is passed to the program and it returns a `string` with the design of that size. For example, if we pass a 3, the program must return a cube of 3x3x3: 8 | 9 | ```js 10 | const cube = createCube(3) 11 | 12 | // output: 13 | /\_\_\_\ 14 | /\/\_\_\_\ 15 | /\/\/\_\_\_\ 16 | \/\/\/_/_/_/ 17 | \/\/_/_/_/ 18 | \/_/_/_/ 19 | ``` 20 | 21 | As you can see, the cube has three faces visually. The symbols used to build the cube faces are: `/`, `\`, `_`. In order to make the cube, some spaces are needed. Also, each line is separated by a new line character `\n`. 22 | 23 | Other examples of cubes: 24 | 25 | ```js 26 | const cubeOfOne = createCube(1) 27 | 28 | // output: 29 | /\_\ 30 | \/_/ 31 | ``` 32 | 33 | ```js 34 | const cubeOfTwo = createCube(2) 35 | 36 | // output: 37 | /\_\_\ 38 | /\/\_\_\ 39 | \/\/_/_/ 40 | \/_/_/ 41 | ``` 42 | 43 | Take into account: 44 | 45 | - Pay attention to the spaces in the cube. 46 | - The cube has to be symmetrical. 47 | - Make sure you use the correct symbols. 48 | - Each line must be separated by a new line character `\n` except for the last one. 49 | -------------------------------------------------------------------------------- /2023/challenge-17/challenge-17.test.ts: -------------------------------------------------------------------------------- 1 | import { describe } from 'vitest' 2 | import { optimizeIntervals } from './challenge-17' 3 | 4 | const TEST_CASES: TestCases< 5 | Array<[number, number]>, 6 | Array<[number, number]> 7 | > = [ 8 | { 9 | args: [ 10 | [2, 7], 11 | [3, 4], 12 | [5, 8], 13 | ], 14 | expected: [[2, 8]], 15 | }, 16 | { 17 | args: [ 18 | [3, 4], 19 | [5, 8], 20 | [2, 7], 21 | ], 22 | expected: [[2, 8]], 23 | }, 24 | { 25 | args: [ 26 | [1, 3], 27 | [2, 6], 28 | [8, 10], 29 | ], 30 | expected: [ 31 | [1, 6], 32 | [8, 10], 33 | ], 34 | }, 35 | { 36 | args: [ 37 | [1, 2], 38 | [3, 4], 39 | [5, 6], 40 | ], 41 | expected: [ 42 | [1, 2], 43 | [3, 4], 44 | [5, 6], 45 | ], 46 | }, 47 | { 48 | args: [ 49 | [5, 7], 50 | [6, 8], 51 | ], 52 | expected: [[5, 8]], 53 | }, 54 | { 55 | args: [ 56 | [1, 5], 57 | [6, 10], 58 | [11, 15], 59 | [16, 20], 60 | ], 61 | expected: [ 62 | [1, 5], 63 | [6, 10], 64 | [11, 15], 65 | [16, 20], 66 | ], 67 | }, 68 | { 69 | args: [ 70 | [1, 15], 71 | [8, 12], 72 | [4, 7], 73 | ], 74 | expected: [[1, 15]], 75 | }, 76 | ] 77 | 78 | describe('Challenge #17: Optimizing the rental', () => { 79 | buildChallengeTestCases({ 80 | cases: TEST_CASES, 81 | fn: optimizeIntervals, 82 | }) 83 | }) 84 | -------------------------------------------------------------------------------- /2022/challenge-13/README.md: -------------------------------------------------------------------------------- 1 | # Challenge #13: Backups for Santa Claus files 2 | 3 | To avoid losing data when the server crashes, Santa Claus has decided to make incremental backups. A hacker called S4vitelf is helping him. 4 | 5 | On one hand, we have the **timestamp** of when the last backup was made. 6 | 7 | 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. 8 | 9 | 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 and **sorted in ascending order**. Example: 10 | 11 | ```js 12 | const lastBackup = 1546300800 13 | 14 | const changes = [ 15 | [3, 1546301100], 16 | [2, 1546300800], 17 | [1, 1546300800], 18 | [1, 1546300900], 19 | [1, 1546301000], 20 | ] 21 | 22 | getFilesToBackup(lastBackup, changes) // => [ 1, 3 ] 23 | // The file with id 1 has been modified twice 24 | // after the last backup. 25 | 26 | // The file with id 2 has not been modified after 27 | // the last backup. 28 | 29 | // The file with id 3 has been modified once 30 | // after the last backup. 31 | 32 | // We have to make a backup 33 | // of the files 1 and 3. 34 | ``` 35 | 36 | Remember that: 37 | 38 | - Returns the id of the files that have been modified after the last backup. 39 | - Returns an empty array if there are no files to make backup. 40 | - Remember to sort ids in ascending order. 41 | -------------------------------------------------------------------------------- /2021/challenge-10/README.md: -------------------------------------------------------------------------------- 1 | # Challenge #10: La máquina de cambio 2 | 3 | > De cara a las ventas navideñas, vamos a automatizar el cambio de las monedas para que no se tenga que hacer manualmente. ¡Ganaremos tiempo! Pero primero, hay que programarlo. 4 | 5 | Para mejorar la productividad de la tienda en la que trabajamos, vamos a crear una pequeña máquina que calcula el mínimo número de monedas que debemos usar para dar el cambio de una compra en metálico. 6 | 7 | Las monedas para cambio que puedes usar son estas: 8 | 9 | ```js 10 | coins[0] = 1 céntimo 11 | coins[1] = 2 céntimos 12 | coins[2] = 5 céntimos 13 | coins[3] = 10 céntimos 14 | coins[4] = 20 céntimos 15 | coins[5] = 50 céntimos 16 | ``` 17 | 18 | Tenemos que crear una función que recibe el número de céntimos que hay que devolver al cliente y la función nos da un array con la **combinación de monedas mínimas** que debemos usar para conseguirlo. 19 | 20 | ```js 21 | getCoins(51) // [1, 0, 0, 0, 0, 1] -> una moneda de 1 céntimo y otra de 50 céntimos 22 | getCoins(3) // [1, 1, 0, 0, 0, 0] -> una moneda de 1 céntimo y otra de 2 23 | getCoins(5) // [0, 0, 1, 0, 0, 0] -> una moneda de 5 céntimos 24 | getCoins(16) // [1, 0, 1, 1, 0, 0] -> una moneda de 1 céntimo, una de 5 y una de 10 25 | getCoins(100) // [0, 0, 0, 0, 0, 2] -> dos monedas de 50 céntimos 26 | ``` 27 | 28 | La dificultad del reto está en saber utilizar correctamente una estructura que te permita conocer las monedas que tienes disponible para crear el array con la devolución, ya que **debes usar siempre el menor número de monedas posible**. ¡Suerte 👩‍💻👨‍💻!. 29 | -------------------------------------------------------------------------------- /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-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-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-16/README.md: -------------------------------------------------------------------------------- 1 | # Challenge #16: Friday deployment 2 | 3 | Yesterday, someone did a production deployment and the application for assembling Christmas trees broke. We've been asked to fix it as soon as possible. 4 | 5 | The problem is that the format of the trees has changed. **It's an array of numbers… but it should be an object!** For example, the tree: `[3, 1, 0, 8, 12, null, 1]` looks like this: 6 | 7 | ```js 8 | // 3 9 | // / \ 10 | // 1 0 11 | // / \ \ 12 | // 8 12 1 13 | ``` 14 | 15 | What we need is to transform the array into an object where each node of the tree has `value`, `left`, and `right` properties. 16 | 17 | For example, running your `transformTree` function with `[3, 1, 0, 8, 12, null, 1]` should return this: 18 | 19 | ```js 20 | { 21 | value: 3, 22 | left: { 23 | value: 1, 24 | left: { 25 | value: 8, 26 | left: null, 27 | right: null 28 | }, 29 | right: { 30 | value: 12, 31 | left: null, 32 | right: null 33 | } 34 | }, 35 | right: { 36 | value: 0, 37 | left: null, 38 | right: { 39 | value: 1, 40 | left: null, 41 | right: null 42 | } 43 | } 44 | } 45 | ``` 46 | 47 | The elf on duty who tried to solve the problem before going home, left us some clues: 48 | 49 | - **If a node doesn't have a value, it's represented with null. Therefore, if a node has a null value, it won't have any children.** 50 | - **The root node is at index 0 in the array.** 51 | - **There's a relationship between the index of a node and the index of its children. Look for the pattern!** 52 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /2022/challenge-06/challenge-06.test.ts: -------------------------------------------------------------------------------- 1 | import { describe } from 'vitest' 2 | import { createCube } from './challenge-06' 3 | 4 | const TEST_CASES: TestCases = [ 5 | { 6 | args: 3, 7 | expected: 8 | ' /\\_\\_\\_\\\n /\\/\\_\\_\\_\\\n/\\/\\/\\_\\_\\_\\\n\\/\\/\\/_/_/_/\n \\/\\/_/_/_/\n \\/_/_/_/', 9 | }, 10 | { args: 1, expected: '/\\_\\\n\\/_/' }, 11 | { args: 2, expected: ' /\\_\\_\\\n/\\/\\_\\_\\\n\\/\\/_/_/\n \\/_/_/' }, 12 | { 13 | args: 10, 14 | expected: 15 | ' /\\_\\_\\_\\_\\_\\_\\_\\_\\_\\_\\\n /\\/\\_\\_\\_\\_\\_\\_\\_\\_\\_\\_\\\n /\\/\\/\\_\\_\\_\\_\\_\\_\\_\\_\\_\\_\\\n /\\/\\/\\/\\_\\_\\_\\_\\_\\_\\_\\_\\_\\_\\\n /\\/\\/\\/\\/\\_\\_\\_\\_\\_\\_\\_\\_\\_\\_\\\n /\\/\\/\\/\\/\\/\\_\\_\\_\\_\\_\\_\\_\\_\\_\\_\\\n /\\/\\/\\/\\/\\/\\/\\_\\_\\_\\_\\_\\_\\_\\_\\_\\_\\\n /\\/\\/\\/\\/\\/\\/\\/\\_\\_\\_\\_\\_\\_\\_\\_\\_\\_\\\n /\\/\\/\\/\\/\\/\\/\\/\\/\\_\\_\\_\\_\\_\\_\\_\\_\\_\\_\\\n/\\/\\/\\/\\/\\/\\/\\/\\/\\/\\_\\_\\_\\_\\_\\_\\_\\_\\_\\_\\\n\\/\\/\\/\\/\\/\\/\\/\\/\\/\\/_/_/_/_/_/_/_/_/_/_/\n \\/\\/\\/\\/\\/\\/\\/\\/\\/_/_/_/_/_/_/_/_/_/_/\n \\/\\/\\/\\/\\/\\/\\/\\/_/_/_/_/_/_/_/_/_/_/\n \\/\\/\\/\\/\\/\\/\\/_/_/_/_/_/_/_/_/_/_/\n \\/\\/\\/\\/\\/\\/_/_/_/_/_/_/_/_/_/_/\n \\/\\/\\/\\/\\/_/_/_/_/_/_/_/_/_/_/\n \\/\\/\\/\\/_/_/_/_/_/_/_/_/_/_/\n \\/\\/\\/_/_/_/_/_/_/_/_/_/_/\n \\/\\/_/_/_/_/_/_/_/_/_/_/\n \\/_/_/_/_/_/_/_/_/_/_/', 16 | }, 17 | ] 18 | 19 | describe('Challenge #6: Creating xmas decorations', () => { 20 | buildChallengeTestCases({ 21 | cases: TEST_CASES, 22 | fn: createCube, 23 | }) 24 | }) 25 | -------------------------------------------------------------------------------- /2021/challenge-16/README.md: -------------------------------------------------------------------------------- 1 | # Challenge #16: Descifrando los números... 2 | 3 | > Hemos encontrado unas cartas misteriores que contienen un montón de símbolos. Por suerte tenemos el diccionario para decodificarlas. ¡Vamos a ver qué contienen! 4 | 5 | Lara Eloft ha encontrado unos restos élficos en una cueva, cerca del Círculo Polar Ártico, a 8 km al norte de Rovaniemi. 6 | 7 | Ahora se encuentra descifrando unas misteriosas cartas que contiene información sobre unos números que le puede hacer llegar al próximo objetivo. 8 | 9 | Lara tiene un documento que contiene una serie de números que pueden ser usados para descifrarlos: 10 | 11 | ```js 12 | Símbolo Valor 13 | . 1 14 | , 5 15 | : 10 16 | ; 50 17 | ! 100 18 | ``` 19 | 20 | Lara, además, ha notado una cosa. **Los símbolos se restan si están inmediatamente a la izquierda de otro mayor**. 😱 21 | 22 | Tenemos que crear una función que nos pasa una cadena de texto con símbolos y tenemos que transformarlo al número correcto. ¡Ojo! Si encuentras un símbolo que no entendemos, mejor que devolvamos un `NaN`: 23 | 24 | ```js 25 | decodeNumbers('...') // 3 26 | decodeNumbers('.,') // 4 (5 - 1) 27 | decodeNumbers(',.') // 6 (5 + 1) 28 | decodeNumbers(',...') // 8 (5 + 3) 29 | decodeNumbers('.........!') // 107 (1 + 1 + 1 + 1 + 1 + 1 + 1 - 1 + 100) 30 | decodeNumbers('.;') // 49 (50 - 1) 31 | decodeNumbers('..,') // 5 (-1 + 1 + 5) 32 | decodeNumbers('..,!') // 95 (1 - 1 - 5 + 100) 33 | decodeNumbers('.;!') // 49 (-1 -50 + 100) 34 | decodeNumbers('!!!') // 300 35 | decodeNumbers(';!') // 50 36 | decodeNumbers(';.W') // NaN 37 | ``` 38 | -------------------------------------------------------------------------------- /2021/challenge-18/README.md: -------------------------------------------------------------------------------- 1 | # Challenge #18: El sistema operativo de Santa Claus 2 | 3 | > Estamos programando un sistema operativo para los ordenadores del taller de Santa Claus... Tenemos que gestionar los nombres de los archivos para que no se repitan. 4 | 5 | Evelyn Belefzin 👩‍💻 está trabajando en **un sistema operativo** para ser usado en el taller de Santa Claus 🎅. 6 | 7 | Se ha dado cuenta que en el taller nadie le presta atención a los nombres de los ficheros y a veces intentan guardar el mismo fichero más de una vez... así que es importante que **gestionemos bien los nombres duplicados**. 8 | 9 | Tenemos que crear **una función que al pasarnos un array de nombres de archivo** devolvamos un array con el mismo número de elementos pero donde los nombres que se repetían se anexe al final `(k)` donde k sería el número de veces que se encontró repetido. 10 | 11 | Lo mejor es que veamos un ejemplo: 12 | 13 | ```js 14 | const files = ['photo', 'postcard', 'photo', 'photo', 'video'] 15 | fixFiles(files) // ['photo', 'postcard', 'photo(1)', 'photo(2)', 'video'] 16 | 17 | const files2 = ['file', 'file', 'file', 'game', 'game'] 18 | fixFiles(files2) = ['file', 'file(1)', 'file(2)', 'game', 'game(1)'] 19 | 20 | // ojo que los elfos ya tenían archivos con (1)... ¡y pueden estar repetidos! 21 | const files3 = ['file', 'file(1)', 'icon', 'icon(1)', 'icon(1)'] 22 | fixFiles(files3) // ['file', 'file(1)', 'icon', 'icon(1)', 'icon(1)(1)'] 23 | ``` 24 | 25 | Por cierto, **nos han dicho que son Agile y usan Scrum**. Por eso quieren saber cuánto tiempo vas a tardar para saber cuándo van a poder usarlo. Que hay prisa. 😝 Así que entra a Discord y cuéntanos. 26 | -------------------------------------------------------------------------------- /2021/challenge-05/README.md: -------------------------------------------------------------------------------- 1 | # Challenge #5: Contando los días para los regalos 2 | 3 | > ¡Qué ganas de abrir los regalos 🎁! Estoy tan nervioso que no paro de contar los días que faltan 🤣. ¿Me ayudas creando un programita? ¡Venga! 4 | 5 | Con la emoción, ya estamos empezando a **contar los días del calendario hasta el 25 de diciembre 📆**. 6 | 7 | Para ayudar a esto, vamos a crear una función que pasándole una instancia de `Date` nos diga el número de días que faltan. 8 | 9 | Veamos unos ejemplos: 10 | 11 | ```js 12 | const date1 = new Date('Dec 1, 2021') 13 | daysToXmas(date1) // 24 14 | const date2 = new Date('Dec 24, 2021 00:00:01') 15 | daysToXmas(date2) // 1 16 | const date3 = new Date('Dec 24, 2021 23:59:59') 17 | daysToXmas(date3) // 1 18 | const date4 = new Date('December 20, 2021 03:24:00') 19 | daysToXmas(date4) // 5 20 | ``` 21 | 22 | El resultado tiene que ser **un número entero** y, como ves, aunque falte un segundo hasta el siguiente día, se entiende que todavía falta un día. 23 | 24 | **¡Pero ojo!** También hay que indicar si la fecha es del mismo día (devolveríamos `0`) o si es una fecha futura (devolveríamos el número de días en negativo `-`): 25 | 26 | ```js 27 | const date = new Date('Dec 25, 2021') 28 | daysToXmas(date) // 0 29 | const date1 = new Date('Dec 26, 2021') 30 | daysToXmas(date1) // -1 31 | const date2 = new Date('Dec 31, 2021') 32 | daysToXmas(date2) // -6 33 | const date3 = new Date('Jan 1, 2022 00:00:01') 34 | daysToXmas(date3) // -7 35 | const date4 = new Date('Jan 1, 2022 23:59:59') 36 | daysToXmas(date4) // -7 37 | ``` 38 | 39 | Por cierto, la fecha de referencia para saber si es 25 de diciembre es `Dec 25, 2021`. 40 | -------------------------------------------------------------------------------- /2022/challenge-07/README.md: -------------------------------------------------------------------------------- 1 | # Challenge #7: Doing gifts inventory 2 | 3 | In the Santa Claus stores they are doing inventory. There are three stores (which is represented as an `Array` each). In each store there are gifts. 4 | 5 | We have been asked to write a program that tells us what gifts we have to buy to replenish our stores now that Christmas is approaching. **A gift must be replenished when there is only stock in one of the three stores**. 6 | 7 | For example, if we have the following stores: 8 | 9 | ```js 10 | const a1 = ['bike', 'car', 'bike', 'bike'] 11 | const a2 = ['car', 'bike', 'doll', 'car'] 12 | const a3 = ['bike', 'pc', 'pc'] 13 | 14 | /* The store a1 has "bike" and "car". 15 | The store a2 has "car", "bike" and "doll". 16 | The store a3 has "bike" and "pc". 17 | 18 | The gift "doll" and "pc" are only in the stores a2 and a3 respectively. 19 | */ 20 | 21 | const gifts = getGiftsToRefill(a1, a2, a3) // ['doll', 'pc'] 22 | ``` 23 | 24 | As you can see, the stores can have the same gift repeated several times. But, no matter how many existences there are in a store, if we do not have it in the other two, we must replenish it to have better distribution. 25 | 26 | 📝 To sum up 27 | 28 | - Create a function `getGiftsToRefill` that receives three `Array` as parameters. 29 | - The function must return an `Array` with the gifts to be replenished. 30 | - A gift must be replenished when there is only stock in one of the three stores. 31 | - If there is no gift to replenish, the function must return an empty `Array`. 32 | - If there is more than one gift to replenish, the function must return an `Array` with all the gifts that need to be replenished. 33 | -------------------------------------------------------------------------------- /2022/challenge-14/README.md: -------------------------------------------------------------------------------- 1 | # Challenge #14: The best path 2 | 3 | Santa Claus is building ice pyramids in the North Pole to train his reindeers. 4 | 5 | Each reindeer starts at the top of the pyramid and must choose the optimal path down to travel it **in the shortest time possible**. Each level has a number that determines the time it takes to get there. 6 | 7 | When arriving at a position, the reindeer **can only slide down and diagonally to the left or right**. Visually, the pyramid looks like this: 8 | 9 | ```js 10 | 0 11 | / \ 12 | 7 4 13 | / \ / \ 14 | 2 4 6 15 | ``` 16 | 17 | In code we represent it like this: 18 | 19 | ```js 20 | ;[[0], [7, 4], [2, 4, 6]] 21 | ``` 22 | 23 | Santa Claus needs a program that tells him what the minimum time each reindeer can take to slide down the pyramid using the optimal path. 24 | 25 | In the previous example, the optimal path is `0 -> 4 -> 4`, which takes a total of `0 + 4 + 4 = 8` units of time. The result would be: `8`. 26 | 27 | Why is it not `6`? It is not `0 -> 4 -> 2` because when you go down to the position of the `4` you can no longer diagonally reach the position of the `2`. So the shortest possible path is `8`. More examples: 28 | 29 | ```js 30 | getOptimalPath([[0], [2, 3]]) // 2 31 | 32 | getOptimalPath([[0], [3, 4], [9, 8, 1]]) // 5 33 | 34 | getOptimalPath([[1], [1, 5], [7, 5, 8], [9, 4, 1, 3]]) // 8 35 | ``` 36 | 37 | To keep in mind: 38 | 39 | - Keep in mind that you can only go down diagonally and down from any position. 40 | - The first position of the pyramid can be different from 0. 41 | - Pyramids can have up to 10 levels. 42 | - The numbers in the pyramids can be negative. 43 | -------------------------------------------------------------------------------- /2022/challenge-11/README.md: -------------------------------------------------------------------------------- 1 | # Challenge #11: Santa Claus is Scrum Master 2 | 3 | Santa Claus is a bit worried because the preparations are taking a long time. He has got a Scrum certification and has decided to use the Scrum methodology to organize the work of his elves. 4 | 5 | The elfs tell him the expected duration of the tasks with a string with the format `hh:mm:ss` and in the same format how long they have been working on it. 6 | 7 | But Santa Claus does not get quickly if there is too much or too little left to finish, so he has asked us to make a program that tells us what portion of the task has already been completed. 8 | 9 | For example, if the task lasts `03:00:00` and they have been working for `01:00:00` then they have already completed `1/3` of the task. In code: 10 | 11 | ```js 12 | getCompleted('01:00:00', '03:00:00') // '1/3' 13 | getCompleted('02:00:00', '04:00:00') // '1/2' 14 | getCompleted('01:00:00', '01:00:00') // '1/1' 15 | getCompleted('00:10:00', '01:00:00') // '1/6' 16 | getCompleted('01:10:10', '03:30:30') // '1/3' 17 | getCompleted('03:30:30', '05:50:50') // '3/5 18 | ``` 19 | 20 | Note: 21 | 22 | - The time format is `hh:mm:ss`. 23 | - The output format is a string `a/b` where `a` is the portion of the task that has already been completed and `b` is the portion of the task that is left to complete. 24 | - The portion is always shown with the smallest fraction possible. (for example, `2/4` will never be shown because it can be represented as `1/2`). 25 | - If the task has already been completed, the fraction would be `1/1`. 26 | - No elf has been mistreated during the execution of this challenge nor have they had to use Scrum for real. 27 | -------------------------------------------------------------------------------- /2022/challenge-15/README.md: -------------------------------------------------------------------------------- 1 | # Challenge #15: Decorating the Christmas tree 2 | 3 | A couple is putting up the Christmas tree. The boy loves Christmas decorations and wants it to be perfectly balanced. He has three types of decorations: 4 | 5 | - Colored balls: `B` 6 | - Small gifts: `R` 7 | - Pine cones: `P` 8 | 9 | The Christmas tree is a triangle that must be generated. They already have the base mounted, which would be the first row, and from there they have to **place the decorations upwards following a formula**. 10 | 11 | ```js 12 | Place on top : P R B P 13 | If below is : P P B P R P B R 14 | ``` 15 | 16 | The combinations are also reversed. For example, if below is `B P`, above is `R`. But it will also be `R` if below is `P B`. Also if below you have repeated the letter, above you use the same letter. For example: if below is `B B`, above is `B`. 17 | 18 | With these rules, we could see the tree that we would generate with the base `B P R P`: 19 | 20 | ```js 21 | R 22 | P B 23 | R B B 24 | B P R P 25 | ``` 26 | 27 | Write a program that receives the string `B P R P` and returns an array with the representation of the tree. 28 | 29 | ```js 30 | decorateTree('B P R P') 31 | // [ 32 | // 'R', 33 | // 'P B', 34 | // 'R B B', 35 | // 'B P R P' 36 | // ] 37 | 38 | decorateTree('B B') // ['B', 'B B'] 39 | ``` 40 | 41 | Keep in mind that: 42 | 43 | - The program always receives the text string that represents the base of the tree. 44 | - The tree must be generated completely, that is, the base and the rows that are generated from it, until the top. 45 | - You have to follow the formula to know which decoration to place in each position. 46 | -------------------------------------------------------------------------------- /2022/challenge-12/README.md: -------------------------------------------------------------------------------- 1 | # Challenge #12: Electric sleighs, wow! 2 | 3 | Santa Claus has new electric sleighs but he must control the electricity consumption because **he only has a 20W battery**. 4 | 5 | We are given **an array of sleighs, from worst to best**, with their respective consumptions. Each sleigh is an object with two properties: `name` and `consumption`. 6 | 7 | The `consumption` field is a number that represents the amount of watts (W) that **consumes** the sleigh for each **distance unit**. The `name` field is a string that represents the sleigh name. 8 | 9 | Create a program that returns the name of the best sleigh available that allows us to cover the distance. 10 | 11 | ```js 12 | const distance = 30 13 | const sleighs = [ 14 | { name: 'Gorusuke', consumption: 0.3 }, 15 | { name: 'Madeval', consumption: 0.5 }, 16 | { name: 'Lolivier', consumption: 0.7 }, 17 | { name: 'Hyuuh', consumption: 1 }, 18 | ] 19 | 20 | selectSleigh(distance, sleighs) // => "Madeval" 21 | 22 | // Gorusuke consumes 9W to cover 30 distance 23 | // Madeval consumes 15W to cover 30 distance 24 | // Lolivier consumes 21W to cover 30 distance 25 | // Hyuuh consumes 30W to cover 30 distance 26 | 27 | // The best sleigh to cover the distance is Madeval. 28 | 29 | // Gorusuke covers the distance but it's a worse sleigh 30 | // Lolivier and Hyuuh can't cover the distance with 20W. 31 | ``` 32 | 33 | Remember that: 34 | 35 | - The battery is always 20W. 36 | - The list of sleighs is ordered from worst to best sleigh. 37 | - You have to return the name of the best sleigh that allows us to cover the distance with the watts we have available. 38 | - If no sleigh can be used, then return `null`. 39 | -------------------------------------------------------------------------------- /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 | - **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]`** 11 | - **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)`** 12 | 13 | **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. 14 | 15 | Your task is to write a function `organizeGifts` that takes a string of gifts as an argument and returns a string representing the warehouse. 16 | 17 | ```js 18 | const result1 = organizeGifts('76a11b') 19 | console.log(result1) 20 | // `[a]{a}{a}(aaaaaa){b}(b)` 21 | 22 | /* Explanation: 23 | 24 | 76a: 76 gifts type 'a' would be packed in 7 boxes and 6 gifts 25 | would be left, resulting in 1 pallet [a] (for the first 5 boxes), 26 | 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 29 | would be left, resulting in 1 loose box {b} and a bag with 1 gift (b) 30 | ``` 31 | -------------------------------------------------------------------------------- /2021/challenge-15/README.md: -------------------------------------------------------------------------------- 1 | # Challenge #15: El salto perfecto 2 | 3 | > Estamos optimizando el trineo para que los saltos que da sean lo más óptimos posible. Un amigo que tiene un Tesla nos ha explicado la mejor forma. ¡A ver si sacamos una función para aseguarnos! 4 | 5 | ¡Estamos haciendo los últimos ajustes para el trineo de Santa Claus! 6 | 7 | Como ya sabes, el trineo es volador y estamos ajustando el motor para que haga parabolas lo más óptimas posibles. Para esto el salto debe ser siempre hacia arriba y, a partir del punto más alto, debe bajar siempre hacia abajo... 8 | 9 | Nuestro mecánico de confianza, **Kiko Belfs**, que tiene un Tesla genial, nos ha explicado que los saltos se pueden ver como arrays... y que sólo tenemos que asegurarnos que **los números suben y bajan de forma correcta**. También nos avisa que sólo pasaremos **arrays de, como mínimo, tres posiciones**. 10 | 11 | Nos ha pasado algunos ejemplos de cómo debería ser nuestra función y algunos resultados: 12 | 13 | ```js 14 | checkSledJump([1, 2, 3, 2, 1]) // true: sube y baja de forma estricta 15 | checkSledJump([0, 1, 0]) // -> true: sube y baja de forma estricta 16 | checkSledJump([0, 3, 2, 1]) // -> true: sube y baja de forma estricta 17 | checkSledJump([0, 1000, 1]) // -> true: sube y baja de forma estricta 18 | 19 | checkSledJump([2, 4, 4, 6, 2]) // false: no sube de forma estricta 20 | checkSledJump([1, 2, 3]) // false: sólo sube 21 | checkSledJump([1, 2, 3, 2, 1, 2, 3]) // false: sube y baja y sube... ¡no vale! 22 | ``` 23 | 24 | **Lo importante**: recorrer el array de izquierda a derecha para ver que la subida es siempre estricta, detectar el punto más alto y entonces ver que la bajada es estricta hacia abajo... 25 | -------------------------------------------------------------------------------- /2023/challenge-12/challenge-12.test.ts: -------------------------------------------------------------------------------- 1 | import { describe } from 'vitest' 2 | import { checkIsValidCopy } from './challenge-12' 3 | 4 | const TEST_CASES: TestCases<[string, string], boolean> = [ 5 | { args: ['Santa Claus is coming', 'sa#ta cl#us is comin#'], expected: true }, 6 | { args: ['Santa Claus is coming', 'p#nt: cla#s #s c+min#'], expected: false }, 7 | { args: ['Santa Claus', ' Santa Claus '], expected: false }, 8 | { args: ['Santa Claus', '###:. c:+##'], expected: true }, 9 | { args: ['Santa Claus', 'sant##claus+'], expected: false }, 10 | { args: ['S#n:a Claus', 'S#+:. c:. s'], expected: true }, 11 | { args: ['Santa Claus', 's#+:. c:. s'], expected: true }, 12 | { args: ['s+#:.#c:. s', 's#+:.#c:. s'], expected: false }, 13 | { args: ['S#nta Claus', 'S#ntA ClauS'], expected: false }, 14 | { args: ['Santa Claus', 's#+:.#c:. s'], expected: false }, 15 | { args: ['Santa Claus', 'SantA ClauS'], expected: false }, 16 | { args: ['3 #egalos', '3 .+:# #:'], expected: true }, 17 | { args: ['3 regalos', '3 .+:# #:'], expected: true }, 18 | { args: ['3 regalos', '3 '], expected: true }, 19 | { args: ['3 regalos 3', '3 .+:# #: 3'], expected: true }, 20 | { 21 | args: [ 22 | 'Santa Claus viene a buscarte para darte muchos regalos y eso es espectacular porque da mucha felicidad a todos los niños', 23 | 'Santa Claus viene a buscarte para darte muchos regalos y eso es espectacular porque da mucha felicidad a todos los niño', 24 | ], 25 | expected: false, 26 | }, 27 | ] 28 | 29 | describe('Challenge #12: Is it a valid copy?', () => { 30 | buildChallengeTestCases({ 31 | cases: TEST_CASES, 32 | spreadFn: checkIsValidCopy, 33 | }) 34 | }) 35 | -------------------------------------------------------------------------------- /2023/challenge-23/README.md: -------------------------------------------------------------------------------- 1 | # Challenge #23: Christmas dinner 2 | 3 | Santa 🎅 is organizing a big Christmas dinner 🫓 and wants to ensure that all dishes are **unique and varied**! 4 | 5 | He gives you a list of Christmas dishes where each element consists of a list of strings that start with the name of the dish, followed by all the ingredients used for its preparation. 6 | 7 | You have to write a **function that groups the dishes by ingredients whenever there are at least 2 dishes that contain them**. 8 | 9 | So we return an array of arrays where the first position is the name of the ingredient and the rest are the names of the dishes. 10 | 11 | Both the list of ingredients and the dishes should be **alphabetically ordered**. 12 | 13 | ```js 14 | const dishes = [ 15 | ['christmas turkey', 'turkey', 'sauce', 'herbs'], 16 | ['cake', 'flour', 'sugar', 'egg'], 17 | ['hot chocolate', 'chocolate', 'milk', 'sugar'], 18 | ['pizza', 'sauce', 'tomato', 'cheese', 'ham'], 19 | ] 20 | 21 | organizeChristmasDinner(dishes) 22 | 23 | /* 24 | 25 | "sauce" is in 2 dishes: "christmas turkey" and "pizza". 26 | "sugar" is in 2 dishes: "cake" and "hot chocolate". 27 | The rest of the ingredients only appear in one dish, so we do not show them. 28 | 29 | We show "sauce" first because alphabetically it comes before "sugar". 30 | And the dishes of each ingredient are also alphabetically ordered. 31 | 32 | [ 33 | ["sauce", "christmas turkey", "pizza"], 34 | ["sugar", "cake", "hot chocolate"] 35 | ] 36 | */ 37 | ``` 38 | 39 | Keep in mind that: 40 | 41 | - **All the names of the dishes are different.** 42 | - **The names of the ingredients for a given dish are different from each other.** 43 | - **If there are no repeated ingredients, we return an empty array.** 44 | -------------------------------------------------------------------------------- /2023/challenge-05/README.md: -------------------------------------------------------------------------------- 1 | # Challenge #5: Santa's CyberTruck 2 | 3 | Santa 🎅 is testing his new electric sled, the CyberReindeer, on a North Pole road. The road is represented by a string of characters, where: 4 | 5 | - **`<` = Road** 6 | - **`S` = Santa's Sled** 7 | - **`*` = Open barrier** 8 | - **`|` = Closed barrier** 9 | 10 | Example of a road: `S...|....|.....` 11 | 12 | Each unit of time, **the sled moves one position to the right**. If it encounters a closed barrier, it stops until the barrier opens. If it is open, it goes through directly. 13 | 14 | **All barriers start closed**, but after 5 units of time, they all open **forever**. 15 | 16 | **Create a function that simulates the sled's movement** for a given time and **returns an array** of strings representing the state of the road at each unit of time: 17 | 18 | ```js 19 | const road = 'S..|...|..' 20 | const time = 10 // units of time 21 | const result = cyberReindeer(road, time) 22 | 23 | /* -> result: 24 | [ 25 | 'S..|...|..', // initial state 26 | '.S.|...|..', // sled advances on the road 27 | '..S|...|..', // sled advances on the road 28 | '..S|...|..', // sled stops at the barrier 29 | '..S|...|..', // sled stops at the barrier 30 | '...S...*..', // barrier opens, sled advances 31 | '...*S..*..', // sled advances on the road 32 | '...*.S.*..', // sled advances on the road 33 | '...*..S*..', // sled advances on the road 34 | '...*...S..', // passes through the open barrier 35 | ] 36 | */ 37 | ``` 38 | 39 | The result is an **array where each element shows the road at each unit of time.** 40 | 41 | Take into account that **if the sled is in the same position as a barrier**, then it takes its place in the array. 42 | 43 | The elves were **inspired by this Code Wars challenge.** 44 | -------------------------------------------------------------------------------- /2023/challenge-20/challenge-20.test.ts: -------------------------------------------------------------------------------- 1 | import { describe } from 'vitest' 2 | import { distributeGifts } from './challenge-20' 3 | 4 | const TEST_CASES: TestCases>, number[][]> = [ 5 | { 6 | args: [ 7 | [4, 5, 1], 8 | [6, null, 3], 9 | [8, null, 4], 10 | ], 11 | expected: [ 12 | [5, 3, 3], 13 | [6, 5, 3], 14 | [7, 6, 4], 15 | ], 16 | }, 17 | { 18 | args: [ 19 | [2, null], 20 | [null, 3], 21 | ], 22 | expected: [ 23 | [2, 3], 24 | [3, 3], 25 | ], 26 | }, 27 | { 28 | args: [ 29 | [2, 1, 1], 30 | [3, 4, null], 31 | ], 32 | expected: [ 33 | [2, 2, 1], 34 | [3, 3, 3], 35 | ], 36 | }, 37 | { 38 | args: [ 39 | [null, 5], 40 | [3, null], 41 | ], 42 | expected: [ 43 | [4, 5], 44 | [3, 4], 45 | ], 46 | }, 47 | { 48 | args: [ 49 | [1, 2, 3], 50 | [4, 5, 6], 51 | [7, 8, 9], 52 | ], 53 | expected: [ 54 | [2, 3, 4], 55 | [4, 5, 6], 56 | [6, 7, 8], 57 | ], 58 | }, 59 | { 60 | args: [ 61 | [null, 1, null, 1, null], 62 | [1, null, 1, null, 1], 63 | ], 64 | expected: [ 65 | [1, 1, 1, 1, 1], 66 | [1, 1, 1, 1, 1], 67 | ], 68 | }, 69 | { 70 | args: [ 71 | [0, 5], 72 | [5, null], 73 | ], 74 | expected: [ 75 | [3, 3], 76 | [3, 5], 77 | ], 78 | }, 79 | { 80 | args: [ 81 | [0, 5], 82 | [5, 0], 83 | ], 84 | expected: [ 85 | [3, 2], 86 | [2, 3], 87 | ], 88 | }, 89 | ] 90 | 91 | describe('Challenge #20: Distribute the weight', () => { 92 | buildChallengeTestCases({ 93 | cases: TEST_CASES, 94 | fn: distributeGifts, 95 | }) 96 | }) 97 | -------------------------------------------------------------------------------- /2021/challenge-25/README.md: -------------------------------------------------------------------------------- 1 | # Challenge #25: El último juego y hasta el año que viene 👋 2 | 3 | > Un ratón ha visto que en el comedor ha quedado un montón de comida 🥮 y ya está relamiéndose los bigotes por el festín que se va a pegar. 🐭 4 | 5 | Ayer, en noche buena, una família cenó por todo lo alto... Con tanta copa 🍾 encima todavía no han retirado los platos y la comida de ayer... 6 | 7 | Un ratoncillo llamado _midurat_ 🐭, que vió ayer el festín escondido, está relamiéndose los bigotes al ver todos los manjares que hay en el comedor. 8 | 9 | Eso sí, hay que tener cuidado 😶 y sólo hacer los movimientos correctos para comer algo. Por eso, el ratón, que se ha visto [los vídeos de midudev](https://midu.tube), va a crear una función para saber si su próximo movimiento es correcto o no ✅. 10 | 11 | El ratoncillo se puede mover en 4 direcciones: _up_, _down_, _left_, _right_ y el comedor es una matriz (un array de arrays) donde cada posición puede ser: 12 | 13 | - Un espacio vacío es que no hay nada 14 | - Una `m` es el ratón 15 | - Un `*` es la comida 16 | 17 | Vamos a ver unos ejemplos: 18 | 19 | ```js 20 | const room = [ 21 | [' ', ' ', ' '], 22 | [' ', ' ', 'm'], 23 | [' ', ' ', '*'], 24 | ] 25 | 26 | canMouseEat('up', room) // false 27 | canMouseEat('down', room) // true 28 | canMouseEat('right', room) // false 29 | canMouseEat('left', room) // false 30 | 31 | const room2 = [ 32 | ['*', ' ', ' ', ' '], 33 | [' ', 'm', '*', ' '], 34 | [' ', ' ', ' ', ' '], 35 | [' ', ' ', ' ', '*'], 36 | ] 37 | 38 | canMouseEat('up', room2) // false 39 | canMouseEat('down', room2) // false 40 | canMouseEat('right', room2) // true 41 | canMouseEat('left', room2) // false 42 | ``` 43 | 44 | ¡Ten en cuenta que el ratón quiere buscar comida en diferentes habitaciones y que cada una puede tener dimensiones diferentes! 45 | -------------------------------------------------------------------------------- /2021/challenge-09/README.md: -------------------------------------------------------------------------------- 1 | # Challenge #9: Agrupando cosas automáticamente 2 | 3 | > Tenemos un montón de cajas en la fábrica de regalos... y como no automaticemos de alguna forma ordenar este desastre... ¡Igual nos quedamos sin Navidad! 4 | 5 | En la fábrica de Papa Noél 🎅 se acerca el día especial... y todavía tenemos un montón de cosas por contar. 😅 6 | 7 | Por suerte a **Mark Zucktheelf** 🧝 se le ha ocurrido crear una función que permita agrupar un array, que puede ser de valores u objetos, a través de una función o de una propiedad. 8 | 9 | Nos trae un montón de **ejemplos**: 10 | 11 | ```js 12 | groupBy([6.1, 4.2, 6.3], Math.floor) // { 6: [6.1, 6.3], 4: [4.2] } 13 | groupBy(['one', 'two', 'three'], 'length') // { 3: ['one', 'two'], 5: ['three'] } 14 | groupBy([{ age: 23 }, { age: 24 }], 'age') // { 23: [{age: 23}], 24: [{age: 24}] } 15 | 16 | groupBy([1397639141184, 1363223700000], timestamp => 17 | new Date(timestamp).getFullYear(), 18 | ) 19 | // { 2013: [1363223700000], 2014: [1397639141184] } 20 | 21 | groupBy( 22 | [ 23 | { title: 'JavaScript: The Good Parts', rating: 8 }, 24 | { title: 'Aprendiendo Git', rating: 10 }, 25 | { title: 'Clean Code', rating: 9 }, 26 | ], 27 | 'rating', 28 | ) 29 | // { 8: [{ title: 'JavaScript: The Good Parts', rating: 8 }], 30 | // 9: [{ title: 'Clean Code', rating: 9 }], 31 | // 10: [{ title: 'Aprendiendo Git', rating: 10 }] } 32 | ``` 33 | 34 | Como ves, la función `groupBy` recibe una colección (array) y una función o una propiedad, y devuelve un objeto con claves que son los valores de la función ejecutada pasando como argumento cada elemento o de la propiedad por cada elemento. Luego los valores son un array de los valores que tengan la misma llave. 35 | 36 | La dificultad del reto está más en **comprender** la función que en la **implementación**. ¡Suerte!. 37 | -------------------------------------------------------------------------------- /2022/challenge-21/challenge-21.test.ts: -------------------------------------------------------------------------------- 1 | import { describe } from 'vitest' 2 | import { printTable, type Gift } from './challenge-21' 3 | 4 | const TEST_CASES: TestCases = [ 5 | { 6 | args: [ 7 | { name: 'PlayStation 5', quantity: 9234782374892 }, 8 | { name: 'Book Learn Web Dev', quantity: 23531 }, 9 | ], 10 | expected: 11 | '++++++++++++++++++++++++++++++++++++++\n| Gift | Quantity |\n| ------------------ | ------------- |\n| PlayStation 5 | 9234782374892 |\n| Book Learn Web Dev | 23531 |\n**************************************', 12 | }, 13 | { 14 | args: [ 15 | { name: 'Game', quantity: 2 }, 16 | { name: 'Bike', quantity: 1 }, 17 | { name: 'Book', quantity: 3 }, 18 | ], 19 | expected: 20 | '+++++++++++++++++++\n| Gift | Quantity |\n| ---- | -------- |\n| Game | 2 |\n| Bike | 1 |\n| Book | 3 |\n*******************', 21 | }, 22 | { 23 | args: [{ name: 'Game', quantity: 10000 }], 24 | expected: 25 | '+++++++++++++++++++\n| Gift | Quantity |\n| ---- | -------- |\n| Game | 10000 |\n*******************', 26 | }, 27 | { 28 | args: [{ name: 'Game', quantity: 1234567890 }], 29 | expected: 30 | '+++++++++++++++++++++\n| Gift | Quantity |\n| ---- | ---------- |\n| Game | 1234567890 |\n*********************', 31 | }, 32 | { 33 | args: [ 34 | { name: 'Toy', quantity: 12 }, 35 | { name: 'Mic', quantity: 123 }, 36 | ], 37 | expected: 38 | '+++++++++++++++++++\n| Gift | Quantity |\n| ---- | -------- |\n| Toy | 12 |\n| Mic | 123 |\n*******************', 39 | }, 40 | ] 41 | 42 | describe('Challenge #21: Creating the gifts table', () => { 43 | buildChallengeTestCases({ 44 | cases: TEST_CASES, 45 | fn: printTable, 46 | }) 47 | }) 48 | -------------------------------------------------------------------------------- /2022/challenge-05/README.md: -------------------------------------------------------------------------------- 1 | # Challenge #5: Optimizing Santa's trips 2 | 3 | To not tire the reindeer, Papa Noel wants to leave the maximum number of gifts by making the least number of trips possible. 4 | 5 | He has an array of cities where each element is the **number of gifts he can leave there**. For example, `[12, 3, 11, 5, 7]`. He also has a **limit on the number of gifts** that can fit in his bag, and finally, the **maximum number of cities** that his reindeer can visit. 6 | 7 | As he doesn't want to leave a city half-way, **if he can't leave all the gifts that are from that city, he doesn't leave any there**. 8 | 9 | Create a program that tells him the **highest sum of gifts that he could distribute**, taking into account the maximum number of gifts and the maximum number of cities he can visit. For example: 10 | 11 | ```js 12 | const giftsCities = [12, 3, 11, 5, 7] 13 | const maxGifts = 20 14 | const maxCities = 3 15 | 16 | // the highest sum of gifts to distribute 17 | // visiting a maximum of 3 cities 18 | // is 20: [12, 3, 5] 19 | 20 | // the highest sum would be [12, 7, 11] 21 | // but it exceeds the limit of 20 gifts and he 22 | // would have to leave a city half-way. 23 | 24 | getMaxGifts(giftsCities, maxGifts, maxCities) // 20 (12 + 3 + 5) 25 | ``` 26 | 27 | If it is not possible to make any trips that satisfies everything, the result should be `0`. More examples: 28 | 29 | ```js 30 | getMaxGifts([12, 3, 11, 5, 7], 20, 3) // 20 31 | getMaxGifts([50], 15, 1) // 0 32 | getMaxGifts([50], 100, 1) // 50 33 | getMaxGifts([50, 70], 100, 1) // 70 34 | getMaxGifts([50, 70, 30], 100, 2) // 100 35 | getMaxGifts([50, 70, 30], 100, 3) // 100 36 | getMaxGifts([50, 70, 30], 100, 4) // 100 37 | ``` 38 | 39 | To consider: 40 | 41 | - `maxGifts >= 1` 42 | - `giftsCities.length >= 1` 43 | - `maxCities >= 1` 44 | - The number of `maxCities` can be greater than `giftsCities.length` 45 | -------------------------------------------------------------------------------- /2021/challenge-23/README.md: -------------------------------------------------------------------------------- 1 | # Challenge #23: ¿Puedes reconfigurar las fábricas para no parar de crear regalos? 2 | 3 | > Santa Claus quiere las máquinas funcionando las 24 horas, como si esto fuera una rave de Pocholo. Chiki pun chiki pun. ¡Ayúdale a conseguirlo! 4 | 5 | Estamos en la fábrica de Santa Claus 🎅 creando regalos como si no hubiera un mañana 6 | 7 | Pensábamos que no íbamos a llegar pero **Jelf Bezos** ha tenido una idea genial para aprovechar las máquinas y optimizar al máximo la creación de regalos. 🎁 8 | 9 | La configuración de las máquinas es un **string**. Podemos reconfigurarla para que haga otro regalo y, para ello, podemos cambiar cada carácter por otro. 10 | 11 | Pero **tiene limitaciones** 🥲: al reemplazar el carácter se debe mantener el orden, no se puede asignar al mismo carácter a dos letras distintas (pero sí a si mismo) y, claro, la longitud del string debe ser el mismo. 12 | 13 | Necesitamos **una función que nos diga si podemos reconfigurar una máquina para que de un regalo pueda pasar a fabricar otro según las reglas** mencionadas. Lo mejor es que veamos un ejemplo: 14 | 15 | ```js 16 | const from = 'BAL' 17 | const to = 'LIB' 18 | const canReconfigure(from, to) // true 19 | /* la transformación sería así: 20 | B -> L 21 | A -> I 22 | L -> B 23 | */ 24 | 25 | const from = 'CON' 26 | const to = 'JUU' 27 | const canReconfigure(from, to) // false 28 | /* no se puede hacer la transformación: 29 | C -> J 30 | O -> U 31 | N -> FALLO 32 | */ 33 | 34 | const from = 'MMM' 35 | const to = 'MID' 36 | cons canReconfigure(from, to) // false 37 | /* no se puede hacer la transformación: 38 | M -> M (BIEN, asigna el mismo carácter a si mismo) 39 | M -> I (FALLO, asigna el mismo carácter a dos letras distintas) 40 | M -> D (FALLO, asigna el mismo carácter a dos letras distintas) 41 | */ 42 | 43 | const from = 'AA' 44 | const to = 'MID' 45 | cons canReconfigure(from, to) // false -> no tiene la misma longitud 46 | ``` 47 | -------------------------------------------------------------------------------- /2022/challenge-17/README.md: -------------------------------------------------------------------------------- 1 | # Challenge #17: Carrying gifts in bags 2 | 3 | We're preparing the Christmas gift bags but each bag has a **weight limit**. 4 | 5 | We are given an array with the names of the gifts and a number that is the maximum weight that each bag can carry. The weight of each gift is the length of its name. 6 | 7 | Write a function that groups the gifts in bags and returns an array with the names of the gifts of each bag. To group the gifts, the names are separated by spaces (the space does not count as weight). 8 | 9 | But keep in mind that if the weight of the gifts of a bag exceeds the maximum weight, the last gift of the bag must be separated and placed in the next bag. 10 | 11 | ```js 12 | carryGifts(['game', 'bike', 'book', 'toy'], 10) 13 | // ['game bike', 'book toy'] 14 | // in each bag you can carry 10kg 15 | // the first bag carries 'game' and 'bike' which weigh 4kg and 4kg 16 | // the second bag carries 'book' and ' toy' which weigh 4kg and 3kg 17 | 18 | carryGifts(['game', 'bike', 'book', 'toy'], 7) 19 | // ['game', 'bike', 'book toy'] 20 | // in each bag you can carry 7kg 21 | // the first bag can only carry 'game' which weighs 4kg 22 | // the second bag can only carry 'bike' which weighs 4kg 23 | 24 | carryGifts(['game', 'bike', 'book', 'toy'], 4) 25 | // ['game', 'bike', 'book', 'toy'] 26 | // in each bag you can carry 4kg 27 | // each bag can only carry one gift 28 | 29 | carryGifts(['toy', 'gamme', 'toy', 'bike'], 6) 30 | // ['toy', 'gamme', 'toy', 'bike'] 31 | // in each bag you can carry 6kg 32 | // each bag can only carry one gift 33 | // note that you can not carry 'toy toy' in a bag 34 | // because it is not next to each other 35 | ``` 36 | 37 | Note: 38 | 39 | - The gifts are always grouped in the order of appearance in the array. 40 | - You can not change the order of the gifts in the array when grouping them. 41 | - All the gifts can be grouped in a single bag. 42 | - If you can not group any gift in a bag, an empty array is returned. 43 | -------------------------------------------------------------------------------- /2021/challenge-12/README.md: -------------------------------------------------------------------------------- 1 | # Challenge #12: La ruta perfecta para dejar los regalos 2 | 3 | > En el taller de Santa ya están preparándolo todo para poder hacer la ruta perfecta para dejar los regalos. El problema es que hay unos obstáculos en el camino que debemos sortear... 4 | 5 | En el taller de Santa 🎅 se están preparando los trineos de motor eléctrico para poder hacer la ruta perfecta para dejar los regalos. 6 | 7 | **La ruta empieza en el punto 0 y de ahí va hacia la derecha en línea recta.** 8 | 9 | El Keanu Relfes 🧝 nos ha preparado una lista de obstáculos a evitar. El problema es que nos ha dado la **lista de posiciones de los obstáculos desordenada...** 😅 aunque al menos nunca **la posición 0 puede tener un obstáculo**. 10 | 11 | Encima, el trineo sólo se puede configurar para saltar un número fijo de posiciones... 😱 12 | 13 | Necesitamos una función que nos diga la longitud mínima del salto del trineo para ir evitando todos los obstáculos en la ruta. 14 | 15 | ```js 16 | const obstacles = [5, 3, 6, 7, 9] 17 | getMinJump(obstacles) // -> 4 18 | 19 | // S es salto, X es obstáculo 20 | /* Así quedaría la representación: 21 | 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 22 | . . . X . X X X . X . 23 | S-----------S-----------S------- 24 | */ 25 | 26 | const obstacles = [2, 4, 6, 8, 10] 27 | getMinJump(obstacles) // -> 7 28 | 29 | /* Así quedaría la representación: 30 | 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 31 | . . X . X . X . X . X 32 | S--------------------S--------- 33 | 34 | // Longitudes de salto: 35 | // 1 caería en el 2 36 | // 2 caería en el 2 37 | // 3 caería en el 6 38 | // 4 caería en el 4 39 | // 5 caería en el 10 40 | // 6 caería en el 6 41 | // 7 es el ideal!!! ✅ 42 | 43 | getMinJump([1, 2, 3, 5]) // -> 4 44 | getMinJump([3, 7, 5]) // -> 2 45 | getMinJump([9, 5, 1]) // -> 2 46 | */ 47 | ``` 48 | 49 | La dificultad del reto está en pensar que sólo podemos configurar el salto del trineo una vez y que buscamos el salto mínimo que nos serviría para sortear todos los obstaculos. 50 | -------------------------------------------------------------------------------- /2023/challenge-18/README.md: -------------------------------------------------------------------------------- 1 | # Challenge #18: The digital clock 2 | 3 | In the toy factory, **the elves are programming a digital clock** to keep them on schedule with gift production. However, they have encountered an interesting programming challenge. They need a function that, given a time in 'HH:MM' format, creates a visual representation of this time on a digital clock by returning **an array of arrays of characters**. 4 | 5 | **The clock screen has 7 rows and 17 columns**, and each digit of the time takes up 7 rows and 3 columns. The digits are composed of asterisks (`*`) and blank spaces (` `). There is an empty column between each digit. 6 | 7 | **The colon separating hours and minutes** is drawn using two asterisks (`*`) and is always placed in the same position, in rows 2 and 4, in column 9, respectively (note: row and column indexing starts at 0). 8 | 9 | For example, if the function receives `01:30`, it should return: 10 | 11 | 12 | ```js 13 | drawClock('01:30') // ⬇️ 14 | 15 | [ 16 | ['*', '*', '*', ' ', ' ', ' ', '*', ' ', ' ', ' ', '*', '*', '*', ' ', '*', '*', '*'], 17 | ['*', ' ', '*', ' ', ' ', ' ', '*', ' ', ' ', ' ', ' ', ' ', '*', ' ', '*', ' ', '*'], 18 | ['*', ' ', '*', ' ', ' ', ' ', '*', ' ', '*', ' ', ' ', ' ', '*', ' ', '*', ' ', '*'], 19 | ['*', ' ', '*', ' ', ' ', ' ', '*', ' ', ' ', ' ', '*', '*', '*', ' ', '*', ' ', '*'], 20 | ['*', ' ', '*', ' ', ' ', ' ', '*', ' ', '*', ' ', ' ', ' ', '*', ' ', '*', ' ', '*'], 21 | ['*', ' ', '*', ' ', ' ', ' ', '*', ' ', ' ', ' ', ' ', ' ', '*', ' ', '*', ' ', '*'], 22 | ['*', '*', '*', ' ', ' ', ' ', '*', ' ', ' ', ' ', '*', '*', '*', ' ', '*', '*', '*'] 23 | ] 24 | ``` 25 | 26 | To know how to draw each digit, we have been given the following image. As you can see, **each digit is composed of 7 rows and 3 columns**. We will represent the pixels in red with an asterisk (`*`), and the white pixels with a space (` `): 27 | 28 | ![Representation of the digits for the digital clock](https://adventjs.dev/digits.png) 29 | -------------------------------------------------------------------------------- /2021/challenge-22/README.md: -------------------------------------------------------------------------------- 1 | # Challenge #22: ¿Cuántos adornos necesita el árbol? 2 | 3 | > ¡Ay! Que llega la Navidad y no hemos decorado todavía el árbol. 🎄😱 ¡Tenemos que hacer algo lo antes posible! 4 | 5 | ¡Ay! Que llega la Navidad y no hemos decorado todavía el árbol. 🎄😱 6 | 7 | Necesitamos una función que pasándole un árbol binario nos diga el número de decoraciones que necesitamos. Para ello tenemos un objeto que sería la representación del árbol y que nos indica en cada nivel el número de ramas a decorar. 8 | 9 | Lo mejor es que veamos un ejemplo: 10 | 11 | ```js 12 | // tenemos el árbol en forma de objeto 13 | const tree = { 14 | value: 1, // el nodo raíz siempre es uno, porque es la estrella ⭐ 15 | left: { 16 | value: 2, // el nodo izquierdo necesita dos decoraciones 17 | left: null, // no tiene más ramas 18 | right: null, // no tiene más ramas 19 | }, 20 | right: { 21 | value: 3, // el nodo de la derecha necesita tres decoraciones 22 | left: null, // no tiene más ramas 23 | right: null, // no tiene más ramas 24 | }, 25 | } 26 | 27 | /* Gráficamente sería así: 28 | 1 29 | / \ 30 | 2 3 31 | 32 | 1 + 2 + 3 = 6 33 | */ 34 | 35 | countDecorations(tree) // 6 36 | 37 | const bigTree = { 38 | value: 1, 39 | left: { 40 | value: 5, 41 | left: { 42 | value: 7, 43 | left: { 44 | value: 3, 45 | left: null, 46 | right: null, 47 | }, 48 | right: null, 49 | }, 50 | right: null, 51 | }, 52 | right: { 53 | value: 6, 54 | left: { 55 | value: 5, 56 | left: null, 57 | right: null, 58 | }, 59 | right: { 60 | value: 1, 61 | left: null, 62 | right: null, 63 | }, 64 | }, 65 | } 66 | 67 | /* 68 | 1 69 | / \ 70 | 5 6 71 | / / \ 72 | 7 5 1 73 | / 74 | 3 75 | */ 76 | 77 | countDecorations(bigTree) // 28 78 | ``` 79 | 80 | Por cierto, Bellf Gates me ha contado que este tipo de ejercicio es muy típico en las entrevistas de trabajo para programadores. ¿Lo sabías? 81 | --------------------------------------------------------------------------------