Advent of code

 13 décembre 2023 

  Point of Incidence : Trouver des axes de symétrie avec droit à une différence
#.#.##.##.#
   
#..#..####.
   
#.#####..##
   
     ⋮   
..##.##.##.
   
..#.#..##..
   
  
   
#.....###..###.
   
.###....#..#...
   
       
  1. code.py
f = open("input.txt", 'r', encoding='utf-8')
lines = [line[:-1] for line in f.readlines()]

grilles = [[]]
for line in lines:
    if line == '':
        grilles.append([])
    else:
        grilles[-1].append(list(line))


def calc_nb_diffs(tab1, tab2):  # 0, 1 ou 2 (=2 ou plus)
    nb_diff = 0
    for i in range(len(tab1)):
        if tab1[i] != tab2[i]:
            nb_diff += 1
        if nb_diff == 2:
            break
    return nb_diff

def trouver_lignes_similaires_premiere(lignes: list[list[str]]) -> tuple[list[int],list[int]]:
    """ retourne les indices des lignes similaires, dans deux listes : une pour lignes égales
        et une pour les lignes qui ont une différence de 1 caractère. """
    listes_indices = ([],[])
    for row in range(1, len(lignes), 2):  # parcourir de 2 en 2, car l'axe doit être *entre* deux lignes
        ligne = lignes[row]
        nb_diff = calc_nb_diffs(ligne, lignes[0])
        if nb_diff < 2:
            listes_indices[nb_diff].append(row)
    return listes_indices

def trouver_indices_axe_horiz_debut(grille):
    indices_parties = [[],[]]
    for nb_erreur_depart, indices in zip([0,1], trouver_lignes_similaires_premiere(grille)):
        for indice in indices:
            nb_erreurs = nb_erreur_depart
            ok = True
            for i in range(1, indice//2+1):
                nb_diff = calc_nb_diffs(grille[i], grille[indice-i])
                if nb_erreurs + nb_diff > 1:
                    ok = False
                    break
                else:
                    nb_erreurs += nb_diff
            if ok:
                indices_parties[nb_erreurs].append(indice)
    return indices_parties

def trouver_indices_axes(grille) -> tuple[list[int], list[int]]:
    """ Retourne la liste des axes horizontaux pour chaque partie, puis la liste des axes verticaux"""
    indices_axes_verticaux = 2*[0]
    indices_axes_horizontaux = 2*[0]
    trouve = 2*[False]

    grille_lignes_inversees   = grille[::-1]
    grille_colonnes_inversees = [[grille[r][c] for r in range(len(grille))] for c in range(len(grille[0]))]
    grille_toute_inversee     = grille_colonnes_inversees[::-1]

    for g in [grille, grille_lignes_inversees, grille_colonnes_inversees, grille_toute_inversee]:
        indice = trouver_indices_axe_horiz_debut(g)
        assert all(len(id) <= 1 for id in indice)
        for partie in (0,1):
            if indice[partie]:
                indice[partie] = indice[partie][0]
                assert not trouve[partie]
                trouve[partie] = True
                if g is grille:
                    indices_axes_horizontaux[partie] += (indice[partie] + 1) // 2
                elif g is grille_lignes_inversees:
                    indices_axes_horizontaux[partie] += (2*len(g) - indice[partie] - 1) // 2
                elif g is grille_colonnes_inversees:
                    indices_axes_verticaux[partie]   += (indice[partie] + 1) // 2
                else:  # g is grille_toute_inversee
                    indices_axes_verticaux[partie]   += (2*len(g) - indice[partie] - 1) // 2

        #if all(trouve):    # Rapide même sans ça et permet de vérifier qu'il n'y a pas plusieurs solutions
        #    break
    
    assert all(trouve)
    return (indices_axes_horizontaux, indices_axes_verticaux)


somme_indices_verticaux   = [0,0]
somme_indices_horizontaux = [0,0]
for grille in grilles:
    indices_axes_horizontaux, indices_axes_verticaux = trouver_indices_axes(grille)
    for partie in (0,1):
        somme_indices_horizontaux[partie] += indices_axes_horizontaux[partie]
        somme_indices_verticaux[partie]   += indices_axes_verticaux[partie]

for partie in (0,1):
    print(f"Réponse partie {partie+1}:", 100*somme_indices_horizontaux[partie] + somme_indices_verticaux[partie])