Advent of Code 2023 Day 04: Scratchcards

Cover Photo

Foto de Capa gerada por IA

Ao usar o Teleférico para chegar na ilha seguinte o Elfo ali presente informa que a ilha que precisa de ajuda não é uma que não é possível chegar com o Teleférico, mas com um barco. Ele oferece o barco dele caso tenha sua ajuda com algo rápido: Ele possui uma porção de scratchcards (ou raspadinhas) mas não sabe quais estão premiadas.

Contexto específico

Cada um desses scartchcards possuem uma lista de números premiados (winning numbers) e uma lista de números daquele scratchcard (card numbers), separados por uma barra vertical (|). Após organizar toda a informação, essa lista se torna seu input. Segundo o elfo, você precisa validar quais dos números do scratchcard aparecem como números premiados.

Para o primeiro número premiado encontrado você ganha 1 ponto naquele scratchcard e cada número premiado seguinte dobra a pontuação do scratchcard.

Resolução Parte 1

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

Ao observar o formato do input, novamente notei que poderia tirar proveito de uma abordagem que usasse Expressões Regulares (RegExp) para buscar tanto os winningNumbers quanto os cardNumbers:

Card <id>: <w-1> <w-2> ... <w-n> | <c-1> <c-2> ... <c-n>

Sabendo disso foi possível já quebrar o input todo separando pelos caracteres : e | usando a função split()

const lines = data
  .split('\n')
  .slice(0, -1)
  .map((line) => line.split(/:|\|/))

Bastava então iterar sobre a lista de linhas e validar quantos dos cardNumbers estavam presentes na lista de winningNumbers.

  • Para quebrar a string em uma lista de números foi usada a função match()
  • Para validar se o valor estava presente na lista de winningNumbers foi usada a função includes()
const getTotalPointsFromScratchCards = (lines) => {
  let totalPoints = 0
  for (const line of lines) {
    const [_cardLabel, winningNumbers, cardNumbers] = line

    const winningNumbersList = winningNumbers.match(/\d+/g)
    const cardNumbersList = cardNumbers.match(/\d+/g)

    let cardPoints = 0
    for (const cardNumber of cardNumbersList) {
      if (winningNumbersList.includes(cardNumber)) {
        cardPoints = cardPoints === 0 ? 1 : cardPoints * 2
      }
    }

    totalPoints += cardPoints
  }

  return totalPoints
}

Ao descobrir o valor total de pontos a segunda parte do desafio fica habilitada. Você percebe que na parte de trás dos scratchcards existem informações sobre como funcionam as regras deles.

Para sua surpresa não existem “pontos”! O sistema dos scratchcards é um pouco mais complexo que a cada número de cardNumbers presentes nos winningNumbers o jogador agora é recompensado com mais scratchcards subsequentes!

A resposta para a segunda parte do desafio agora é o número total e final de scratchcards que você terá, sejam eles originais ou “cópias”.

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!

Após pensar um pouco a respeito, decidi abordar o problema por partes:

  • Iterar sobre o input montando uma estrutura (um Map()) com
    • o valor de cardPoints do scratchcard atual
    • adicionar os cardPoints usando o id do scratchcard atual, adicionando uma cópia a ele
    • popular os próximos ids de scratchcards subsequentes com cópias baseado no número de réplicas do scratchcard atual
  • Somar todos os scratchcards existentes na estrutura montada

Duas funções auxiliares foram criadas para realizar essas tarefas. São elas, respectivamente: findOriginalScratchCardsAndCopies() (código disponível no repositório do GitHub!) e getTotalScratchCards(). Enquanto a primeira é relativamente parecida com a função que resolvia a primeira parte do desafio, a segunda apenas itera sobre o Map() criado.

const getTotalScratchCards = (scratchCardsMap) => {
  let totalScratchCards = 0
  for (let scratchCard of scratchCardsMap.values()) {
    totalScratchCards += scratchCard.copies
  }

  return totalScratchCards
}

Com isso, após montar o mapa de scratchcards com a quantidade de cada um existente e suas respectivas cópias, uma chamada a função getTotalScratchCards() fornece a solução para a segunda parte do desafio: o número total de scratchcards.

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: