• # En Python, modélisé

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

    #! /usr/bin/python3
    # Advent of Code 2022, day 9
    from __future__ import annotations
    from enum import Enum
    from typing import Iterable, Iterator, Set, Tuple
    Coords = Tuple[int, int]
    class Direction(Enum):
     O = ( 0, 0)
     U = ( 0, 1)
     D = ( 0, -1)
     L = (-1, 0)
     R = ( 1, 0)
     UL = (-1, 1)
     UR = ( 1, 1)
     DL = (-1, -1)
     DR = ( 1, -1)
     def __init__(self, dx, dy):
     self.dx = dx
     self.dy = dy
    class Knot:
     def __init__(self, x: int, y: int):
     self.x = x
     self.y = y
     @property
     def coords(self) -> Coords:
     return (self.x, self.y)
     def move(self, direction: Direction) -> None:
     self.x += direction.dx
     self.y += direction.dy
     def dist(self, other: Knot) -> int:
     """"Chebyshev distance! (Not really used in my code, actually)"""
     return max(abs(self.x - other.x), abs(self.y - other.y))
     def direction_to(self, other: Knot) -> Direction:
     dx = other.x - self.x
     dy = other.y - self.y
     if dx < -1:
     if dy < 0:
     return Direction.DL
     if dy == 0:
     return Direction.L
     if dy > 0:
     return Direction.UL
     if dx == -1:
     if dy < -1:
     return Direction.DL
     if -1 <= dy <= 1:
     return Direction.O
     if dy > 1:
     return Direction.UL
     if dx == 0:
     if dy < -1:
     return Direction.D
     if -1 <= dy <= 1:
     return Direction.O
     if dy > 1:
     return Direction.U
     if dx == 1:
     if dy < -1:
     return Direction.DR
     if -1 <= dy <= 1:
     return Direction.O
     if dy > 1:
     return Direction.UR
     if dx > 1:
     if dy < 0:
     return Direction.DR
     if dy == 0:
     return Direction.R
     if dy > 0:
     return Direction.UR
     assert False # cannot happen, all cases were covered
     def follow(self, other: Knot) -> None:
     self.move(self.direction_to(other))
    class Rope:
     def __init__(self, n_knots: int) -> None:
     self.knots = [Knot(0, 0) for _ in range(n_knots)]
     @property
     def head(self) -> Knot:
     return self.knots[0]
     @property
     def tail(self) -> Knot:
     return self.knots[-1]
     def move(self, direction: Direction) -> None:
     self.head.move(direction)
     for i in range(1, len(self.knots)):
     leader = self.knots[i - 1]
     follower = self.knots[i]
     follower.follow(leader)
    def import_lines(lines: Iterable[str]) -> Iterator[Direction]:
     for line in lines:
     word1, word2 = line.split()
     direction = Direction[word1]
     repeat = int(word2)
     for _ in range(repeat):
     yield direction
    def solve_both(lines: Iterable[str]) -> Tuple[int, int]:
     """Solve both parts of today's puzzle"""
     rope = Rope(10)
     visited1 = set() # type: Set[Coords]
     visited2 = set() # type: Set[Coords]
     for direction in import_lines(lines):
     rope.move(direction)
     visited1.add(rope.knots[1].coords)
     visited2.add(rope.tail.coords)
     return len(visited1), len(visited2)

    J'aurais bien aimé éviter d'énumérer tous les cas de mouvement de suivi, mais je n'ai rien trouvé d'astucieux pour éviter cela.

    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...