Advent of Code 2023 Day 15: Lens Library

Cover Photo

Foto de Capa gerada por IA

Após arrumar ter arrumado os refletores no desafio de ontem, a luz refletida está sendo apontada para outra montanha próxima: A maior montanha na Lava Island. Lá é onde a Lava é produzida e existe uma instalação completa com muita segurança envolvida.

Indo até lá, seu trabalho será ajudar as Renas Natalinas a configurar uma série de lentes com base em um manual. Porém antes de começar é necessário entender um algoritmo de HASH (Holiday ASCII String Helper) que converte qualquer string em um valor ASCII de 0 a 256. Ele é usado para validar se você está usando as configurações corretas.

Você também possui a initialization sequence (seu input) para descobrir qual é valor correto a ser usado para inicializar o sistema.

Contexto específico

O input é uma sequência de caracteres separada por vírgulas. Cada grupo de caracteres tem no máximo 4 caracteres agrupados e seu objetivo é executar o algoritmo HASH e somar os valores de cada grupo.

O Algoritmo em si é relativamente simples. Para uma sequência de caracteres deve-se

  • começar com o valor atual 0 (ou usar o valor atual da iteração anterior)
  • encontrar o ASCII code para o caractere e somar ao valor atual
  • multiplicar por 17
  • salvar o resto da divisão por 256 como valor atual

Esses passos devem ser executados para cada caractere da lista até chegar ao final da mesma.

Resolução Parte 1

Caso queira resolver antes de ler a respeito de minha solução, esse é o momento!

Para a primeira parte o desafio bastava iterar sobre a lista, executar o algoritmo e somar os valores para obter a solução.

O algoritmo HASH foi escrito em um arquivo separado de nome hash-algorightm.js e foi implementado da seguinte forma

const MULTIPLY_FACTOR = 17
const REMAINDER_FACTOR = 256

export const hashAlgorithm = ({ character, currentValue = 0 }) => {
  const asciiCode = character.charCodeAt(0) + currentValue

  const multipliedValue = asciiCode * MULTIPLY_FACTOR

  const remainderValue = multipliedValue % REMAINDER_FACTOR

  return remainderValue
}

E a iteração sobre a lista de grupos de caracteres foram dois laços encadeados

const findSumOfHashResultsForSequence = (sequence) => {
  let sumOfHashResults = 0

  for (const charSequence of sequence) {
    let currentValue = 0

    for (const character of charSequence.split('')) {
      currentValue = hashAlgorithm({ character, currentValue })
    }

    sumOfHashResults += currentValue
  }

  return sumOfHashResults
}

O parâmetro sequence passado já está com os grupos das sequências quebradas a partir do input.

Com a primeira parte do desafio resolvida sendo o valor de sumOfHashResults, a segunda parte fica disponível. Nela é mencionado a existência de uma sequência de caixas com aberturas para o posicionar lentes na parte interna de cada caixa. Essas lentes serão usadas para focar a luz que passa em linha reta pelo centro das caixas.

Seu trabalho agora era encontrar onde as lentes seriam colocadas, removidas e substituídas entre essas caixas para conseguir calcular o poder focal do conjunto.

Resolução Parte 2

Novamente, Caso queira resolver a segunda parte antes de ler a respeito de minha solução, interrompa sua leitura aqui mesmo!

O input na verdade era uma sequência de instruções! É necessário executar essas instruções que se caracterizam entre:

  • colocar uma determinada lente em uma caixa
  • remover uma determinada lente de uma caixa
  • substituir uma determinada lente por outra de mesmo nome, com distância focal diferente

Para esse trabalho foi criada uma estrutura que guarda o estado das caixas (BOXES) que internamente seria apenas um objeto Map() do JavaScript. Essa estrutura levou o nome de LensBoxes() e foi implementada no arquivo lens-boxes.js.

Segue um esboço da implementação com seções omitidas mas com a API no final do trecho de código:

export const LensBoxes = () => {
  const BOXES = new Map()

  /* ... */

  const addLensToBox = ({ lensLabel, lensValue, boxId }) => {
    /* ... */
  }

  const removeLensFromBox = ({ lensLabel, boxId }) => {
    /* ... */
  }

  const executeOperation = ({ operation }) => {
    const [lensLabel, lensValue] = operation.match(/\w+|\d+/g)

    const boxId = findBoxId(lensLabel)

    if (lensValue) {
      // add/update lens to box
      addLensToBox({ lensLabel, lensValue, boxId })
    } else {
      // remove lens from box
      removeLensFromBox({ lensLabel, boxId })
    }
  }

  const findFocusPowerOfLens = ({ lensValue, boxId, slotPower }) =>
    (boxId + 1) * slotPower * lensValue

  const evaluateFocusPower = () => {
    let focusPower = 0

    for (const [boxId, boxLens] of BOXES) {
      for (let i = 0; i < boxLens.length; i++) {
        const [, lensValue] = boxLens[i].split(' ')
        focusPower += findFocusPowerOfLens({
          boxId,
          lensValue: Number(lensValue),
          slotPower: i + 1,
        })
      }
    }

    return focusPower
  }

  return {
    executeOperation,
    evaluateFocusPower,
  }
}

Usando essa estrutura, é possível executar operações com a função executeOperation() e calcular o poder focal com a função evaluateFocusPower(). O código final para a solução do desafio teria apenas que iterar sobre a sequência, executar as operações que modificariam as lentes nas caixas e ao final de tudo calcular o poder focal total

const lensBoxes = LensBoxes()

for (const operation of initSequence) {
  lensBoxes.executeOperation({ operation })
}

const focusPower = lensBoxes.evaluateFocusPower()

Como resultado, obtemos a solução para a parte 2 do desafio!

Referências

O código final esta disponível no repositório do GitHub. Esses são alguns links que podem te auxiliar a compreender melhor o código e cada detalhe que mencionei ou esqueci de comentar a respeito de minha solução:

Métodos Array:

Métodos String: