Yth a écrit 2744 commentaires

  • [^] # Re: Mineurs

    Posté par  (Mastodon) . En réponse au journal Nintendo enfreint le droit de propriété. Évalué à 7 (+5/-0).

    Ouais, la disquette, c'est l'icône de distributeur de boisson qui sert à sauvegarder les fichiers, rien à voir…!

    • Ythosaurus.
  • [^] # Re: Tentative d'explication

    Posté par  (Mastodon) . En réponse au lien Voitures neuves : la Commission renonce au tout-électrique pour 2035. Évalué à 2 (+1/-1).

    Les arguments sont très intéressants.
    Mais :

    • La pollution aux particules fines provient en grande partie de l'usure des pneus, et des plaquettes de frein, on va juste supprimer les résidus de pétrole, ce qui est déjà bien.
    • La voiture individuelle prend un espace de dingue pour poser ces voitures sur le sol, et souvent il y a de vraies difficultés à la trouver cette place.
    • Ça coûte une blinde, le prix moyen des bagnoles a augmenté de 13k€ ces dix dernières années, la mienne m'a coûtée 11k€ neuve, et ça coûte terriblement cher en assurance, entretien, etc.

    L'alternative c'est de remettre des transports en communs, qui peuvent facilement être nettement moins polluants, et plus économes. Ça coûte cher en infrastructures, mais c'est de la mutualisation, après, c'est rentable, si on pousse des méthodes efficaces.
    Donc pas plus de TGV, mais au final, en province, ce dont on a besoin c'est de pouvoir rejoindre la gare TGV sans voiture.
    Prend a peu près n'importe quel abonnement de transport en commun, c'est moins cher que d'avoir une voiture, même si elle ne roule presque pas.

    Et c'est nettement moins dangereux : tu peux les prendre en toute sécurité même en cas de fatigue, d'ivresse, de maladie, de vieillesse, ou de jeunesse.

    Et tu peux dormir, bouquiner, discuter, doom-scroller, ou répondre à des SMS…

    En termes de qualité de vie, ça change tout.
    Au prix d'un peu de souplesse - dépendant du réseau qui aurait été développé.

    Mais c'est une histoire de cercle vicieux : des nouvelles lignes actuellement sont difficilement rentables puisque tout le monde a sa voiture, et qu'au final la perte relative de souplesse, et le fait que la voiture coûte du simple fait de l'avoir, et pas trop par trajet (c'est souvent moins cher au trajet que le train, même seul, hors abonnement).
    Pour que ça soit rentable à titre individuel, il faut se débarrasser de sa voiture, et donc il faut un sérieux investissement initial pour fournir directement une offre permettant à un maximum de gens de se séparer dudit bolide.

    Et ça, c'est de la politique.
    De la politique du service public même, autant dire que c'est pas prêt d'aller dans le bon sens…

    • Yth.
  • [^] # Re: Latex la galère

    Posté par  (Mastodon) . En réponse au journal pdfLaTeX, XeLaTeX et LuaLaTeX sont dans un bateau. Évalué à 3 (+2/-1).

    Bah non, c'est pas ennuyeux, c'est que du bonheur en fait.
    J'ai un rapport technique boulot à faire, je l'écris en org-mode, j'exporte en LaTeX/PDF, et pif, c'est propre, lisible, clair, pro, et j'ai à peu près fait zéro efforts de mise en page.

    Bon, quand je fais des cartons d'invitation pour l'anniversaire de mes enfants, je n'utilise pas LaTeX, mais plutôt Scribus par exemple, voire Gimp quand c'est assez simple (c'est souvent simple).

    T'as vraiment zéro questions de mise en page à te poser, tu changes un truc, tu exportes, et c'est fini, le reste c'est pas ton problème.
    Et dans org-mode tu as une hiérarchie claire, des listes, des tableaux, et même des tableurs, des images, avec légende etc.
    Tu veux finalement mettre un paragraphe après un autre : alt+↓
    Passer un sous-titre en titre : alt+←
    Etc.

    LaTeX c'est que du bonheur, et org-mode c'est génial.

    • Yth.
  • [^] # Re: Jour 10

    Posté par  (Mastodon) . En réponse au journal Advent of Code 2025. Évalué à 2 (+0/-0).

    Ouais, ça va plus vite.
    Jaime bien me creuser la tête sur l'advent of code, un minimum de libs externes, ou de trucs tout faits.

    D'ailleurs on doit pouvoir remarquer que plus on appuie sur les boutons rebelles, moins on appuie sur plein d'autres boutons.
    Dans un cas avec une seule colonne en trop, on doit pouvoir prouver que la solution maximise la valeur pour cette colonne, en respectant la règle de de n'avoir que des solutions entières.
    Donc parcourir depuis le maximum possible en descendant, et s'arrêter dès qu'on a une solution.
    Mais ça accélère le cas le plus simple, rien de bien utile en somme.

    • Yth.
  • [^] # Re: Jour 10

    Posté par  (Mastodon) . En réponse au journal Advent of Code 2025. Évalué à 3 (+1/-0).

    Ben j'ai fini par bricoler un truc qui fonctionne, mais bigre, j'ai pas les maths bien à plat sur ce problème.

    Au début j'avais cherché les boutons avec le moins de choix possibles.
    Exemple :

    [1, 0, 0, 1, 1, 1, 1, 1, 1, 0] = 55
    [1, 0, 0, 0, 0, 1, 0, 1, 1, 0] = 25
    [1, 1, 1, 0, 1, 1, 1, 0, 0, 1] = 58
    [1, 1, 0, 1, 0, 0, 0, 1, 0, 1] = 54
    [1, 0, 0, 1, 1, 1, 1, 1, 0, 0] = 55
    [1, 1, 1, 1, 0, 1, 1, 0, 0, 0] = 53
    [0, 0, 1, 1, 1, 0, 1, 0, 0, 1] = 44
    [0, 1, 1, 1, 1, 0, 0, 1, 0, 0] = 43
    [1, 0, 0, 1, 1, 1, 0, 1, 0, 0] = 42

    On voit que pour obtenir le résultat de la ligne 2, 25, on a quatre boutons possibles, notés a, b, c, d. On a a+b+c+d=25, ça fait pas beaucoup de combinaisons, ici 3276, calculées en python avec itertools.combinations_with_replacement([a,b,c,d],25).
    Et après je brute-force un peu en espérant avoir suffisamment réduit le champs des possibles.
    Ça simplifie suffisamment le problème pour permettre de résoudre la plupart des problèmes parfois en assez longtemps, mais clairement pas tous, j'ai oublié mon programme deux jours, et j'ai un problème qui tournait encore.

    Donc là il faut simplifier la matrice. Je fais avec une diagonale, en gardant des nombres entiers positifs, donc je n'ai plus uniquement des 1 dedans.
    Je réduis à ça, en réorganisant les lignes, mais l'ordre des boutons n'a pas d'importance, on a juste besoin du nombre total de boutons appuyés :

    [24,  0, 0,  0,  0,  0,  0, 0, 0,  60] =  864
    [ 0, 48, 0,  0,  0,  0,  0, 0, 0, -36] =  168
    [ 0,  0, 4,  0,  0,  0,  0, 0, 0,   4] =   56
    [ 0,  0, 0, 24,  0,  0,  0, 0, 0, -12] =  144
    [ 0,  0, 0,  0, 24,  0,  0, 0, 0,  12] =  264
    [ 0,  0, 0,  0,  0, 24,  0, 0, 0, -54] = -468
    [ 0,  0, 0,  0,  0,  0, 12, 0, 0,   0] =  156
    [ 0,  0, 0,  0,  0,  0,  0, 4, 0,  -1] =   34
    [ 0,  0, 0,  0,  0,  0,  0, 0, 1,   0] =    0

    Ensuite, bah j'ai ma dernière colonne qui casse les pieds, on voit un maximum à 864/60, ou 56/4, ou 264/12, et un minimum à -468/-54, donc une valeur entre 9 et 14.
    Alors on remplace, on regarde si notre solution devenue triviale (matrice diagonale, ça va :)) est entière, on jette, et on calcule la solution avec le minimum de boutons appuyés.
    Facile.

    Parfois, on n'a même pas de dernière colonne et la matrice est diagonale directement, super.
    Et puis parfois on en a 2 ou 3, et ça devient casse-pied.
    Les calculs de maxima et minima ne tiennent plus, puisque les boutons rebelles influent les uns sur les autres.
    Au final j'ai énuméré toutes les possibilités d'appuyer de 0 à 200 fois sur chacun des derniers boutons et résolu, comme au dessus, solution entière, que des valeurs positives, et on prend la plus petite.

    Le tout optimisé comme un projet Microsoft, ça prend 2 minutes 36s, et le résultat est valide.
    Je n'arrive déjà plus à relire mon code, et ça sent qu'il faudrait utiliser une bibliothèque d'algèbre linéaire, au moins pour les simplifications, et manipulations de lignes et colonnes, un numpy avec des matrices propres serait probablement une grande avancée dans la lisibilité et les performances.

    À noter qu'avec un solveur, dans l'exemple au dessus, il suffit de rajouter une ligne [0, 0, 0, 0, 0, 0, 0, 0, 0, 1] = (valeur de 9 à 14), et de lui dire « c'est bon, c'est carré, alors c'est quoi ma solution ? », de vérifier qu'elle est entière et de passer à la suite.
    Mais bon, la résolution d'une matrice diagonale, c'est pas vraiment le plus difficile !

    Bref, j'aurais bien pataugé dans ce problème, pour au final ne pas avoir trouvé de truc sensiblement différent des autres, moins bien fait et en plus longtemps.
    Officiellement la cuvée 2025 n'est pas la meilleure pour moi ;)

    • Yth.
  • [^] # Re: Jour 12

    Posté par  (Mastodon) . En réponse au journal Advent of Code 2025. Évalué à 2 (+0/-0). Dernière modification le 13 décembre 2025 à 15:53.

    Ah ouais, j'ai juste eu le temps de réfléchir au problème, mais c'est tellement con au final.
    Me reste plus qu'à prendre le temps de finir le jour 10 maintenant, parce que sur téléphone c'est trop galère, donc je dois retrouver mon PC, et mon Linux :)

    • Yth.
  • [^] # Re: Jour 11

    Posté par  (Mastodon) . En réponse au journal Advent of Code 2025. Évalué à 2 (+0/-0).

    Une journée - encore - sans ordinateur, avec une résolution en très peu de temps de réflexion, et un calcul instantané sur téléphone.

    Pour l'exercice 2, un cache bien posé, un découpage en trois sous-graphes entre svr->fft->dac->out, et la supposition qu'il n'y avait pas de boucles.
    En fait, avec des boucles, de toute façon l'énoncé est infaisable, puisqu'on doit dénombrer les chemins possible, une seule boucle où que ce soit sur un seul chemin permet un nombre infini de chemins.
    Les boucles ça se gère en cas de recherche du plus court chemin.

    Si j'avais eu 0 entre fft et dac, j'aurais inversé les deux, et pour le cas général, additionner les deux semble pertinent.

    J'ai aussi fait une fonction d'analyse des données pour faire le cas d'exemple, où les données sont différentes entre l'exercice 1 et le 2, d'où le clear_cache aussi.

    Ma multiplication finale c'est 3241 * 22130172 * 7345, donc avec un algo plus naïf, et surtout sans cache, on arrive à trouver les valeurs de svr à fft et de dac à out, mais pas de fft à dac, avec 22 millions de chemins.
    En pratique, ma fonction paths est appelée 3102 fois entre les deux exercices, avec 1891 fois où le cache a répondu et 1211 fois où le contenu de la fonction a été exécuté, autant dire que c'est assez négligeable pour le dénombrement total de ~500 billions de chemins, et ça ne prend rien en RAM.

    data = sys.stdin.read().strip().splitlines()
    @cache
    def paths(src="you",dst="out"):
        return 1 if src==dst else sum(paths(i,dst) for i in servers.get(src,[]))
    
    def getservers(data):
        paths.cache_clear()
        return {
            a: b.strip().split()
            for l in data
            for a,b in [l.split(":")]
        }
    servers=getservers(data)
    ex1 = paths()
    
    srv=paths("svr","fft")
    fft=paths("fft","dac")
    dac=paths("dac","out")
    ex2 = srv*fft*dac
    • Yth.
  • [^] # Re: jour 9

    Posté par  (Mastodon) . En réponse au journal Advent of Code 2025. Évalué à 2 (+0/-0).

    Je me pose des questions sur un truc comme ça :

    #######    ┌─────┐
    #.....#    │.....│
    #.###.# => │.┌─┐.│
    #.#.#.#    │.│.│.│
    ###.###    └─┘.└─┘

    Qui se « simplifie » en ne considérant que les coordonnées des 8 sommets, en ça :

    ####    ┌──┐
    #### => │┌┐│
    ####    └┘└┘

    Et nous dis assez facilement que le rectangle complet est valide alors que pas vraiment, en vrai.

    • Yth.
  • [^] # Re: Jour 8

    Posté par  (Mastodon) . En réponse au journal Advent of Code 2025. Évalué à 2 (+0/-0).

    Ici, j'ai réfléchi au fait que si on a deux boîtes de dérivation qui ne sont pas reliées par le fil le plus petit existant, alors on peut raccourcir le réseau total.
    Donc il apparaît clair qu'il faut ajouter les liens du plus petit au plus grand, jusqu'à compléter l'exercice.

    J'ai un peu lutté sur la représentation des données, je voulais être intelligent, mais je me suis retrouvé avec des cas où des sous-graphes étaient encore référencés.
    Là, j'aurais été plus à l'aise en C, à manipuler proprement des pointeurs, pour modifier directement les données à plusieurs endroits à la fois.
    Bon, je le fais aussi en Python, mais j'ai des trucs qui m'ont embêté, et j'ai perdu l'élégance que j'espérais avoir.

    Au final, j'ai quand même un truc pas super optimisé qui s'exécute en 3,5s, c'est très laid.
    Et comme ça prend seulement 1s en PyPy, je sais que j'ai fait du mauvais travail :D

    from dataclasses import dataclass
    import math
    import itertools
    
    MAX=1000 # 10 pour l'exemple
    data = sys.stdin.read().strip().splitlines()
    
    @dataclass(frozen=True)
    class Box:
        x:int
        y:int
        z:int
        def __mul__(self, x):  # distance euclidienne: a*b
            return (self.x-x.x)**2+(self.y-x.y)**2+(self.z-x.z)**2
    boxes={
        Box(*(int(_) for _ in l.split(",")))
        for l in data
    }
    dist=sorted({
        (a*b,frozenset((a,b)))
        for a,b in itertools.product(boxes,boxes)
        if b!=a
    })
    circuits={b:{b} for b in boxes}
    
    for i,(d,(a,b)) in enumerate(dist):
        if b not in circuits[a]:
            c=circuits[a].union(circuits[b])
            if len(c)==len(boxes):
                break  # On a fini
            for _ in c:
                circuits[_]=c
        if i==MAX-1:
            ex1={(len(_),frozenset(_)) for _ in circuits.values()}
    
    ex1=math.prod(sorted(l for l,_ in ex1)[-3:])
    ex2=a.x*b.x
  • [^] # Re: Jour 7

    Posté par  (Mastodon) . En réponse au journal Advent of Code 2025. Évalué à 2 (+0/-0). Dernière modification le 09 décembre 2025 à 19:18.

    Je ne crois pas avoir spécialement utilisé mon cerveau pour cet exercice, un code sur smartphone, peu lisible, mais efficace.
    Je crois avoir été naïf en première approche, mais la naïveté ici est explosive : on ne peut pas compter les 357 525 737 893 560 rayons de l'exercice 2 de façon indépendante.

    data = sys.stdin.read().strip().splitlines()
    
    w,h=len(data[0]),len(data)
    map="".join(data)
    b={map.index("S")}   # exercice 1
    c={map.index("S"):1} # exercice 2: 1 seul rayon au départ
    
    ex1 = 0
    for _ in range(h-1):
        n={i+w for i in b if map[i+w]=="."} # ligne droite
        s={i+w for i in b if map[i+w]=="^"} # split
        ex1+=len(s)
        # Tous les endroits où on a des rayons, pour les 2 exercices
        b=n.union({i+1 for i in s},{i-1 for i in s}) # ex1
        d={i:c[i-w] if i in n else 0 for i in b} # On reprend les rayons directs
        for i in s:  # On ajoute les rayons splittés
            d[i-1]+=c[i-w]
            d[i+1]+=c[i-w]
        c=d
    
    ex2=sum(c.values())
    • Yth.
  • [^] # Re: Jour 6

    Posté par  (Mastodon) . En réponse au journal Advent of Code 2025. Évalué à 2 (+0/-0).

    Pour la partie 2, je transpose la matrice des données en inversant les X et les Y, en gros ça fait un miroir par la diagonale, pour lire dans l'autre sens.
    En ayant retiré la ligne d'opérateurs à la fin.
    Et après les nombres sont séparés par des lignes vides.
    C'est assez simple.

    from operator import mul, add
    data = sys.stdin.read().strip().splitlines()
    
    op=[{"+":add,"*":mul}[_] for _ in data[-1].split()]
    
    r=[0 if o==add else 1 for o in op]
    for l in data[:-1]:
        for i,v in enumerate(l.split()):
            r[i]=op[i](r[i],int(v))
    ex1=sum(r)
    
    d=[  # bascule de matrice
     "".join([l[j] for l in data[:-1]]).strip()
     for j in range(len(data[0]))
    ]
    i,r=0,[0 if o==add else 1 for o in op]
    for l in d:
        if not l:
            i+=1
            continue
        r[i]=op[i](r[i],int(l))
    ex2=sum(r)

    Prodigieusement peu lisible, mais assez concis :)

    • Yth.
  • [^] # Re: jour 5

    Posté par  (Mastodon) . En réponse au journal Advent of Code 2025. Évalué à 2 (+0/-0).

    Jour 5, 6, 7, 8, pas d'ordinateur, résolus en balade, dans la forêt, sur un téléphone…

    Donc concis, moche, peu clair, mais un minimum performant, et je me rappelle plus de l'énoncé, alors pour expliquer…

    def range_ingredients(a,b):
        return range(min(a, b), max(a, b)+1)
    
    ranges=[
        range_ingredients(*(int(_) for _ in l.split("-")))
        for l in data
        if "-" in l
    ]
    ingredients={int(l) for l in data if l and "-" not in l}
    ex1=sum(any(i in r for r in ranges) for i in ingredients)
    
    ranges=sorted(sorted(int(_) for _ in l.split("-")) for l in data if "-" in l)
    a=b=ranges[0][0]
    ex2=0
    for c, d in ranges:
        if c <= b:
            b=max(b, d)
        else:
            ex2+=b-a+1
            a,b=c,d
    ex2+=b-a+1
    • Yth.
  • [^] # Re: jour 9

    Posté par  (Mastodon) . En réponse au journal Advent of Code 2025. Évalué à 3 (+1/-0).

    Ah, c'est bien ça, shapely.
    Parce que bon, j'ai salement polioté sur cette partie, et au final j'ai une solution en faisant une hypothèse de travail et 9 minutes de calculs avec PyPy.
    Il faut beaucoup trop réfléchir pour modéliser le bidule proprement, savoir ce qui est dedans et dehors, dépatouiller les sous-ensembles etc.

    Et mon code aurait foiré s'il y avait eu des « crochets » du genre ça, en considérant le rectangle entre les deux O, la case avec un _ rend le rectangle impossible, mais n'est pas « vue » par mon algo :

    ######.
    #....#.
    #O##.#.
    ##_#.O#
    ...#..#
    .###..#
    .######

    Bref, c'est moche, je montre pas, mais dans l'idée, je liste toutes les arêtes, puis pour chaque x possible (0 à 100000 en gros) on regarde le y maximal et minimal, et on fait idem pour les les y possible avec les x maximal et minimal.
    L'hypothèse ici c'est qu'on voit toutes les arêtes depuis l'extérieur de la surface, nord, sud, est ouest, on a soit une arête dans l'axe et on voit une seule case, soit elle est entièrement visible face à nous.

    Là dessus, pour chaque rectangle possible, triés par superficie décroissante, on regarde si les sommets sont « à l'intérieur », c'est à dire si pour le x du sommet, le y est entre le max et le min, et si pour le y du sommet le x est entre le min et le max.
    Si c'est le cas, on pousse plus loin, et on teste tous les bords du rectangle (nous avons ici une optimisation d'échelle, permettant d'éliminer très rapidement la majorité des rectangles, et de se concentrer sur ceux qui ont un potentiel !).
    Dès qu'on est 100% OK, on a le bon résultat, et ça fonctionne.

    C'est brutal, je ne suis pas sûr de pourquoi ça prend tant de temps, je suppose que je pourrais optimiser mon calcul initial des bornes, j'ai utiliser un cache de fonction, puis j'ai forcé le remplissage du cache au début pour voir pourquoi c'était long.
    Et la version Python (non PyPy donc) bloque au calcul du bon rectangle.

    D'expérience, si la version Python est plus lente que la version PyPy, c'est qu'on a fait de la boue…

    Une bonne optimisation serait de simplifier le terrain par tous les x et y existant dans les coordonnées.
    Dans l'exemple on a x dans [2, 7, 9, 11] et y dans [1, 3, 5, 7], et en réalité le « terrain » fait 4x4 et non 11x7. Dans le cas réel, on a un terrain de moins de 250x250 (496 lignes et chaque coordonnées y est normalement au moins représentée deux fois), et là c'est plutôt facile de parcourir un peu en force sur une terrain minuscule (60 000 cases contre 10 milliards…).
    Mais ça demande de bien réfléchir à ne pas tomber sur des effets de bords.

    Et ceci m'a - enfin - amené à une optimisation intermédiaire : je reprend exactement mon algo, mais après avoir testé les sommets des différents rectangles, au lieu de tester les côtés entiers, je ne teste que les points dont les coordonnées sont dans cette fameuse liste réduite de ~250 x et ~250 y.
    Je tombe à 3s avec PyPy, ce qui prouve qu'on doit pouvoir optimiser pour aller à une vitesse raisonnable.

    Mais…
    Mais est-ce que ça répond au problème dans le cas général ? Est-ce que j'ai un biais de chez-moi-ça-marche ? Parce que je peux décrire des situations où ça ne fonctionne pas, typiquement mon exemple plus haut. J'ai quand même une simplification qui n'est absolument pas prévue par l'énoncé du problème.

    Bref, shapely c'est officiellement une bonne solution…

    • Yth, et je ne parle pas de la partie 1, triviale.
  • [^] # Re: Jour 4

    Posté par  (Mastodon) . En réponse au journal Advent of Code 2025. Évalué à 2 (+0/-0).

    On peut facilement faire mieux en stockant les adresses des @ dans un ensemble, et en supprimant à chaque itération ceux qu'on a pu repérer.

    Ça évite de reconstruire une carte, et ça laisse manipuler uniquement des ensembles d'entiers, ça doit forcément être plus efficace que de jouer avec une chaîne de caractères.

    • Yth.
  • [^] # Re: Jour 4

    Posté par  (Mastodon) . En réponse au journal Advent of Code 2025. Évalué à 3 (+1/-0).

    Jour 4 : pour des raisons personnelles, aucun accès à un ordinateur de la journée. Mais une balade, fairphone à la main.
    Armé de mon qpython, je code…
    Ici, pas de noms de variables explicites, parce que écrire sur un clavier tactile, c'est chiant, donc moins on écrit, mieux on se porte.
    Ici, un classique, pour tester les 8 directions, on définit une carte en ayant ajouté une bordure vide autour, qu'on bascule en 1 dimension, avec huit déplacements entiers.

    data = data.strip().splitlines()
    
    H = len(data)+2
    W = len(data[0])+2
    map = " "*W + "".join(f" {x} " for x in data) + " "*W
    dirs=[-1,1,-W,W,-W-1,-W+1,W-1,W+1]
    
    def count(p):
        return sum(1 for d in dirs if map[p+d]=="@")
    
    def step():
        r=[i for i,x in enumerate(map) if x=="@" and count(i)<4]
        m="".join(x if i not in r else " " for i,x in enumerate(map))
        return m,len(r)
    
    map,n=step()
    ex2=ex1=n
    while n:
        map,n=step()
        ex2+=n

    Durée difficile à évaluer, c'est lent en qpython, zéro chance de brute force.
    On est à 3s environ, donc l'algo est assez bon 😉

    • Yth.
  • [^] # Re: Jour 3

    Posté par  (Mastodon) . En réponse au journal Advent of Code 2025. Évalué à 4 (+2/-0). Dernière modification le 03 décembre 2025 à 14:31.

    Assez similaire dans l'algo au bout du compte, j'ai passé assez rapidement le second exercice, pas de bug, j'ai trouvé plus simple celui du jour.

    • Yth.
    data = stdin.read().strip().splitlines()
    
    def joltage(bank, count=2):
        r, i, bank = [], 0, bank+"."  # bank[i:-0] does not work, going for [:-1] with 1 garbage
        for n in range(count, 0, -1):
            r.append(a:=max(bank[i:-n]))
            i += bank[i:].index(a)+1  # i(ndex) of next unused battery inside bank
        return int("".join(r))
    
    ex1 = sum(joltage(_, 2) for _ in data)
    ex2 = sum(joltage(_, 12) for _ in data)
  • [^] # Re: Jour 2

    Posté par  (Mastodon) . En réponse au journal Advent of Code 2025. Évalué à 2 (+0/-0).

    J'ai adopté une approche qui consiste à regarder les valeurs possibles en fonction de la première partie de l'identifiant (par exemple pour 1188511880-1188511890 exercice 1 on a uniquement 11885, mais pour 111200-222200, avec 3 répétitions on aurait tout dans [11-22]).
    Je les multiplie par le nombre de répétition (ici ça donne 1188511885, ou 111111, 121212, …, 222222), et on vérifie si le nombre est dans l'intervalle.

    Pour simplifier, je redécoupe les intervalles où les nombres n'ont pas la même taille, par exemple je remplace 95-115 par 95-99 et 100-115, au début.
    Après pour l'exercice 1 on regarde que sur les nombres répétés deux fois, et pour l'exercice 2 on teste toutes répétitions de 2 à longueur.

    Pour l'exercice 2 il faut dédupliquer, par exemple 222220-222224 donne 222222 pour 222|222, 22|22|22 et 2|2|2|2|2|2.

    class Range:
        def __init__(self, a, b):
            self.sa, self.sb = str(a), str(b)
            self.a, self.b = int(a), int(b)
            self.len=len(self.sa)
        def invalid(self, repeat=2):
            if self.len%repeat: return  # longueur non divisible par le nb de répétitions
            for root in range(
                    int(self.sa[:self.len//repeat]),
                    int(self.sb[:self.len//repeat])+1):
                if self.a <= (v:=int(str(root)*repeat)) <= self.b:
                    yield v
        def all_invalid(self):
            for repeat in range(2, self.len+1):
                yield from self.invalid(repeat)
    
    
    r=[]  # ranges de même taille, eg 95-115 => 95-99, 100-115
    for rng in data:
        a, b = (int(_) for _ in rng.split("-"))
        ta = a
        while ta < b:
            tb = min(int("9"*len(str(ta))), b)
            r.append(Range(ta, tb))
            ta = tb+1
    
    ex1 = sum(v for _ in r for v in _.invalid())
    ex2 = sum({v for _ in r for v in _.all_invalid()})
    • Yth.
  • [^] # Re: Jour 1

    Posté par  (Mastodon) . En réponse au journal Advent of Code 2025. Évalué à 3 (+1/-0).

    J'ai perdu le fil de mes pensées sur l'équivalent de ta ligne ans2 += t + ((pos>0 and new_pos<=0) or new_pos>=100), j'ai laissé tombé l'idée d'être intelligent à ce moment là, et suis parti sur du clair et sûr.

    class Serrure:
        pos = 50
        sens = "R"
        clic = 0
        zero = 0
        def __call__(self, rotation):
            sens = rotation[0]
            valeur = int(rotation[1:])
            if sens != self.sens:
                self.sens, self.pos = sens, (-self.pos)%100
            clic, self.pos = divmod(self.pos + valeur, 100)
            self.zero += self.pos==0  # exercice 1
            self.clic += clic  # exercice 2
    
    serrure = Serrure()
    for x in data.strip().splitlines(): # data
        serrure(x)
    
    ex1 = serrure.zero
    ex2 = serrure.clic
    • Yth.
  • [^] # Re: Jour 1

    Posté par  (Mastodon) . En réponse au journal Advent of Code 2025. Évalué à 3 (+1/-0).

    Aujourd'hui, je démarre comme une bouse, 1ère partie triviale, et 2de partie bourrée d'erreurs d'algo.
    J'ai tout recommencé avec une jolie classe et une unique fonction, appliquée dans une unique boucle, avec une itération par ligne de donnée, moins de 10 opérations basiques (tests, modulo, assignation, etc) dans la fonction, et les résultats des deux exercices sont calculés en une fois, et justes dès la première exécution :)

    Comme quoi, vouloir faire vite-fait, même au jour 1, c'pas toujours idéal…
    Bon, allez, demain je m'y mets plus tôt, et plus concentré !

    • Yth.
  • [^] # Re: Jour 1

    Posté par  (Mastodon) . En réponse au journal Advent of Code 2025. Évalué à 2 (+0/-0).

    Honnêtement, je ne sais pas s'il a un système pour générer des données, ou s'il a un panel.

    Mais a priori, chaque exercice peut toujours se résoudre rapidement, en temps de calcul, avec un algorithme bien pensé, donc c'est possible qu'il puisse générer des données pour chacun.
    A priori donc on a un jeu de données personnalisé, on ne peut pas comparer les résultats, mais on peut valider les données d'un autre sur son algo, ou l'algorithme d'un autre sur ses propres données.

    Ça m'est vraiment rarement arrivé d'avoir un code adapté en fonction de mes données (quelques optimisations au début, sans détection génériques).

    • Yth.
  • [^] # Re: Leaderboard

    Posté par  (Mastodon) . En réponse au journal Advent of Code 2025. Évalué à 2 (+0/-0). Dernière modification le 01 décembre 2025 à 17:20.

    Cette année tu devrais pouvoir aller au bout, puisque ça se termine le 12 décembre, au lieu du 24 !
    Et oui, j'ai toujours trouvé difficile de coder les derniers jours.

    Bon, bah c'est reparti hein :)

  • # Et hop

    Posté par  (Mastodon) . En réponse au lien GIMP a 30 ans!. Évalué à 4 (+2/-0).

    Joyeux Gimpersaire !

    • Y.
  • [^] # Re: Fiabilité ?

    Posté par  (Mastodon) . En réponse au lien Les entreprises françaises veulent adopter l’IA, mais voici pourquoi c’est « compliqué ». Évalué à 10 (+10/-0).

    Pourtant on confie habituellement ça à des êtres humains, et on sait tous qu'il est absurde de faire confiance à des humains les yeux fermés.

    Mais ce sont des humains qui ont réussi à faire se poser une sonde sur une comète.

    En fait, avec les humains, on sait qu'il va y avoir des défaillances, donc on prévoit le cas, et on met des sécurité.
    Et des sécurités aux sécurités.

    Mais on ne sait pas quelles sécurités mettre aux IAs pour rattraper leurs bévues.
    Ni comment corriger ça automatiquement.

    Donc on ne peut rien automatiser, il faut tout contrôler à toutes les étapes, quitte à refaire soi-même quand ça se plante.

    • Yth.
  • [^] # Re: Commown

    Posté par  (Mastodon) . En réponse au lien Fairphone, téléphone reconditionné… Comment choisir un smartphone éthique. Évalué à 5 (+3/-0).

    Je suis aussi chez Commown, avec un Fairphone 4.

    Déjà c'est un très bon téléphone, c'est à dire qu'il fonctionne sans problème, et permet de répondre à peu près à tous les usages de smartphones.
    Ensuite il est bien maintenu par LineageOS et Murena/e/os/ (je n'ai pas d'infos ou de recul pour d'autres OS alternatifs).

    Et j'approuve la démarche Commown qui nous incite à conserver le téléphone le plus longtemps possible, et les incite eux à conserver le plus longtemps possible des pièces de rechange à disposition pour les réparations.

    La logique derrière est simple : l'entreprise Faiphone, comme tous les constructeurs de smartphones, n'ont pas un intérêt direct à faire durer ton téléphone le plus possible : ils veulent que tu en rachètes des neufs, sinon ils meurent.
    Commown, eux, c'est pas leur problème, ils ont au contraire tout intérêt à te garder chez eux avec ton vieux téléphone, et le plus longtemps possible.

    Il y a eu une émission de Libre à Vous à ce sujet, si je ne m'abuse ?
    Mais comme je ne l'ai pas retrouvée, je ne suis plus très sûr…

    • Yth.
  • [^] # Re: Doute sur la crédibilité

    Posté par  (Mastodon) . En réponse au lien Language performance . Évalué à 3 (+1/-0).

    Trois années d'Advent of Code fait en python m'ont appris que PyPy est terriblement plus rapide que Python quand tu codes mal et ne sait pas exploiter les bonnes structures, ou que tu fais un algo peu optimisé, ou ne sait pas exploiter les merveilles disponibles en une ligne de code.

    Si tu penses bien ton programme, si tu connais les forces et faiblesses du langage, même sur d'énormes boucles, des traitements à plusieurs millions d'occurrences, Python est similaire, voire meilleur que PyPy, et occupe presque toujours moins de mémoire.

    À mon avis tu as ta réponse : le code en dessous doit être identique partout, utilisant le même algo, pas du tout repensé langage par langage, et donc le benchmark est in fine complètement faux.

    • Yth.