Yth a écrit 2615 commentaires

  • [^] # Re: À la chasse aux singes, j'envoie le Python !

    Posté par  (Mastodon) . En réponse au message Avent du Code, jour 11. Évalué à 2.

    Ah oui, j'ai sorti l'eval avec réticence, mais c'est tellement adapté à ce cas précis…

    Professionnellement il faudrait salement valider la chaîne, et probablement qu'un parseur aurait été la solution, pour ne pas faire d'eval.

    • Yth, PGPM, PPCD, rhaaa, c'pareil quoi, le lecteur aura corrigé de lui-même !
  • # À la chasse aux singes, j'envoie le Python !

    Posté par  (Mastodon) . En réponse au message Avent du Code, jour 11. Évalué à 3.

    Pour le « truc qui scale pas » de la seconde partie, un bug dans la première partie m'a fait tomber dessus très tôt, et la solution est assez simple.

    Un mélange de propre et de hack tout moche pour celui-là, avec un affichage qui montre l'avancement des calculs, ya 9s pour la seconde partie tout de même…

    import sys
    from collections import deque, OrderedDict
    import math
    def input():
        operations = {
            "Starting items": "items",
            "Operation": "operation",
            "Test": "test",
            "If true": "true",
            "If false": "false",
        }
        monkey = {}
        for line in sys.stdin:
            if line == "\n":
                yield monkey
                monkey = {}
                continue
            operation, value = line.strip().split(":")
            monkey[operations.get(operation, "monkey")] = (
                value or int(operation.split()[-1]))
        yield monkey
    
    class Monkey:
        relief = 3
        modulo = None
        def __init__(self, monkey, items, operation, test, true, false):
            self.id = monkey
            self.items = deque([int(x) for x in items.split(',')])
            self.operation = operation.split("=", 1)[1]
            self.test = int(test.split()[-1])
            self.true = int(true.split()[-1])
            self.false = int(false.split()[-1])
            self.inspection = 0
        def __call__(self):
            self.inspection += 1
            item = self.items.popleft()
            worry = eval(self.operation.replace("old", str(item))) // self.relief
            if self.modulo:
                worry = worry % self.modulo
            return self.false if worry % self.test else self.true, worry
        def __add__(self, n):
            self.items.append(n)
        def __repr__(self):
            return (
                f"Monkey#{self.id}@{self.inspection}:{self.operation}"
                f" % {self.test} ? {self.true} : {self.false}"
                f" => {list(self.items)}"
            )
        def __bool__(self):
            return len(self.items) != 0
        def __gt__(self, other):
            return self.inspection > other.inspection
    
    def monkey_simulation(
            rounds, input,
            relief=3, modulo=None,
            skiplines=0, show_monkeys=True):
        Monkey.relief = relief
        Monkey.modulo = modulo
        monkeys = OrderedDict(sorted(
            (monkey['monkey'], Monkey(**monkey))
            for monkey in inputmonkeys
        ))
        if show_monkeys:
            print("\033[0;0H", end='')
            print("\n".join([repr(m) for m in monkeys.values()]))
            skiplines += len(monkeys) + 1
        movecursor = f"\033[{skiplines};0H"
        for round in range(rounds):
            print(f"{movecursor}Round #{round}")
            for monkey in monkeys.values():
                while monkey:
                    dst, worry = monkey()
                    monkeys[dst] + worry
        b1, b2 = [m.inspection for m in sorted(monkeys.values())[-2:]]
        print(f"Monkey business level: {b1*b2}")
        return skiplines + 2, math.prod(monkey.test for monkey in monkeys.values())
    
    inputmonkeys = [line for line in input()]
    skiplines, modulo = monkey_simulation(20, inputmonkeys)
    print(f"Global modulo = {modulo}")
    monkey_simulation(10000, inputmonkeys, 1, modulo, skiplines+1, False)

    L'idée pour la seconde passe c'est que tout peut se passer au modulo du produit de tous les modulos. Même au PGCM de tous les modulos des différents singes, mais le produit suffit, on a des nombres suffisamment petits pour que ça roule vite et bien.

    Donc la première passe retourne ce produit des modulos, qui est nourri à la seconde passe, en modifiant la classe de base avant instanciation des nouveaux singes.
    Joli hack bien crade quoi :)

    Après on a quelques bidouilles d'affichage pour lister les singes et faire défiler le n° de round, et ainsi voir l'avancement des calculs.

    Et bien sûr, le cœur du problème avec un bon gros eval sur l'opération à effectuer, sinon c'pas drôle !

    • Yth.
  • [^] # Re: En Python, modélisé

    Posté par  (Mastodon) . En réponse au message Avent du Code, jour 9. Évalué à 2.

    C'est Tanguy qui a pensé à l'utf8 en premier pour le jour 10, je reprends les bonnes idées :)

    • Yth.
  • [^] # Re: En faisant pivoter la forêt

    Posté par  (Mastodon) . En réponse au message Avent du Code, jour 8. Évalué à 2. Dernière modification le 10 décembre 2022 à 20:25.

    Ya ça en numpy :
    https://numpy.org/doc/stable/reference/generated/numpy.rot90.html

    J'aime bien l'idée, c'est bourrin, c'est fun, ça fait du code plus léger, et ça ressemble à ce qu'on ferait à la main avec une maquette ou une feuille de papier : on fait tourner (ou on tourne autour) !

    Par contre c'est faux je pense.
    Il y a des arbres visibles depuis plusieurs côtés, et tu vas les compter plusieurs fois…

    • Yth.
  • [^] # Re: Plus simple

    Posté par  (Mastodon) . En réponse au message Avent du Code, jour 10. Évalué à 2. Dernière modification le 10 décembre 2022 à 20:17.

    C'est un peu l'idée derrière mon propre code.
    Et si on l'enrichissait ?

    Par exemple si on veut ajouter une commande affxy a b - qui fait la fonction affine a*x+b - à deux arguments et trois cycles par exemple, dans mon code il suffit d'ajouter cette méthode :

        def affxy(self, a, b):
            self.noop()  # ou :
            self.noop()  # self.history.append(self.X)
            self.noop()  # pour la première version
            self.X = int(a) * self.X + int(b)

    Voilà, c'est géré.

    • Yth.
  • [^] # Re: En Python, modélisé

    Posté par  (Mastodon) . En réponse au message Avent du Code, jour 9. Évalué à 3.

    Bonne idée de remettre le curseur en haut !
    Et encore une petite amélioration inutile donc indispensable :

            top = "*" * (self.w)
            lines = [
                "*" + "░ " * (self.w//2 - 1) + "*",
                "*" + " ░" * (self.w//2 - 1) + "*",
            ]
            self.backgrounds = [
                top + "".join(lines[_ % 2] for _ in range(self.h - 2)) + top,
                top + "".join(lines[1 - _ % 2] for _ in range(self.h - 2)) + top,
            ]
    [...]
            txt = [c for c in self.backgrounds[sum(self.delta) % 2]]
    [...]
                txt[int(x + y * self.w)] = "█"

    On a un fond en échiquier, qui bascule quand on « déplace la caméra », ça permet de visuellement voir que ça défile.
    Ça marche parce qu'on décale toujours de 1, et ça alterne entre les deux fonds quelle que soit la direction du déplacement.
    Et avec des caractères utf-8 de bloc plein, ou gris, c'est plus joli.

    • Yth.
  • [^] # Re: Où est la partie 2 ?

    Posté par  (Mastodon) . En réponse au message Avent du Code, jour 10. Évalué à 3.

    Il faut te connecter, d'une façon ou d'une autre, j'utilise mon compte github perso.
    Et là tu as un jeu de donnée personnalisées en entrée, et tu peux fournir le résultat dans un champs spécifique en bas de page.
    Ça te donne accès au second exercice.

    • Yth.
  • [^] # Re: quick python

    Posté par  (Mastodon) . En réponse au message Avent du Code, jour 10. Évalué à 4. Dernière modification le 10 décembre 2022 à 15:14.

    Tu peux simplifier un peu ta gestion des input en faisant ça :

    for l in sys.stdin:
      m, *q = l.split()
      if m == "noop":
        inc()
      else: # add
        inc()
        inc()
        X += int(*q)

    Le premier point, d'utiliser for l in sys.stdin permet de ne pas lire tout l'input, mais simplement d'itérer sur les lignes, on reste plus bas en RAM, on traite un flux.

    Le second point, c'est d'utiliser *q pour prendre « ce qui reste ».
    Dans le cas d'une ligne "noop" on a m="noop" et q=[].
    Dans le cas de "addx XX" on a m="addx" et q=[XX], une list avec la valeur en premier (et unique) élément.
    Mais en refilant *q à int, il « unpack » et int(*q) devient équivalent à int(XX).

    Avec ça tu gères des instructions différentes, avec autant de paramètres que tu veux, tu t'en fous au niveau du parsing, tu veux juste connaître l'instruction et passer le reste à la gestion de l'instruction.

    Ça évite de rajouter un argument factice, et de splitter en ne prenant que les deux premiers.

    • Yth.

    PS: et aussi, chapeau pour la maîtrise d'awk, j'en suis pas là !

  • # Entre deux.

    Posté par  (Mastodon) . En réponse au message Avent du Code, jour 10. Évalué à 3.

    Moins modélisé que Tanguy, mais plus que steph.

    import sys
    def input():
        for line in sys.stdin:
            yield tuple(line.split())
    
    class cpu:
        def __init__(self):
            self.X = 1
            self.history = []
        def noop(self):
            self.history.append(self.X)
        def addx(self, n):
            self.history.append(self.X)
            self.history.append(self.X)
            self.X += int(n)
        def __getitem__(self, n):  # Cycle 1 is history 0
            return self.history[n-1]
        def __call__(self, row, col):
            return "#" if abs(self.history[row * 40 + col] - col) <= 1 else " "
    
    
    mycpu = cpu()
    for action, *values in input():
        getattr(mycpu, action)(*values)
    
    signal_strength = sum(mycpu[n]*n for n in range(20, 221, 40))
    print(f"Signal Strength : {signal_strength}")
    screen = "\n".join(
        "".join(mycpu(row, col) for col in range(40))
        for row in range(6)
    )
    print(screen)

    Là je conserve tout l'historique des valeurs de X à chaque cycle, ça n'est évidemment pas viable sur un long programme.
    Il faudrait optimiser pour la demande, c'est à dire écrire instruction après instruction l'état de l'écran pour le cycle qui passe, et faire un hook sur les valeurs de la première question, pour additionner/stocker par ailleurs.

    Au début j'avais mis un compteur de cycle dans ma classe cpu mais à la fin je ne l'utilisais pas, alors je l'ai viré…
    C'est assez facile d' « optimiser » la classe cpu pour qu'elle dessine l'écran au fur et à mesure, on fait une méthode comme ça :

        def cycle(self):
            self.screen.append("#" if abs(self.X-self.col) <= 1 else " ")
            self.col = (self.col + 1) % 40
            if not self.col:
                self.screen.append("\n")
            self.history.append(self.X)

    Et définir self.col = 0; self.screen = [] dans l'init.
    On remplace les appels à self.history.append(self.X) par self.cycle().
    On réalise que cpu.noop ne fait plus qu'exécuter cpu.cycle, donc on garde noop et addx devient :

        def addx(self, n):
            self.noop()
            self.noop()
            self.X += int(n)

    On vire le cpu.call puisqu'on dessine l'écran à la volée dans mycpu.screen et on affiche print("".join(mycpu.screen)) à la fin. On n'a plus qu'à rajouter un history à 0 pour le cycle 0 inexistant, et on n'a plus besoin de faire return self.history[n-1] dans cpu.__getitem__()

    Avec tout ça mis en place on a un code plus concis, plus clair, mais on conserve toujours l'historique pour la question 1, et s'en débarrasser oblige a faire un hook un peu crados dans cpu.noop(), conserver le numéro du cycle courant, et si cycle%40==20 ajouter à self.signal_strength self.X * self.cycle.
    On se débarrasse de l'historique.

    Plus propre ?
    Moins propre ?
    Difficile à dire…
    Mais il s'agit de débuggage, donc on peut introduire un hook cpu.debug à chaque cycle, qui servirait à ça.

    Bilan :

    import sys
    def input():
        for line in sys.stdin:
            yield tuple(line.split())
    
    class cpu:
        def __init__(self):
            self.X = 1
            self.history = [0]
            self.col = 0
            self.screen = []
            self.cycle = 0
            self.strength = 0
        def debug(self):
            self.cycle += 1
            if self.cycle % 40 == 20:
                self.strength += self.cycle * self.X
        def noop(self):
            self.screen.append("#" if abs(self.X-self.col) <= 1 else " ")
            self.col = (self.col + 1) % 40
            if not self.col:
                self.screen.append("\n")
            self.debug()
        def addx(self, n):
            self.noop()
            self.noop()
            self.X += int(n)
    
    mycpu = cpu()
    for action, *values in input():
        getattr(mycpu, action)(*values)
    
    print(f"Signal Strength : {mycpu.strength}")
    print("".join(mycpu.screen))

    Bilan ?
    8 octets de différence dans le fichier final, temps d'exécution rigoureusement similaire.
    En pratique si on bourrine sur les entrées, le second code est plus lent puisqu'il maintient à jour un écran.
    On peut utiliser un deque pour cpu.screen et avoir un scroll automatique, ça restreint la RAM utilisée, qui reste stable quelles que soient les données en entrées.
    Alors que le premier fait juste un history.append(), ça pompe de la RAM (à l'infini d'ailleurs) mais pas du CPU.

    • Yth
  • [^] # Re: Vivement , le 1

    Posté par  (Mastodon) . En réponse au journal Calendrier de l'Avent du code. Évalué à 3.

    Et je suis repassé tout juste devant avec encore un nouvel inscrit !
    C'est rigolo remarque de voir ça évoluer :)

    Tu remarqueras que si on trie par global score on est tous à égalité à 0 hein…

    • Yth.
  • [^] # Re: En Python, modélisé

    Posté par  (Mastodon) . En réponse au message Avent du Code, jour 9. Évalué à 3.

    C'est vrai !

    Bon, comme j'aime bien les trucs rigolos qui bougent, j'ai fait une animation en terminal.
    On ajoute ça :

    import os
    import time
    class display:
        def __init__(self):
            self.w, self.h = tuple(os.get_terminal_size())
            self.h -= 1
            self.delta = numpy.array((self.w//2, self.h//2))
            self.top = "*" * (self.w)
            self.line = "*" + " " * (self.w - 2) + "*"
            self.background = self.top + "".join(
                self.line for _ in range(self.h - 2)) + self.top
    
        def __call__(self, knots):
            x, y = self.delta + knots[0]
            if x <= 0:
                self.delta[0] += 1
            if x >= self.w-1:
                self.delta[0] -= 1
            if y <= 0:
                self.delta[1] += 1
            if y >= self.h-1:
                self.delta[1] -= 1
            txt = [c for c in self.background]
            for knot in knots:
                x, y = knot + self.delta
                txt[int(x + y * self.w)] = "#"
            print("".join(txt))
            time.sleep(.01)
    
    d = display()

    Et dans la boucle on ajoute à la fin : d(knots)
    J'ai 11460 mouvements dans mon input, à cette vitesse - sleep(.01) - ça prends 2 minutes et quelques.

    Ça commence centré sur le démarrage, et quand la tête déborde d'un côté, on translate tout pour garder dans le champs.

    • Yth, du temps à perdre on dirait…
  • [^] # Re: En Python, modélisé

    Posté par  (Mastodon) . En réponse au message Avent du Code, jour 9. Évalué à 4.

    J'espère que dans vos solutions perso, vous aurez pensé à utiliser, en arrivant à la deuxième partie, à utiliser une seule corde pour simuler les deux…

    Pas lors de la première résolution, mais après en nettoyant un peu le code oui… Comme d'hab, le problème arrivant en deux fois, on n'optimise pas dès le début pour la seconde partie :)

    Voici mon code :

    import numpy
    input = [(r[0], int(r[2:])) for r in sys.stdin]
    
    move = dict(
        U=(0, 1),
        D=(0, -1),
        L=(-1, 0),
        R=(1, 0),
    )
    def follow(head, tail):
        delta = head - tail
        if max(abs(delta)) <= 1:  # no move
            return
        if delta[0]:
            tail[0] += 1 if delta[0] > 0 else -1
        if delta[1]:
            tail[1] += 1 if delta[1] > 0 else -1
    
    
    knots = [numpy.array((0, 0)) for _ in range(10)]
    tail1_pos = {(0, 0), }
    tail_pos = {(0, 0), }
    for dir, nb in input:
        for _ in range(nb):
            knots[0] += move[dir]
            for n in range(1, 10):
                follow(knots[n-1], knots[n])
            tail1_pos.add(tuple(knots[1]))
            tail_pos.add(tuple(knots[-1]))
    print(f"Tail positions visited : {len(tail1_pos)}")
    print(f"LongTail positions visited : {len(tail_pos)}")

    Pas trop modélisé, des vecteurs numpy juste pour avoir des simplifications comme l'addition, la soustraction ou la valeur absolue.
    J'aime bien ton import_line qui fait un unique itérateur pour chaque mouvement unitaire, c'est propre !

    Et donc :

    def input():
        move = dict(
            U=(0, 1),
            D=(0, -1),
            L=(-1, 0),
            R=(1, 0),
        )
        for line in sys.stdin:
            direction = move[line[0]]
            for _ in range(int(line[2:])):
                yield direction
    
    def follow(head, tail):
        delta = head - tail
        if max(abs(delta)) <= 1:  # no move
            return
        if delta[0]:
            tail[0] += 1 if delta[0] > 0 else -1
        if delta[1]:
            tail[1] += 1 if delta[1] > 0 else -1
    
    knots = [numpy.array((0, 0)) for _ in range(10)]
    tail1_pos = {(0, 0), }
    tail_pos = {(0, 0), }
    for direction in input():
        knots[0] += direction
        for n in range(1, 10):
            follow(knots[n-1], knots[n])
        tail1_pos.add(tuple(knots[1]))
        tail_pos.add(tuple(knots[-1]))
    print(f"Tail positions visited : {len(tail1_pos)}")
    print(f"LongTail positions visited : {len(tail_pos)}")
    • Yth.
  • [^] # Re: Procrastination

    Posté par  (Mastodon) . En réponse au message Avent du Code, jour 8. Évalué à 3.

    Ouais, c'est des glandus ces elfes, on dirait moi le lundi matin…

    • Yth.
  • [^] # Re: python procédural, moche mais efficace

    Posté par  (Mastodon) . En réponse au message Avent du Code, jour 8. Évalué à 3.

    Mon premier parcours est moins bourrin, je ne cherche pas à savoir pour chaque arbre s'il est visible, mais bien à noter les arbres vus depuis chaque angle de vue, donc au lieu d'avoir size=width*height traitements, j'en ai 2*(width+height).

    def check(rows, cols):
        h = -1
        for r in rows:
            for c in cols:
                if input[r][c] > h:
                    visible[r * width + c] = True
                    h = input[r][c]
    
    visible = [False for _ in range(size)]
    for r in range(height):
        check([r], range(width))
        check([r], range(width-1, -1, -1))
    for c in range(width):
        check(range(height), [c])
        check(range(height-1, -1, -1), [c])
    print(f"Visible Trees : {sum(visible)}")

    Pour la seconde partie c'est algorithmiquement équivalent :

    def look(h, rows, cols):
        n = 0
        for r in rows:
            for c in cols:
                n += 1
                if input[r][c] >= h:
                    return n
        return n
    
    scenic = [0 for _ in range(size)]
    for r in range(1, height-1):
        for c in range(1, width-1):
            h = input[r][c]
            scenic[r * width + c] = (
                look(h, range(r-1, -1, -1), [c])  # top
                * look(h, range(r+1, height), [c])  # bottom
                * look(h, [r], range(c-1, -1, -1))  # left
                * look(h, [r], range(c+1, width))  # right
            )
    print(f"Best scenic tree : {max(scenic)}")
    • Yth.
  • [^] # Re: En Python

    Posté par  (Mastodon) . En réponse au message Avent du Code, jour 7. Évalué à 3.

    Et un peu d'utilisation de fonction magiques chez moi ici :

    class Directory:
        def __getitem__(self, name):
            return self.files[name]

    Et là pour chopper le sous-répertoire "toto" de mon Directory(truc) je fais truc['toto'].
    Je n'ai même pas pensé à inclure le '..' dans mes fichiers, j'ai un cas particulier si on fait cd .., c'était tellement évident de faire ça !

    On pourrait accéder à truc/toto via truc.toto en implémentant __getattr__ comme j'ai fait le __getitem__, mais si "toto" est dans une variable il faut faire getattr(truc, variable_toto), ça ne simplifie pas la lecture, c'est plus simple d'utiliser truc[variable_toto].

    Après on peut implémenter __truediv__ et faire que truc/"toto" retourne Directory(truc/toto).
    Là on a root/"a"/"b"/".."/"c"/".."/".."/"e"/"f" = Directory("/e/f") :)
    Mais les guillemets partout alourdissent la lecture et l'écriture…

    Sinon, côté algo, et même implémentation, c'est pareil rien à signaler, pas de subtilité, j'ai juste utilisé du if line.startswith("$ cd"): cwd = cwd[line[5:]] plutôt qu'une regexp.

    • Yth.
  • [^] # Re: En Python bref

    Posté par  (Mastodon) . En réponse au message Avent du Code, jour 2. Évalué à 3.

    J'ai fait du Caml, j'ai joué un peu avec OCaml, et même un poil de Lisp Jadis, mais je ne code pas régulièrement dans ces langages là.

    Réutiliser les opérateurs, c'est bien pour jouer, mais ça peut péter complètement la lisibilité, donc il faut faire très très attention quand on veut partager le code…

    Bien fait c'est génial par contre.

    • Yth.
  • [^] # Re: En Python bref

    Posté par  (Mastodon) . En réponse au message Avent du Code, jour 2. Évalué à 3.

    J'aime bien réutiliser les opérateurs, mais j'ai fait ça assez rapidement, donc la pertinence est celle d'un truc pondu en vingt minutes.
    Le ~chifumi est pratique et plutôt lisible, comme ça on normalise tout le temps.

    Par contre réutiliser la classe chifumi pour qu'elle représente une choix ou un résultat d'affrontement, c'est plutôt moche, ça rendrait illisible un code plus gros, et c'est totalement lié à la présentation de la seconde partie du problème après la résolution de la première : comment bricoler vite fait du code qui répond à la question. C'était plus simple de rajouter un opérateur à une classe existante que de gérer une nouvelle classe.

    Mais en plus propre on pourrait avoir une classe resultat d'affrontement, qui sait se multiplier (par exemple) avec une classe chifumi, avec __mul__ et __rmul__, pour pouvoir faire chifumi * resultat = resultat * chifumi = "mais qu'a donc joué mon adversaire ?"
    On apprends à resultat à se multiplier dans les deux sens avec chifumi sans que celle-ci sache comment se multiplier avec resultat.

    On pourrait garder le même opérateur partout, et faire du __mod__ et __rmod__ sur resultat. Sachant que dans tous les cas on retourne un score qui est un entier et n'a rien à voir avec aucune des deux classes.

    Pour le total_ordering, c'est vrai, mais en pratique j'ai implémenté __lt__ qui n'est jamais utilisé. J'aurais pu nettoyer ça du code avant de le poster.

    • Yth.
  • [^] # Re: Vivement , le 1

    Posté par  (Mastodon) . En réponse au journal Calendrier de l'Avent du code. Évalué à 4.

    Je viens de voir le message, j'ai rejoins alors :)
    Bon, par contre le leaderboard a la valeur qu'on lui donne : hors de question de me lever à 6h du matin pour faire ça parmi les premiers…

    • Yth.
  • [^] # Re: En mode 10 minutes

    Posté par  (Mastodon) . En réponse au message Avent du Code, jour 1. Évalué à 2. Dernière modification le 07 décembre 2022 à 10:08.

    Ma solution vite faite.

    import sys
    input = sys.stdin.read().split("\n")
    s = [0]
    for i in input:
        if i == "":
            s.append(0)
        else:
            s[-1] += int(i)
    
    print(f"Maximum = {max(s)}")
    print(f"3 Best : {sum(sorted(s)[-3:])}")
    time python3 01.py < 01.in
    Maximum = 71506
    3 Best : 209603
    
    real    0m0,041s
    user    0m0,034s
    sys 0m0,007s
    
    • Yth.
  • [^] # Re: En Python bref

    Posté par  (Mastodon) . En réponse au message Avent du Code, jour 2. Évalué à 3.

    Sur celui-là, j'ai vachement modélisé au contraire :

    class chifumi(int):
        lose = 1
        draw = 2
        win = 3
    
        def __init__(self, chifumi):
            self.chifumi = chifumi
            self.chifumi = ~self
    
        def __invert__(self):
            """~chifumi returns value in [1, 2, 3]"""
            return (self.chifumi % 3) or 3
    
        def __eq__(self, other):
            return ~self == ~other
    
        def __lt__(self, other):
            return +self == ~other
    
        def __gt__(self, other):
            return -self == ~other
    
        def __neg__(self):
            return ((self.chifumi - 1) % 3) or 3
    
        def __pos__(self):
            return ((self.chifumi + 1) % 3) or 3
    
        def __mod__(self, other):
            """score of self against other"""
            if self == other:
                return ~self+3
            if self > other:
                return ~self+6
            return ~self
    
        def __matmul__(self, other):
            """score when result is self, against other"""
            if ~self == self.win:
                return +other+6
            if ~self == self.lose:
                return -other
            return ~other+3

    Et la résolution est triviale derrière :

    import sys
    input = [l.split(" ") for l in sys.stdin.read().strip().split("\n")]
    values = dict(
        A=chifumi(1),
        B=chifumi(2),
        C=chifumi(3),
        X=chifumi(1),
        Y=chifumi(2),
        Z=chifumi(3),
    )
    
    score1 = sum(values[me] % values[you] for you, me in input)
    score2 = sum(values[me] @ values[you] for you, me in input)
    
    print(f"Strategy 1 = {score1}")
    print(f"Strategy 2 = {score2}")
    • Yth.
  • [^] # Re: En Python

    Posté par  (Mastodon) . En réponse au message Avent du Code, jour 6. Évalué à 3.

    Franchement, je ne fais pas mieux.
    Toute optimisation semble compliquer terriblement le code.
    Et vu la taille des données…

    C'est quoi la citation à propos des optimisations trop tôt dans le code ? :)

    • Yth.
  • [^] # Re: Serverless ?

    Posté par  (Mastodon) . En réponse au journal Neon : Postgresql serverless avec branches . Évalué à 6.

    L'idée c'est que le « cloud » en lui-même a des serveurs, mais ton appli dessus n'en a pas, il est exécuté magiquement par de la puissance de calcul brute sortie du ciel, et le stockage est global sans espace dédié.

    Donc de ton point de vue c'est sans serveur, que de la magie, tu alloues de la puissance d'un côté, du stockage de l'autre, et bingo.

    Bien sûr derrière c'est très terre à terre avec des fermes de centaines de milliers de serveurs…

    Mais le sens est là.

    • Yth.
  • # En France on stocke plus, sans Lithium

    Posté par  (Mastodon) . En réponse au lien En Belgique, on stocke l'électricité (100 MWh) pour gérer les pics (et épargner le climat ?). Évalué à 4.

    Mais il faut des montagnes pour ça.

    La plus grosse batterie de France peut délivrer une puissance de 1,8Gw (deux réacteurs nucléaires) pendant 48h, sous 2 minutes.
    Elle sert en recharge avec les surplus de production, et en décharge face aux pics de production.

    C'est une conduite forcée de 7m de diamètre sur plus de 1000m de dénivelé, 12 turbines hydrauliques, dont 8 réversibles, pour repomper l'eau du bas vers le haut, donc deux lacs de retenue, et deux écosystèmes totalement transformés.
    Quantité de CO2 assez négligeable (plein de béton, des dizaines milliers de tonnes d'acier, un paquet d'ordinateurs) par rapport à la puissance. A priori pas de pollution à l'utilisation. Pas d'usure outre-mesure, au delà de l'entretient d'un gros ouvrage de type barrage hydraulique. Il tourne depuis 35 ans, et n'a pas a priori de limite d'usure, donc tout ces matériaux c'est un investissement unique.

    Il s'agit de Grand-Maison, en Isère.
    Le vidage/pompage fait perdre ~20% d'énergie, donc quand on produit 1GWh, il faut 1,2GWh pour recharger la batterie, c'est moins efficace que des batteries au lithium (apparemment proches des 100%), mais bien plus durable et moins polluant.

    Mais faut des montagnes, la Belgique en est relativement dépourvue : elle culmine à 694m d'altitude…

    Il y a 6 batteries de ce type en France pour une puissance totale de 5GW (mais je n'ai pas l'énergie totale stockable en GWh).
    La plus ancienne datant de 1934, est démantelée en 2014, elle a essuyé les plâtres des défauts d'ingénierie, mais 80 ans c'est pas mal déjà pour un truc qui a explosé à la première mise en service.
    Heureusement on fait mieux aujourd'hui…
    Il n'est pas spécialement évident de pouvoir accroître cette capacité (il faut des lieux, et il faut casser des écosystème, inonder des lacs de retenue, etc), mais la France n'a peut-être pas besoin de plus que ça ?

    Évidemment ça sert en complément d'une énergie produite « à perte », ça peut être utile avec un parc éolien/solaire dont la production dépend assez peu de la consommation recherchée, mais c'est surtout utile avec des centrales nucléaires qui ont une production constante, ça permet de lisser.
    Ça sert aussi à kickstarter des centrales nucléaires, qui ont besoin d'un paquet de jus pour démarrer.

    Et tout ça c'est la raison pour laquelle il ne faut pas découpler l'hydraulique du nucléaire en France.

    Bref, je suis pas hyper convaincu pour le stockage au lithium, mais si on n'a pas de montagnes pour faire des piles hydrauliques, c'est peut-être la seule solution envisageable.
    Ça me paraît être une aberration écologique cependant…
    Le recul qu'on a sur l'usure des batteries au lithium des téléphones portables est assez déprimant, c'est un gâchis délirant de métaux rares et polluants !

    On ferait (l'Europe, la Belgique) probablement mieux de faire de la recherche sur des batteries biologiques.

    • Yth.
  • [^] # Re: Ridicule, pas sérieux

    Posté par  (Mastodon) . En réponse au lien La 5G est-elle soluble dans la sobriété ?. Évalué à 5.

    De plus, et c’est l’argument principal de l’article, absolument aucune compatibilité descendante n’a été prévue. On est en plein dans l’obsolescence programmée.

    En fait, il y a exactement la même compatibilité descendante qu'entre la 4G et la 3G, ou la 2G.
    C'est à dire que les réseaux fonctionnent en parallèle et qu'il y a des passerelles pour que ta connexion passant d'un coup de Edge à 5G, tu ne sois pas déco/reco, tu gardes ta conversation téléphonique fluide, etc.

    • Yth.
  • [^] # Re: Discussion sur HN

    Posté par  (Mastodon) . En réponse au lien Scaling Mastodon is Impossible. Évalué à 2. Dernière modification le 17 novembre 2022 à 09:41.

    Mais Twitter ET Mastodon sont client-serveur, ça n'a aucun rapport avec la centralisation, la décentralisation ou la fédération.

    Le web est fédéré : tous les serveurs parlent le même langage (HTTP(S), aux variantes près), tous les clients parlent le même langage, on peut accéder à n'importe quel serveur avec n'importe quel client, et n'importe quel site web peut faire un lien vers n'importe quel autre (techniquement, juridiquement ou commercialement, c'est une autre question).

    Après, à la différence de trucs comme Mastodon ou XMPP, ou le mail, tu ne passes pas par un serveur pour aller vers un autre (même si c'est possible, avec des proxy), donc c'est peut-être plus décentralisé que fédéré, il n'y a vraiment pas de lien entre les serveurs, à part les liens URL entre les pages webs.

    Cela dit, la question est plus sur décentralisé/centralisé.
    La fédération c'est une façon de décentraliser, c'est tout.

    • Yth.