Forum Programmation.autre Advent of Code 2023 : Day 4

Posté par  (site web personnel) . Licence CC By‑SA.
Étiquettes :
3
4
déc.
2023

Jour 4 (résumé)

Partie 1

Le tĂ©lĂ©cabine vous dĂ©pose sur une masse de terre flottant dans le ciel, l'Ăźle de l'Ăźle. Le liftier pense qu'il y a sĂ»rement des sources sur cette Ăźle. En revanche, cela dĂ©passe ses compĂ©tences, il faudrait aller voir le jardinier, qui habite sur une Ăźle entourĂ©e d'eau, elle-mĂȘme sur l'Ăźle cĂ©leste oĂč nous nous trouvons.

Bref, notre lutin veut bien vous prĂȘter son bateau en Ă©change d'un peu d'aide avec ses cartes Ă  gratter.

Un lutin joueur

Chaque carte a une liste de nombre gagnants et une liste de nombre obtenus, par exemple :

Card 1: 41 48 83 86 17 | 83 86  6 31 17  9 48 53
Card 2: 13 32 20 16 61 | 61 30 68 82 17 32 24 19
Card 3:  1 21 53 59 44 | 69 82 63 72 16 21 14  1
Card 4: 41 92 73 84 69 | 59 84 76 51 58  5 54 83
Card 5: 87 83 26 28 32 | 88 30 70 12 93 22 82 36
Card 6: 31 18 13 56 72 | 74 77 10 23 35 67 36 11

C'est plutÎt simple, pour chaque carte on cherche les numéros gagnants qu'on possÚde :
* le premier vaut un point,
* les suivants doublent les points obtenus.

On fait la somme des valeurs des cartes et voilĂ .

La premiÚre carte a par exemple 4 numéros gagnants : 83, 86, 17 et 48, ce qui vaut 8 points.
La seconde carte a deux numéros gagnants et vaut 2 points. Et l'ensemble des cartes vaut au final 13 points.

Partie 2

Ces histoires de points, c'est n'importe quoi. On n'invente pas les rÚgles à sa guise, espÚce de sagouin ! Il y a des instructions écrites au dos des cartes : chaque carte vous fait gagner une copie supplémentaire d'autant de cartes qui la suivent qu'elle a de numéros gagnants.

Dans l'exemple, la premiÚre carte a 4 numéros gagnants, et crée donc une copie supplémentaire des cinq cartes qui la suivent, soit les 2, 3, 4 et 5. Puis on à la carte numéro 2, ou plutÎt les deux cartes 2, puisqu'on en a gagné une copie. Et ainsi de suite, avec la garantie qu'aucune carte ne vous fera déborder de la pile.

Enfin, quand on est arrivé au bout, on compte combien de cartes on a. Avec les données d'exemple, on obtient 30 cartes.

  • # En Python

    Posté par  (site web personnel) . Évalué à 4.

    #! /usr/bin/python3
    
    # Advent of Code 2023, day 4
    
    from __future__ import annotations
    
    from collections.abc import Iterable
    from typing import Self
    
    import re
    
    
    class Card:
        def __init__(self, id_: int, win_nums: Iterable[int], got_nums: Iterable[int]):
            self.id = id_
            self.win_nums = set(win_nums)
            self.got_nums = set(got_nums)
            self._matches: Optional[int] = None
    
        pattern = re.compile(r'^Card +(\d+): ([ \d]+) \| ([ \d]+)\n?$')
    
        @classmethod
        def import_line(cls, line: str) -> Self:
            if (m := cls.pattern.match(line)) is not None:
                return cls(int(m[1]), (int(word) for word in m[2].split()), (int(word) for word in m[3].split()))
            print(line)
            raise ValueError("invalid card description")
    
        @property
        def matches(self) -> int:
            if self._matches is None:
                self._matches = len(self.win_nums & self.got_nums)
            assert self._matches is not None
            return self._matches
    
        @staticmethod
        def _num_to_points(num: int) -> int:
            if num <= 0:
                return 0
            return 2 ** (num - 1)
    
        def points(self) -> int:
            return self._num_to_points(self.matches)
    
    
    def solve_both_parts(lines: aoc.Data) -> tuple[int, int]:
        """Solve puzzle parts 1 and 2: determine the sum of all card values and the total number of cards"""
        # Import
        cards: list[int, Card] = []
        for line in lines:
            card = Card.import_line(line)
            cards.append(card)
        # Part 1
        points = sum(card.points() for card in cards)
        # Part 2
        copies = [1 for card in cards]
        for i, card in enumerate(cards):
            for j in range(i + 1, i + 1 + card.matches):
                copies[j] += copies[i]
        return points, sum(copies)
    • [^] # Re: En Python

      Posté par  (Mastodon) . Évalué à 2.

      J'aime bien, c'est assez propre et concis !

      Je suis sur quelque chose de nettement moins abstrait, mais l'exercice du jour est assez simple je trouve.

      import sys
      def read_element(winning, numbers):
        winning = set(int(x) for x in winning.strip().split(" ") if x)
        numbers = set(int(x) for x in numbers.strip().split(" ") if x)
        return winning, numbers
      
      def input():
        for line in sys.stdin:
          game, elements = line.strip().split(':')
          game = int(game.split(" ")[-1].strip())
          winning, numbers = read_element(*elements.strip().split("|"))
          i = numbers.intersection(winning)
          num = len(i)
          score = 2**(num - 1) if i else 0
          yield game, num, score
      
      datas = [x for x in input()]
      r = sum(s for *_, s in datas)
      print(f"Score : {r}")
      
      winnings = {card: 1 for card, *_ in datas}
      for card, num, s in datas:
        wins = winnings[card]
        for i in range(card + 1, card + num + 1):
          winnings[i] += wins
      
      print(f"Total cards: {sum(winnings.values())}")

      Et le calcul est immédiat aussi.

      • Yth.
  • # Simple et rapide

    Posté par  . Évalué à 1.

    Une solution simple mais efficace :

    #!/bin/python3
    
    def game_number(game):
        return int(game.split(":")[0][5:])
    
    def find_winning(game):
        game = game.split(":")[1].split("|")[0].split(" ")
        w = []
        for n in game:
            if n != "":
                w.append(int(n))
        return w
    
    def nb_winning(game, winning):
        game = game.split(":")[1].split("|")[1].split(" ")
        nb = 0
        for n in game:
            if n != "":
                if int(n) in winning:
                    nb += 1
        return nb
    
    def solve1(puzzle,testing=False):
        s=0
        for game in puzzle:
            if game == "":
                continue
            nb = nb_winning(game, find_winning(game))
            if nb == 0:
                continue
            points = 1 * (2 ** (nb-1))
            if testing:
                print(game,"had", points, "points beacause of", nb, "winning numbers.")
            s += points
        if testing:
            print(s)
        return s
    
    def solve2(puzzle,testing=False):
        s = 0
        for i in range(puzzle.count("")):
            puzzle.remove("")
        wons = [1 for i in range(len(puzzle))]
        for game in puzzle:
            num = game_number(game)
            nb = nb_winning(game, find_winning(game))
            if testing:
                print("You had", wons[num-1], "cards", num, "who give you", nb, "cards.")
            for i in range(nb):
                wons[num+i] += wons[num-1]
        s = sum(wons)
        if testing:
            print(s)
        return s
    
    test1 = """Card 1: 41 48 83 86 17 | 83 86  6 31 17  9 48 53
    Card 2: 13 32 20 16 61 | 61 30 68 82 17 32 24 19
    Card 3:  1 21 53 59 44 | 69 82 63 72 16 21 14  1
    Card 4: 41 92 73 84 69 | 59 84 76 51 58  5 54 83
    Card 5: 87 83 26 28 32 | 88 30 70 12 93 22 82 36
    Card 6: 31 18 13 56 72 | 74 77 10 23 35 67 36 11
    """
    result1 = 13
    test2 = test1
    result2 = 30
    
    def solve(short=False):
    
        print("----Part 1----")
        if short == False:
            if solve1(test1.split("\n"),testing=True) != result1:
                print("Not working.")
                return False
            else :
                print("Maybe working?")
        with open("input.txt",'r') as file:
            lines = file.read().split("\n")
            s1 = solve1(lines)
            print(s1)
    
        print("----Part 2----")
        if short == False:
            if solve2(test2.split("\n"),testing=True) != result2:
                print("Not working.")
                return False
            else :
                print("Maybe working?")
        with open("input.txt",'r') as file:
            lines = file.read().split("\n")
            s2 = solve2(lines)
            print(s2)
    
        return s1, s2
    
    if __name__ == "__main__":
    
        from sys import argv
    
        try:
            if argv[1] == "--summary" or argv[1] == "-s":
                solve(short=True)
        except IndexError:
            solve()

    J'ai trouvé le problÚme étonnamment simple, j'y suis arrivé du premier coup pour les deux parties.

    Et contrairement à beaucoup de monde ce jour-là, cette solution est presque instantanée à l'exécution, de l'ordre de 0.01 sec.

    L'informatique n'est pas une science exacte, on n'est jamais Ă  l'abri d'un succĂšs

Suivre le flux des commentaires

Note : les commentaires appartiennent Ă  celles et ceux qui les ont postĂ©s. Nous n’en sommes pas responsables.