Thursday 1 December 2022

Scrimba's JavaScriptmas and Advent of Code 2022

Day 1

JavaScriptmas

const panic = str => `${str.toUpperCase().split(' ').join(' 😱 ')}!`
console.log(panic("I'm almost out of coffee"))
console.log(panic("winter is coming"))

Advent of Code

const elves = input.split('\n\n').map(e => Number(e.split('\n').reduce((a, c) => Number(a) + Number(c))))
console.log(Math.max(...elves))
elves.sort((a, b) => b - a)
console.log(elves.slice(0, 3).reduce((a, c) => a + c))

Day 2

JavaScriptmas

const transformData = d => d.map(e => ({
    fullName: `${e.name.first} ${e.name.last}`,
    birthday: new Date(e.dob.date).toDateString()
}))

Advent of Code

/**
 * Rock:     A, X. Worth: 1. Beats: Scissors
 * Paper:    B, Y. Worth: 2. Beats: Rock
 * Scissors: C, Z. Worth: 3. Beats: Paper
 * 0 if you lost,
 * 3 if the round was a draw
 * and 6 if you won
 */ 

console.log(input.split('\n').reduce((a, c) => {
  const [them, me] = c.split(' ')
  if(them === 'A'){
    if(me === 'X'){
      a += 3 + 1
    }
    if(me === 'Y'){
      a += 6 + 2
    }
    if(me === 'Z'){
      a += 0 + 3
    }
  }
  if(them === 'B'){
    if(me === 'X'){
      a += 0 + 1
    }
    if(me === 'Y'){
      a += 3 + 2
    }
    if(me === 'Z'){
      a += 6 + 3
    }
  }
  if(them === 'C'){
    if(me === 'X'){
      a += 6 + 1
    }
    if(me === 'Y'){
      a += 0 + 2
    }
    if(me === 'Z'){
      a += 3 + 3
    }
  }
  return a
}, 0))

/**
 * Rock:     A, X. Worth: 1. Beats: Scissors
 * Paper:    B, Y. Worth: 2. Beats: Rock
 * Scissors: C, Z. Worth: 3. Beats: Paper
 * 0 if you lost,
 * 3 if the round was a draw
 * and 6 if you won
 * X: Lose
 * Y: Draw
 * Z: Win
 */ 

console.log(input.split('\n').reduce((a, c) => {
  const [them, me] = c.split(' ')
  if(them === 'A'){ // They've played Rock
    if(me === 'X'){ // I need to lose
      a += 0 + 3    // I play Scissors
    }
    if(me === 'Y'){ // I need to draw
      a += 3 + 1    // I play Rock
    }
    if(me === 'Z'){ // I need to win
      a += 6 + 2    // I play Paper
    }
  }
  if(them === 'B'){ // They've played Paper
    if(me === 'X'){ // I need to lose
      a += 0 + 1    // I play Rock
    }
    if(me === 'Y'){ // I need to draw
      a += 3 + 2    // I play Paper
    }
    if(me === 'Z'){ // I need to win
      a += 6 + 3    // I play Scissors
    }
  }
  if(them === 'C'){ // They've played Scissors
    if(me === 'X'){ // I need to lose
      a += 0 + 2    // I play Paper  
    }
    if(me === 'Y'){ // I need to draw
      a += 3 + 3    // I play Scissors  
    }
    if(me === 'Z'){ // I need to win
      a += 6 + 1    // I play Rock  
    }
  }
  return a
}, 0))

Day 3

JavaScriptmas

const faveFoods = {
    breakfast: 'croissants',
    lunch: 'pasta',
    supper: 'pizza'
}

const {breakfast, lunch, supper} = faveFoods

document.getElementById('meals').innerHTML = `
For breakfast, I only like ${breakfast}. For lunch, I love ${lunch}, and for supper I want usually want ${supper}.`

Advent of Code

// drop 96 for lowercase
// drop 38 for uppercase

const getCodeValue = letter => letter === letter.toLowerCase() 
  ? letter.charCodeAt(0) - 96
  : letter.charCodeAt(0) - 38

const getIntersection = (a, b) => {
  for(let i in a){
    if(b.includes(a[i])){
      return a[i]
    }
  }
}

const getIntersectionThree = (a, b, c) => {
  for(let i in a){
    if(b.includes(a[i]) && c.includes(a[i])){
      return a[i]
    }
  }
}

console.log(input.split('\n').reduce((a, c) => a + getCodeValue(getIntersection(...[c.slice(0, Math.ceil(c.length / 2)).split(''), c.slice(Math.ceil(c.length / 2)).split('')]))
, 0))

let total = 0
const lines = input.split('\n')
for (let i = 0; i < lines.length; i += 3) {
  const chunk = lines.slice(i, i + 3);
  total += getCodeValue(getIntersectionThree(...chunk))
}
console.log(total)

console.log(input.split('\n').reduce((p, _, i, a) => !(i%3) 
  ? p + getCodeValue(getIntersectionThree(...a.slice(i, i + 3))) 
  : p, 0))

Day 4

JavaScriptmas

const whisper = s => `shh... ${s.endsWith('!') 
  ? s.toLowerCase().slice(0, -1) 
  : s.toLowerCase()}`

Advent of Code

const range = (start, stop) => Array.from({ length: stop - start + 1 }, (_, i) => start + i)
const contains = (a, b) => a.every(e => b.includes(e)) || b.every(e => a.includes(e))
const overlaps = (a, b) => a.some(e => b.includes(e))
                                                                  
console.log(input.split('\n').reduce((a, line) => {
  const [firstArea, secondArea] = line.split(',').map(elf => range(...elf.split('-').map(e => Number(e))))
  a.contains += contains(firstArea, secondArea) ? 1 : 0
  a.overlaps += overlaps(firstArea, secondArea) ? 1 : 0
  return a
}, {
  contains: 0,
  overlaps: 0
}))

// let containedWithin = 0
// let overlaps = 0

// input.split('\n').forEach(line => {
//   const [firstArea, secondArea] = line.split(',').map(elf => range(...elf.split('-').map(e => Number(e))))
//   if(secondArea.every(e => firstArea.includes(e)) || firstArea.every(e => secondArea.includes(e))){
//     containedWithin += 1
//   }
//   if(secondArea.some(e => firstArea.includes(e))){
//     overlaps += 1
//   }
// })
// console.log(containedWithin, overlaps)

Day 5

JavaScriptmas

const getSaleItems = d => d.filter(e => e.type === "sweet").map(e => ({item: e.item, price: e.price}))

Advent of Code

const chunk = (str, size) => {
  const numChunks = Math.ceil(str.length / size)
  const chunks = new Array(numChunks)
  for (let i = 0, o = 0; i < numChunks; ++i, o += size) {
    chunks[i] = str.substr(o, size)
  }
  return chunks
}

const [initialState, instructions] = input.split('\n\n')
const initialStateLines = initialState.split('\n').reverse()
const positions = chunk(initialStateLines.shift(), 4).reduce((a, c) => {
  a[Number(c.trim())] = []
  return a
}, {})

initialStateLines.forEach(line => {
  chunk(line, 4).forEach((position, index) => {
    if (position.trim().length) {
      const { groups: { letter } } = /\s*\[(?<letter>[A-Z])\]\s*/.exec(position)
      positions[Object.keys(positions)[index]].push(letter)
    }
  })
})

/**
 * Part 1
 */
// instructions.split('\n').forEach(instruction => {
//   const {groups: {num, from, to}} = /move (?<num>[0-9]+) from (?<from>[0-9]+) to (?<to>[0-9]+)/.exec(instruction)
//   for(let i = 0; i < Number(num); i++){
//     positions[Number(to)].push(positions[Number(from)].pop())
//   }
// })

/**
 * Part 2
 */
instructions.split('\n').forEach(instruction => {
  const { groups: { num, from, to } } = /move (?<num>[0-9]+) from (?<from>[0-9]+) to (?<to>[0-9]+)/.exec(instruction)
  positions[Number(to)].push(...positions[Number(from)].slice(-Math.abs(Number(num))))
  positions[Number(from)].splice(positions[Number(from)].length - Number(num), Number(num) )
})

console.log(Object.keys(positions).reduce((a, c, i) => {
  return a + positions[c].at(-1)
}, ''))

Day 6

JavaScriptmas

const getRandomNumberOfTacos = () => new Array(Math.floor(Math.random() * (10 - 1) + 1)).fill('🌮')

Advent of Code

const test_input_1 = `mjqjpqmgbljsphdztnvjfqwrcgsmlb`
const test_input_2 = `bvwbjplbgvbhsrlpgdmjqwftvncz`
const test_input_3 = `nppdvjthqldpwncqszvftbrmjlhg`
const test_input_4 = `nznrnfrfntjfmvfwmzdfjlvtqnbhcprsg`
const test_input_5 = `zcfzfwzzqfrljwzlrfnpqdbhtmscgvjw`

const findPacket = stream => {
  for(let i = 3; i <= stream.length; i++){
    if(new Set(Array.from({length: 4}, (_, a) => stream.charAt(i - a))).size === 4){
      return i + 1
    }
  }
}

const findMessage = stream => {
  for(let i = 13; i <= stream.length; i++){
    if(new Set(Array.from({length: 14}, (_, a) => stream.charAt(i - a))).size === 14){
      return i + 1
    }
  }
}

console.log(findPacket(test_input_1))
console.log(findPacket(test_input_2))
console.log(findPacket(test_input_3))
console.log(findPacket(test_input_4))
console.log(findPacket(test_input_5))
console.log(findPacket(input))

console.log(findMessage(test_input_1))
console.log(findMessage(test_input_2))
console.log(findMessage(test_input_3))
console.log(findMessage(test_input_4))
console.log(findMessage(test_input_5))
console.log(findMessage(input))

Day 7

JavaScriptmas

const altCaps = str => str.split('').map((c, i) => i % 2 ? c : str.charCodeAt(i) > 96 || str.charCodeAt(i) < 123 ? c.toUpperCase() : c).join('')

Advent of Code

// import { Tree } from './Tree.js'
// import { TreeNode } from './TreeNode.js'

class TreeNode {
  constructor(key, value = key, parent = null) {
    this.key = key;
    this.value = value;
    this.parent = parent;
    this.children = [];
  }

  get isLeaf() {
    return this.children.length === 0;
  }

  get hasChildren() {
    return !this.isLeaf;
  }
}

class Tree {
  constructor(key, value = key) {
    this.root = new TreeNode(key, value);
  }

  *preOrderTraversal(node = this.root) {
    yield node;
    if (node.children.length) {
      for (let child of node.children) {
        yield* this.preOrderTraversal(child);
      }
    }
  }

  *postOrderTraversal(node = this.root) {
    if (node.children.length) {
      for (let child of node.children) {
        yield* this.postOrderTraversal(child);
      }
    }
    yield node;
  }

  insert(parentNodeKey, key, value = key) {
    for (let node of this.preOrderTraversal()) {
      if (node.key === parentNodeKey) {
        node.children.push(new TreeNode(key, value, node));
        return true;
      }
    }
    return false;
  }

  remove(key) {
    for (let node of this.preOrderTraversal()) {
      const filtered = node.children.filter(c => c.key !== key);
      if (filtered.length !== node.children.length) {
        node.children = filtered;
        return true;
      }
    }
    return false;
  }

  find(key) {
    for (let node of this.preOrderTraversal()) {
      if (node.key === key) return node;
    }
    return undefined;
  }
}

/*
 * https://www.30secondsofcode.org/articles/s/js-data-structures-tree
 */

addEventListener('DOMContentLoaded', async () => {
  //const res = await fetch("./test.txt")
  const res = await fetch("./input.txt")
  const text = await res.text()
  const input = text.split('\n')
  let target = null
  let tree = null
  let lineParts
  input.forEach((line, index) => {
    if (!index) {
      tree = new Tree('/', 0)
      target = tree.root
    } else {
      if (line.charAt(0) === '$') {
        lineParts = line.split(' ')
        if (lineParts.length === 3) {
          if (lineParts[2] === '..') {
            target = target.parent
          } else if (lineParts[2] === '/') {
            target = tree.root
          } else {
            target = target.children.find(ch => ch.key === lineParts[2])
          }
        }
      } else {
        lineParts = line.split(' ')
        if (lineParts[0] === 'dir') {
          target.children.push(new TreeNode(lineParts[1], 0, target))
        } else {
          const value = Number(lineParts[0])
          target.children.push(new TreeNode(lineParts[1], value, target))
          target.value += value
          let parent = target.parent
          while (parent) {
            parent.value += value
            parent = parent.parent
          }
        }
      }
    }
  })

  const dirs = [...tree.postOrderTraversal()].filter(x => x.hasChildren)
  console.log('Part 1:', dirs.reduce((a, c) => {
    if (c.value <= 100000) {
      a += c.value
    }
    return a
  }, 0))

  const spaceRequired = 30000000 - (70000000 - tree.root.value)
  console.log('Space required: ', spaceRequired)
  console.log('Part 2:', dirs.filter(dir => {
    if (dir.value >= spaceRequired) {
      console.log(dir.key, dir.value)
      return true
    }
    return false
  }).sort((a, b) => a.value - b.value)[0].value)
})

Day 8

JavaScriptmas

const validTime = str => {
  const [hours, minutes] = str.split(':').map(n => Number(n))
  return hours >= 1 && hours <= 24 && minutes >= 0 && minutes < 60
}

Day 9

JavaScriptmas

const capitalizeWord = w => `${w.charAt(0).toUpperCase()}${w.slice(1, w.length)}`

const toTitleCase = s => s.split(' ').map(w => capitalizeWord(w)).join(' ')

Day 10

JavaScriptmas

const sortByLength = s => s.sort((a, b) => a.length - b.length)

Day 11

JavaScriptmas

const reverseString = arr => arr.split('').reverse().join('')

const reverseStringsInArray = arr => arr.map(a => reverseString(a))

Day 12

JavaScriptmas

class MenuItem extends HTMLElement{
    static get observedAttributes() {
        return [
            'food'
        ]
    }
    constructor() {
        super()
    }
    render() {
        this.innerHTML = this.html
    }
    get html() {
        return `<div class="food">${this.food}</div>`
    }
    attributeChangedCallback(name, oldValue, newValue) {
        if (oldValue !== newValue) {
            this.render()
        }
    }
    set food(v){
        this.setAttribute('food', v)
    }
    get food() {
        return this.getAttribute('food')
    }
}
window.customElements.define('menu-item', MenuItem)
const menu = document.getElementById('menu')
const dinnerFoods = ['🍝','🍔','🌮']
menu.innerHTML = dinnerFoods.map(food => `<menu-item food="${food}"/></menu-item>`).join('')

Day 13

JavaScriptmas

const emojifyWord = w => w.charAt(0) === ':' && w.charAt(w.length - 1) === ':' ? emojis[w.slice(1, -1)] ? emojis[w.slice(1, -1)] : w.slice(1, -1) : w

const emojifyPhrase = phrase => phrase.split(' ').map(word => emojifyWord(word)).join(' ')

Day 14

JavaScriptmas

const countVowelConsonant = str => str.split('').reduce((a, c) => a += ['a','e','i','o','u'].includes(c) ? 1 : 2, 0)

Day 15

JavaScriptmas

const isPalindrome = str => str === str.split('').reverse().join('')

Day 16

JavaScriptmas

const insertDashes = arr => arr.split(' ').map(w => w.split('').join('-')).join(' ')

Day 17

JavaScriptmas

const flatten = arr => arr.flat()
const flatten = arr => Array.prototype.concat.apply([], arr)
const flatten = arr => arr.toString().split(',')

Day 18

JavaScriptmas

const candies = (ch, ca) => Math.floor(ca/ch) * ch

Day 19

JavaScriptmas

const centuryFromYear = num => num % 100 === 0 ? num / 100 : Math.floor(num / 100) + 1

Day 20

JavaScriptmas

const getFreePodcasts = data => podcasts.filter(p => !p.paid).map(({title, rating, paid}) => ({title, rating, paid}))

Day 21

JavaScriptmas

const awardBonuses = num => Array.from({length: num}, (_, i) => i + 1).forEach(i => console.log(`${i} - ${!(i%3) && !(i%5) ? 'JACKPOT! 1 Million and a Yacht!' : !(i%3) ? 'Vacation!' : !(i%5) ? '$100,000 bonus!' : ':('}`))

awardBonuses(100)

Day 22

JavaScriptmas

function getReadyTables() {
  const readyTables = []
  for (let i = 0; i < 2; i++) {
    readyTables.push(Math.round(Math.random()*20) + 1)
  }
  return readyTables
}

const displayTables = () => getReadyTables().map(el => {
  const table =  document.createElement('div')
  table.classList.add('table')
  table.innerText = el
  return table
})

document.getElementById('tables').append(...displayTables())

Day 23

JavaScriptmas

const sortProducts = data => data.sort((a, b) => a.price - b.price)

Day 24

JavaScriptmas

const player = document.getElementById("player")
const playSong = id => player.src = `https://www.youtube.com/embed/${id}?autoplay=1`