• [^] # Re: Rien de vraiment compliqué, il faut juste utiliser tout ce qu'on sait faire.

    Posté par (site web personnel) . En réponse au message Advent of Code 2023, jour 12. Évalué à 4. Dernière modification le 12 décembre 2023 à 13:51.

    Ayé, j'ai résolu la partie 2. Mais je ne suis pas du tout satisfait de ma solution, c'est super efficace mais j'ai l'impression d'avoir triché.

    Je vous explique. Pour la partie 1, j'ai écrit une fonction récursive qui donne le nombre de possibilités d'arrangements restants étant donné des choix déjà faits sur un certain nombre de sources. Elle prend en entrée un état d'avancement, qui indique :

    • l'état de fonctionnement de la dernière source considérée (fonctionnelle ou hors service, pas inconnue, comme on va le voir.) ;
    • la position de la dernière source considérée ;
    • l'index du groupe courant de sources hors service ;
    • le nombre de sources hors service actuellement comptabilisée dans le groupe courant.

    Si on a considéré toutes les sources, ça renvoie directement 1 si le compte est bon (on a atteint le dernier groupe de sources cassées et il est exactement rempli) et 0 sinon.

    Si on n'a pas encore considéré toutes les sources, ça regarde la suivante et ça itère sur ses états possibles (une source en état ne peut être qu'en état, une source cassée ne peut être que cassée et une source en état inconnu peut être les deux, évidemment) :
    * pour un état hors service, si la dernière source considérée était en état, ça ouvre un nouveau groupe... si possible, c'est à dire s'il reste des groupes non considérés, sinon on est dans une impasse et ça continue ;
    * pour un état en état (hmm...), si la dernière source considérée était hors service, ça vérifie si le compte de sources hors service correspond au groupe courant et si ce n'est pas le cas on est dans une impasse et ça continue ;
    * dans les cas où on n'a pas continueé, on appelle la même fonction récursive pour l'étape d'après et on ajoute sa valeur de retour à celle qu'on va renvoyer.

    Ce sera peut-être plus clair avec le code :

    from collections.abc import Iterable, Iterator
    from typing import Optional, Self
    from enum import Enum
    class Condition(Enum):
     OPE='.'
     BRK='#'
     UNK='?'
     def states(self) -> Iterator[Self]:
     if self is self.OPE:
     yield self
     elif self is self.BRK:
     yield self
     else:
     yield self.OPE # type: ignore
     yield self.BRK # type: ignore
    class Condition(Enum):
     OPE='.'
     BRK='#'
     UNK='?'
     def states(self) -> Iterator[Self]:
     if self is self.OPE:
     yield self
     elif self is self.BRK:
     yield self
     else:
     yield self.OPE # type: ignore
     yield self.BRK # type: ignore
     def arrangements(self) -> int:
     def aux(condition: Condition, spring: int, group: int, count: int) -> int:
     if spring == len(self.springs) - 1:
     # Last spring has just been accounted for, time to check:
     # - we are at last group;
     # - that group is full.
     if (group == len(self.groups) - 1
     and count == self.groups[-1]):
     return 1
     return 0
     # Some springs have not been checked yet.
     next_spring = spring + 1
     next_group = group
     next_count = count
     possibilities = 0
     for next_condition in self.springs[next_spring].states():
     if next_condition is Condition.BRK:
     if condition is Condition.OPE:
     # Broken spring after an operational one opens
     # new group... if possible.
     if group == len(self.groups) - 1:
     # Last group has already been checked and closed:
     # dead end.
     continue
     # We can open next group
     next_group += 1
     next_count = 0
     # Regardless of previous spring condition, increase current
     # group count.
     next_count += 1
     if next_count > self.groups[next_group]:
     # New current group is overfull: dead end.
     continue
     if next_condition is Condition.OPE:
     if condition is Condition.BRK:
     # Operational spring after a broken one closes current
     # group, time to check group count.
     if count != self.groups[group]:
     # Dead end
     continue
     possibilities += aux(next_condition, next_spring, next_group, next_count)
     return possibilities
     return aux(Condition.OPE, -1, -1, 0)

    Bon, pour la partie 2, c'est beaucoup trop long, évidemment. Et donc, faute de trouver une astuce, vu le genre de paramètres de la fonction, j'ai bêtement utilisé un...