Advent of Code 2023 Day 20: Pulse Propagation
Foto de Capa gerada por IA
Com todas as peças no lugar os Elfos agora precisam enviar o comando de iniciar para conseguir a areia!
As máquinas estão todas interligadas por cabos que não se conectam diretamente as máquinas mas sim a “repetidores” e módulos de comunicação. Esses módulos se comunicam entre pulsos e você deve ajudar os Elfos a entender como ligar o aparato todo.
Contexto específico
Seu input contém uma lista de módulos:
Flip-Flop
: um módulo que tem um estado interno (umbooleano
). Ao recever umhigh pulse
nada acontece mas caso receba umlow pulse
emite um pulso baseado no estado interno (off
->high-pulse
,on
->low-pulse
) e inverte o estado interno.Conjunction
: tem uma memória que guarda qual o último pulso recebido de todos os módulos(high
oulow
). Quando recebe um pulso atualiza a memória da origem e caso todos as memórias sejamhigh
emite umlow-pulse
, caso contrário emite umhigh-pulse
Broadcaster
: é o módulo “inicial” que recebe os pulsos do botão que inicia o sistema
Ao pressionar o botão inicial, ele emite ao broadcaster
um low-pulse
.
O formato do input é bem intuitivo e suas linhas são no seguinte formato:
<origem> -> <destino-1>, ..., <destino-n>
Resolução Parte 1
Caso queira resolver antes de ler a respeito de minha solução, esse é o momento!
No desafio de hoje precisamos pressionar o botão 4000
vezes e contar quantos pulsos foram gerados (high
ou low
). A resposta final é os dois valores multiplicados.
Pensando na solução, optei por implementar algumas estruturas:
FlipFlop()
Conjunction()
Broadcaster()
que possuem em commum suas APIs:
name
- o nome desse módulolowPulse
- envia umlow-pulse
as todas as conexõeshighPulse
- envia umhigh-pulse
as todas as conexõesconnect
- estabelece uma conexão entre os dois módulos
Para a API do módulo Conjunction()
, ao invés de usar a função connect
, foram implementadas duas outras funções:
connectInput
- informa ao módulo que deve guardar informações a respeito do módulo passadoconnectOut
- realiza a conexão como a funçãoconnect
Com essas estruturas todas preenchidas baseadas no input foi implentado um loop para disparar todas as vezes que o botão inicial foi pressionado e um segundo loop aguardando todos os pulsos que seriam executados na seguência.
Para solucionar o problema de que precisamos aguardar para os pulsos disparados pelo módulo atual terminem de executar para então os outros pulsos serem executados em sequência foi criada uma lista chamada actualPulses
. Todas as funções que executam pulsos retornam uma lista dos pulsos que serão disparados na sequência e essa lista é colocada ao final da lista actualPulses
.
A cada iteração, uma função de callback é removida da lista e executada, colocando mais pulsos ao final da lista de pulsos actualPulses
. O código final foi implementado da seguinte forma
for (let i = 0; i < BUTTON_PRESSES; i++) {
// execute pulses
let startingModuleName = 'broadcaster'
let actualModule = allModules.get(startingModuleName)
// starting pulse
pulsesCount.lowPulse += 1
let actualPulses = actualModule.lowPulse('button')
while (actualPulses.length !== 0) {
const callbackFn = actualPulses.shift()
actualPulses = [...actualPulses, ...callbackFn()]
}
}
E com os pulsos todos executados bastava então multiplicar o número de pulsos executados e essa seria a solução para nosso desafio.
Ao resolver a primeira parte, novamente a segunda parte fica disponível. Existe um módulo que é considerado a saída final de todas as máquinas. O desafio agora é encontrar o número de vezes que o botão inicial precisa ser apertado para que esse módulo receba um low-pulse
.
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:
Map
Object- 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: