• # À 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.