Advent of code

 5 décembre 2023 

  If You Give A Seed A Fertilizer : Faire des correspondances de nombres sur de larges intervalles qui se chevauchent
seeds: 364807853 408612163 302918330
   
  
   
seed-to-soil map:
   
2069473506 3732587455 1483883
   
3235691256 2348990120 6550341
   
                  ⋮   
  
   
soil-to-fertilizer map:
   
2700214958 2743391193 363795571
   
                  ⋮   
humidity-to-location map:
   
                  
  1. code1.py
  2. code2.py
  3. code2_oo.py
  4. code2_oo_nonRecursif.py
  5. intervalle.py
import re
f = open("input.txt", 'r', encoding='utf-8')
lines = [line[:-1] for line in f.readlines()]

def parse_ints(line: str) -> list[int]:
    return [ int(tok) for tok in re.split('[^0-9]+', line) if tok ]


""" Pour les règles de transformations entre les différentes catégories 
    seed => soil => fertilizer => water => light => temperature => humidity => location """
Intervalle = tuple[int, int]      # représente intervalle [a;b[ = [a;b-1] !
Regle =  tuple[Intervalle, int]   # intervalle [a;b[ qui s'applique, décalage
Transformation = list[Regle]      # Règles pour passer d'une catégorie à la suivante

transformations: list[Transformation] = []

seeds = parse_ints(lines[0])
seeds_groupes_par_pairs        = zip(* [iter(seeds)]*2 )
# ou:  seeds_groupes_par_pairs = zip( seeds[0::2], seeds[1::2] )
# ou:  seeds_groupes_par_pairs = itertools.batched(seeds,2)      # idéal, mais depuis Python 3.12

seeds_intervalles: list[Intervalle] = [(a,a+d) for (a,d) in seeds_groupes_par_pairs]

# transformer la façon de décrire la règle dans input en une Regle plus lisible
def creer_regle(new_value_start: int, old_value_start: int, size: int) -> Regle:
    intervalle_applique = (old_value_start, old_value_start + size)
    decalage = new_value_start - old_value_start
    return (intervalle_applique, decalage)

for line in lines[1:]:
    if not line:
        continue
    elif ':' in line:
        print("Lecture des règles de " + line.split()[0])
        transformations.append([])
        continue
    else:
        transformations[-1].append( creer_regle(*parse_ints(line)) )

def calc_valeur_suivante(regles: Transformation, intervalle: Intervalle) -> list[Intervalle]:
    start, end = intervalle
    for (a,b), decalage in regles:
        
        # Si la règle ne s'applique pas ici:  valeurs)  s___e |
        if start >= b or end <= a:      #     règle)          | a_____b
            continue

        # Si la règle s'applique partout:     valeurs)   s___e
        elif start >= a and end <= b:   #     règle)   a_|___|____b
            return [ (start + decalage,  end + decalage) ]
        
        # Si la règle s'applique sur une partie intérieure:  v)  s__|___|____e
        elif start < a and end > b:                  #       r)     a___b
            return calc_valeur_suivante(regles, (start, a))  \
                +    [ (a + decalage, b + decalage) ]          \
                +  calc_valeur_suivante(regles, (b, end))
        
        # Si la règle s'applique sur un bout à droite:       v)  s____|___e
        elif start < a:  # et donc on sait que end <= b      r)       a___|___b
            return calc_valeur_suivante(regles, (start, a))  +  [ (a + decalage, end + decalage) ]
        
        # Sinon, la règle s'applique sur un bout à gauche:   v)      s___|____e
        else:                                         #      r)  a___|___b
            return  [ (start + decalage, b + decalage) ] + calc_valeur_suivante(regles, (b, end))
        
    # Aucune règle ne s'est appliquée    
    return [ (start, end) ]


def calculer_intervalles_suivants(regles: Transformation, intervalles: list[Intervalle]) -> list[Intervalle]:
    nouveaux_intervalles = []
    for intervalle in intervalles:
        nouveaux_intervalles.extend(calc_valeur_suivante(regles, intervalle))
    return nouveaux_intervalles


intervalles = seeds_intervalles
for regles in transformations:
    intervalles = calculer_intervalles_suivants(regles, intervalles)

print("Reponse partie 2:", min(a for (a,b) in intervalles))