Advent of code 2024 - Dia 04 de 25
Conteúdo
O quarto dia do Advent of Code foi ontem 04/12/2024! Se você caiu aqui hoje pela primeira vez, não deixe de ler os posts sobre o Dia 1, Dia 2 e Dia 3!
Classifiquei o quarto problema como médio (parte 1) e difícil (parte 2).
Dicas#
-
Se você visualizar as entradas como uma matriz, a solução fica mais natural.
-
Considere que cada caractere ocupa uma posição y, x na matriz, onde y é a linha e x a coluna.
-
Na parte 1, você pode começar a busca quando o primeiro caractere da palavra for encontrado.
-
Na parte 2, você pode começar a busca quando o caractere “A” for encontrado.
-
Parte 1: Médio - exige um pouco de criatividade para implementar a busca recursiva, mantendo a direção da busca.
-
Parte 2: Difícil - exige mais experiência para aplicar algumas heurísticas
Quanto de Python você precisa saber?#
Os capítulos se referem ao meu livro Introdução à Programação com Python.
- Listas - Capítulo 6
- Funções recursivas - Capítulo 8
- Classes - Capítulo 10
Resposta#
from auxiliares.arquivos import le_arquivo_sem_linhas_em_branco
from auxiliares import string_para_matriz
ENTRADA = """
MMMSXXMASM
MSAMXMSMSA
AMXSXMAAMM
MSAMASMSMX
XMASAMXAMM
XXAMMXXAMA
SMSMSASXSS
SAXAMASAAA
MAMMMXMMMM
MXMXAXMASX
"""
PALAVRA = "XMAS"
class MatrixString:
def __init__(self, matriz: list[list[str]]):
self.matriz = matriz
self.y_max = len(matriz)
self.x_max = len(matriz[0])
def valida_posição(self, y, x):
return 0 <= y < self.y_max and 0 <= x < self.x_max
class Problema(MatrixString):
DIREÇÕES = (
(1, 0),
(1, 1),
(1, -1),
(0, 1),
(0, -1),
(-1, 0),
(-1, -1),
(-1, +1),
)
def __init__(self, matriz: list[list[str]], palavra: str):
self.palavra = palavra
self.t_palavra = len(palavra)
super().__init__(matriz)
def visita(self, y, x, posição_palavra, iy=0, ix=0):
if (
not self.valida_posição(y, x)
or self.matriz[y][x] != self.palavra[posição_palavra]
):
return 0
if (
self.matriz[y][x] == self.palavra[posição_palavra]
and posição_palavra == self.t_palavra - 1
):
return 1
a_visitar = self.DIREÇÕES if iy == 0 and ix == 0 else ((iy, ix),)
achadas = 0
for ny, nx in a_visitar:
if self.visita(y + ny, x + nx, posição_palavra + 1, ny, nx) > 0:
achadas += 1
return achadas
def conta_palavras(self):
palavras_encontradas = 0
for y, linha in enumerate(self.matriz):
for x in range(len(linha)):
palavras_encontradas += self.visita(y, x, 0)
return palavras_encontradas
class ProblemaParte2(MatrixString):
def pega_letra(self, y, x):
if self.valida_posição(y, x):
return self.matriz[y][x]
return None
def visita_xmas(self, y, x):
v = [None, None, None, None]
for i, (ny, nx) in enumerate(
((y - 1, x - 1), (y - 1, x + 1), (y + 1, x - 1), (y + 1, x + 1))
):
v[i] = self.pega_letra(ny, nx)
if v[i] is None:
return 0
return 1 if "".join(v) in ["MMSS", "MSMS", "SMSM", "SSMM"] else 0
def conta_xmas(self):
xmas_encontrados = 0
for y, linha in enumerate(self.matriz):
for x, letra in enumerate(linha):
if letra == "A":
if achados := self.visita_xmas(y, x):
xmas_encontrados += achados
return xmas_encontrados
# Parte 1 - Teste da primeira parte
p_teste = Problema(string_para_matriz(ENTRADA), PALAVRA)
r_teste = p_teste.conta_palavras()
print("Resultado do teste (parte 1):", r_teste)
assert r_teste == 18
# Parte 1 - Problema
p_parte1 = Problema(
string_para_matriz(le_arquivo_sem_linhas_em_branco("04/input1.txt")), PALAVRA
)
r_parte1 = p_parte1.conta_palavras()
print("Resultado do problema (parte 1):", r_parte1)
assert r_parte1 == 2530
# Parte 2 - Teste da segunda parte
teste_parte2 = ProblemaParte2(string_para_matriz(ENTRADA))
r_teste_p2 = teste_parte2.conta_xmas()
print("Resultado do teste (parte 2):", r_teste_p2)
assert r_teste_p2 == 9
# Parte 2 - Problema
teste_parte2 = ProblemaParte2(
string_para_matriz(le_arquivo_sem_linhas_em_branco("04/input1.txt"))
)
r_p2 = teste_parte2.conta_xmas()
print("Resultado do problema (parte 2):", r_p2)
assert r_p2 == 1921
# Saída
# Resultado do teste (parte 1): 18
# Resultado do problema (parte 1): 2530
# Resultado do teste (parte 2): 9
# Resultado do problema (parte 2): 1921