Advent of Code 2023 Day 10: Pipe Maze
December 10, 2023 • 3 minutes reading time

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
TILESresponsável por identificar cada caractere - a constante
TILES_CONNECTION_OFFSETresponsável por conhecer osoffsetsde 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:
MapObject- Documentação a respeito de
Closures - Parágrafo sobre o padrão de Módulo dentro da documentação de
IIFE
Métodos Array:
Métodos String: