◂ 3 décembre 2023 ▸
Gear Ratios : Trouver des nombres qui touchent des symboles dans une grille
..172................... …
..........*.......712... …
.......823.835......%... …
⋮
Mon algo pour la partie 2 est plus compliqué que nécessaire. Il suffit d'avoir un tableau qui compte le nombre de chaque carte, et d'augmenter au fur et à mesure qu'on les passe en revue.
- code1.py
- code1_bordSeulement.py
- code2.py
- code2_court.py
- code2_depuis_gears_plutot_que_nombres.py
"""Au lieu de parcourir les nombres et de regarder ceux qui touchent un gear '*' pour ensuite faire une liste
de nombres pour chaque gear (en réutilisant le code de la partie 1), il peut être plus logique de parcourir
les gears trouvés et de voir combien de nombres ils touchent.
L'algorithme est plus compliqué, puisqu'il s'agit de vérifier non pas juste le bord autour, mais des
suites de chiffres qui partent de ce bord.
L'algorithme trouvé est concis, puisqu'il fonctionne de la même façon sur la ligne du gear que sur les deux
lignes dessus et dessous.
Une fonction sans regexp est donnée en fin de code.
"""
import re
import math
f = open("input.txt", 'r', encoding='utf-8')
lines = [line[:-1] for line in f.readlines()]
HEIGHT = len(lines)
WIDTH = len(lines[0])
def liste_nombres_touchant(lines: list[str], y: int, x: int) -> list[int]:
"""Retourne une liste de nombres qui touchent la position x,y"""
liste_nombres: list[int] = []
# Pas besoin des max et min sur h en réalité, car pas d'* sur le bord dans mon input.
for h in range(max(0,y-1), min(y+2,HEIGHT)):
gauche = (re.findall( '[0-9]+$', lines[h][:x]) or [''])[0]
droite = (re.findall('^[0-9]+', lines[h][x+1:]) or [''])[0]
liste_nombres.extend([int(s) for s in re.split('[^0-9]+', gauche + lines[h][x] + droite) if s])
return liste_nombres
somme_gear_ratios = 0
for i, line in enumerate(lines):
# On se passe de regexp ici juste pour le plaisir :
for x in (i for i,c in enumerate(line) if c=='*'):
nombres = liste_nombres_touchant(lines, i, x)
if len(nombres) == 2:
# pour changer aussi: math.prod !
somme_gear_ratios += math.prod(nombres)
print("Réponse partie 2:", somme_gear_ratios)
########################################################################
# Version sans regexp #
########################################################################
def liste_nombres_touchant(lines: list[str], y: int, x: int) -> list[int]:
"""Retourne une liste de nombres qui touchent la position x,y"""
liste_nombres: list[int] = []
CHIFFRES = "0123456789" # [chr(v) for v in range(ord('0'), ord('9')+1)]
# Pas besoin des max et min sur h en réalité, car pas d'* sur le bord dans mon input.
for h in range(max(0,y-1), min(y+2,HEIGHT)):
x_gauche = x_droite = x
while x_gauche > 0 and lines[h][x_gauche - 1] in CHIFFRES:
x_gauche -= 1
while x_droite < WIDTH - 1 and lines[h][x_droite + 1] in CHIFFRES:
x_droite += 1
if lines[h][x] in CHIFFRES:
liste_nombres.append(int(lines[h][x_gauche:x_droite+1]))
else:
if x_gauche < x:
liste_nombres.append(int(lines[h][x_gauche:x]))
if x_droite > x:
liste_nombres.append(int(lines[h][x+1:x_droite+1]))
return liste_nombres