Advent of Code 2023 Day 10: Pipe Maze

Cover Photo

Foto de Capa gerada por IA

Já no dia seguinte, ao chegar na ilha flutuante de metal você se depara com um clima frio e literalmente tudo ali é feito de metal. Flores, grama e tudo mais é feito de puro metal porém, nenhum animal é avistado. Existem também alguns avisos de Águas termais em uma direção e você resolve dar uma olhada.

Enquanto caminha, ao longe você percebe um movimento perto de algo que parece ser um tubo metálico. Rapidamente é possível fazer um esboço dos tubos ali e este é seu input para o desafio de hoje!

Contexto específico

No desafio de hoje o input é mais como um mapa e existem caracteres indicando conexões entre os tubos. Existem tubos em linha e tubos em formato de “cotovelos” ou ângulos de 90 graus.

Para resolver o desafio é preciso encontrar o ponto mais distante do início (notado pelo caractere S no mapa). Só é possível se mover entre tubos, então uma medição em linha reta não faz sentido.

Resolução Parte 1

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

Como o desafio fazia menção a um mapa, decidi que seria interessante criar algumas abstrações para cada parte do mesmo e separei entre arquivos diferentes.

Tile

O Tile é justamente a coordenada [x, y] que pode conter um tubo conectando 2 outros tiles ou apenas o chão. É necessário saber algumas informações como:

  • se ele é o tile inicial
  • suas conexões diretas
  • coordenadas

Para isso, foi criado um Módulo chamado Tile() usando algumas constantes que podem ser obersvadas no arquivo tile.js como

  • a constante TILES responsável por identificar cada caractere
  • a constante TILES_CONNECTION_OFFSET responsável por conhecer os offsets de cada tubo

Essas constantes foram omitidas e podem ser encontradas no arquivo tile.js mencionado anteriormente. A implementação do Tile() ficou então da seguinte forma

export const Tile = ({ tileType, coordinates }) => {
  let tileConnections = []

  const connectToTile = (tile) => {
    if (tileConnections.length > 1) {
      return
    }

    tileConnections.push(tile)
  }

  return {
    name: () => `tile[${coordinates[0]}][${coordinates[1]}]`,
    isStartTile: () => tileType === TILES.STARTING,
    getTileConnections: () => tileConnections,
    getTileConnectionsCoordinatesOffset: () =>
      TILES_CONNECTIONS_OFFSET[tileType],
    connectToTile,
    coordinates,
  }
}

Maze

Para isolar a lógica exclusiva ao “labirinto” foi criada uma abstração chamada Maze. Esta é a abstração que isola a maior parte da lógica relacionada a

  • Preencher os valores dos tubos
  • Conectar os tubos enquanto itera pelo mapa
  • Isolar o cálculo de distâncias entre os pontos

Sua implementação ficou um pouco complexa e notei que havia o código criado em um dia anterior que se encaixaria muito bem no caso de uso de um mapa 2d envolvendo encontrar pontos adjascentes. Foi então que o Módulo Square() foi criado e colocado dentro do diretório de utils no arquivo square.js.

A respeito do Módulo Maze(), notei que existia muita lógica envolvida a conexão e cálculo do ponto mais distante do início e também isolei essa lógica em outro Módulo chamado DistanceConnections()(encontrada no arquivo distance-connections.js ). A implementação do Módulo Maze() pode ser encontrada no arquivo maze.js.

Assim, o estado interno do Módulo Maze(), sua função interna de setup e sua API ficou da seguinte forma:

export const Maze = ({ data }) => {
  let internalSquare = null
  let getAdjascentPoints = null
  let MAZE = null
  let START_TILE = null
  let distanceConnections = null

  // ...

  const init = () => {
    internalSquare = Square({
      data,
      itemCallbackFn: buildMazeTilesCallbackFn,
    })
    getAdjascentPoints = internalSquare.getAdjascentPoints
    MAZE = internalSquare.getSquare()

    fillMazeConnections()

    distanceConnections = DistanceConnections({ startTile: START_TILE })
    distanceConnections.evaluateDistance()
  }

  init()
  return {
    getFarthestPointFromStart: distanceConnections.getFarthestDistance,
  }
}

Com a lógica isolada dentro dos respetivos Módulos foi possível encontrar o ponto mais distante apenas chamando uma função maze.getFarthestPointFromStart() e assim, resolvendo o desafio!

Assim que a primeira parte foi encerrada, a segunda parte fica disponível e agora é necessário contar quantos Tiles existem na parte interna dos tubos.

Nota: Ainda estou resolvendo a segunda parte desse 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: