• # En Pypthon

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

    Pour la définition de aoc.group_lines(lines: str), cf. hier

    from __future__ import annotations
    import itertools
    from typing import Iterable, Iterator, List, Optional, Tuple, Union
    import aoc
    class Element:
     def __init__(self, value: Union[List[Element], int]):
     self.value = value
     def __lt__(self, other: Element) -> bool:
     if isinstance(self.value, int) and isinstance(other.value, int):
     return self.value < other.value
     if isinstance(self.value, list) and isinstance(other.value, list):
     for self_elt, other_elt in zip(self.value, other.value):
     if self_elt < other_elt:
     return True
     if other_elt < self_elt:
     return False
     # Ran out of elements on one of the lists
     return len(self.value) < len(other.value)
     if isinstance(self.value, int):
     return Element([self]) < other
     if isinstance(other.value, int):
     return self < Element([other])
     assert False # should not happen: we covered all cases
     def __eq__(self, other: object) -> bool:
     if not isinstance(other, Element):
     return NotImplemented
     if isinstance(self.value, int) and isinstance(other.value, int):
     return self.value == other.value
     if isinstance(self.value, list) and isinstance(other.value, list):
     return (len(self.value) == len(other.value)
     and all(self_elt == other_elt for self_elt, other_elt
     in zip(self.value, other.value)))
     if isinstance(self.value, int):
     return Element([self]) == other
     if isinstance(other.value, int):
     return self == Element([other])
     assert False # should not happen: we covered all cases
     def __str__(self) -> str:
     if isinstance(self.value, int):
     return str(self.value)
     elif isinstance(self.value, list):
     return '[{}]'.format(
     ','.join(str(element) for element in self.value))
     assert False # should not happen: we covered all cases
    def import_element(chars: Iterable[str]) -> Element:
     element: Optional[Element] = None
     lists: List[List[Element]] = [] # List[List[Element]]
     current_list: Optional[List[Element]] = None
     current_int: Optional[int] = None
     for char in chars:
     if char == '[':
     if current_int is not None:
     raise ValueError("unexpected beginning of list")
     if current_list is not None:
     lists.append(current_list)
     current_list = []
     elif char == ']':
     if current_list is None:
     raise ValueError("unexpected end of list")
     # If we were parsing an int, add it before closing current list
     if current_int is not None:
     current_list.append(Element(current_int))
     current_int = None
     if lists:
     # We are closing a sub-list
     prev_list = lists.pop()
     prev_list.append(Element(current_list))
     current_list = prev_list
     else:
     # We are closing the top-level list
     element = Element(current_list)
     current_list = None
     elif char.isdecimal():
     value = int(char)
     if current_int is None:
     current_int = value
     else:
     current_int = 10 * current_int + value
     continue
     elif char == ',':
     if current_list is None:
     raise ValueError("unexpected separator")
     if current_int is not None:
     current_list.append(Element(current_int))
     current_int = None
     elif char == '\n':
     pass
     else:
     raise ValueError("unexpected character")
     if element is None:
     raise ValueError("nothing to parse")
     return element
    def import_pairs(lines: Iterable[str]) -> Iterator[Tuple[Element, Element]]:
     for group in aoc.group_lines(lines):
     elements = [import_element(line) for line in group]
     if len(elements) != 2:
     raise ValueError("unexpected group length")
     yield (elements[0], elements[1])
    def import_elements(lines: Iterable[str]) -> Iterator[Element]:
     for line in lines:
     if line and line != '\n':
     yield import_element(line)
    def solve1(lines: Iterable[str]) -> int:
     """Solve part 1 of today's puzzle"""
     pairs = import_pairs(lines)
     return sum(i + 1 for i, (elt1, elt2) in enumerate(pairs) if elt1 < elt2)
    def solve2(lines: Iterable[str]) -> int:
     """Solve part 2 of today's puzzle"""
     elements: Iterable[Element] = import_elements(lines)
     div1 = import_element("[[2]]")
     div2 = import_element("[[6]]")
     elements = sorted(itertools.chain(elements, (div1, div2)))
     key = 1
     for i, element in enumerate(elements):
     if element == div1 or element == div2:
     key *= i + 1
     return key