示例#1
0
    def from_file(cls, path: str) -> 'DayDescription':
        with open(path) as file:
            # """
            # Advent of Code 2020
            # Day 20: Jurassic Jigsaw
            # https://adventofcode.com/2020/day/20
            # """
            if next(file) != '"""\n':
                raise ValueError(f"missing description in {path}")
            try:
                year, = parse_line(next(file), "Advent of Code $\n")
                day, title = parse_line(next(file), "Day $: $\n")
            except AssertionError as exc:
                raise ValueError(
                    f"description in {path} in unexpected format") from exc

            aoc_url = next(file).strip()
            if aoc_url != f'https://adventofcode.com/{year}/day/{day}':
                raise ValueError(f"unexpected url in {path}")

            if next(file) != '"""\n':
                raise ValueError(
                    f"description missing closing quotes in {path}")

            return cls(year=int(year),
                       day=int(day),
                       title=title,
                       path=path,
                       aoc_url=aoc_url)
示例#2
0
        def rules() -> Iterable[Rule]:
            moves = {'left': -1, 'right': +1}
            while True:
                try:
                    empty_line = next(lines_it)
                    assert not empty_line
                except StopIteration:
                    # all rules read
                    return

                try:
                    current_state, = parse_line(next(lines_it), "In state $:")
                    for current_value in range(2):
                        assert next(
                            lines_it
                        ) == f"If the current value is {current_value}:"
                        write, = parse_line(next(lines_it),
                                            "- Write the value $.")
                        move_dir, = parse_line(next(lines_it),
                                               "- Move one slot to the $.")
                        next_state, = parse_line(next(lines_it),
                                                 "- Continue with state $.")
                        action = Action(int(write), moves[move_dir],
                                        next_state)
                        yield (current_state, current_value), action
                except StopIteration as stop:
                    # unexpected end of file/lines
                    raise EOFError() from stop
示例#3
0
def rules_from_lines(lines: Iterable[str]) -> dict[int, Rule]:
    rules: dict[int, Rule] = {}
    rule_refs: dict[int, list[list[int]]] = {}

    for line in lines:
        if '"' in line:
            # 1: "a"
            number_str, text = parse_line(line.strip(), '$: "$"')
            rule = RuleText(int(number_str), text)
            rules[rule.number] = rule

        else:
            # 2: 1 3
            # 6: 12
            # 8: 14 | 15
            # 10: 2 4 | 8 16
            number_str, rdef = parse_line(line.strip(), '$: $')
            rule_refs[int(number_str)] = [[
                int(v) for v in group_text.split(' ')
            ] for group_text in rdef.split(' | ')]

    while rule_refs:
        number, groups = next(
            (number, groups) for number, groups in rule_refs.items()
            if all(num in rules for group in groups for num in group))
        referenced_rules = [[rules[num] for num in group] for group in groups]
        rules[number] = RuleGroups(number, referenced_rules)
        del rule_refs[number]

    return rules
示例#4
0
 def from_lines(cls, name: str, lines: Iterable[str]) -> 'Character':
     lines = (line.strip() for line in lines)
     hit_points, = parse_line(next(lines), "Hit Points: $")
     damage, = parse_line(next(lines), "Damage: $")
     armor, = parse_line(next(lines), "Armor: $")
     return cls(name,
                hit_points=int(hit_points),
                damage=int(damage),
                armor=int(armor))
示例#5
0
    def from_str(cls, line: str) -> 'Event':
        # [1518-11-01 00:00] Guard #10 begins shift
        # [1518-11-01 00:05] falls asleep

        ts_str, action = parse_line(line, "[$] $")
        time = Timestamp.from_str(ts_str)
        if action.startswith("Guard #"):
            guard_id, what = parse_line(action, "Guard #$ $")
            return cls(time, int(guard_id), what)
        else:
            return cls(time, None, action)
示例#6
0
 def instructions_from_lines():
     for line in lines:
         line = line.strip()
         if line.startswith('mask'):
             (mask_string,) = parse_line(line, 'mask = $')
             yield 'mask', BitMask(mask_string)
         elif line.startswith('mem'):
             address, value = parse_line(line, 'mem[$] = $')
             yield 'mem', int(address), int(value)
         else:
             raise ValueError(line)
示例#7
0
def load_walls(fn: str) -> Iterable[Pos]:
    with open(fn) as file:
        for line in file:
            if line.startswith('x='):
                # x=651, y=334..355
                x, y1, y2 = (int(v) for v in parse_line(line, 'x=$, y=$..$\n'))
                assert y1 <= y2
                yield from ((x, y) for y in range(y1, y2 + 1))
            elif line.startswith('y='):
                # y=1708, x=505..523
                y, x1, x2 = (int(v) for v in parse_line(line, 'y=$, x=$..$\n'))
                assert x1 <= x2
                yield from ((x, y) for x in range(x1, x2 + 1))
            else:
                raise ValueError(line)
def roads_from_lines(lines: Iterable[str]) -> Iterable[Road]:
    # "London to Dublin = 464"
    for line in lines:
        place_1, place_2, distance_str = parse_line(line.strip(), "$ to $ = $")
        distance = int(distance_str)
        yield (place_1, place_2), distance
        yield (place_2, place_1), distance
示例#9
0
def coordinates_from_text(text: str) -> tuple[int, int]:
    row, col = parse_line(
        text.strip(),
        "To continue, please consult the code grid in the manual.  "
        "Enter the code at row $, column $.")

    return int(row), int(col)
示例#10
0
 def from_str(cls, line: str) -> 'Rotation':
     s_x, s_y, s_z = parse_line(line, 'x->$, y->$, z->$')
     return cls(
         x_to=cls.axis_from_str(s_x),
         y_to=cls.axis_from_str(s_y),
         z_to=cls.axis_from_str(s_z)
     )
示例#11
0
    def from_lines(cls, lines: Iterable[str]) -> 'Tower':
        # name -> weight, names of subtowers
        protos: dict[str, tuple[int, list[str]]] = {}
        # name -> parent (tower it stands on)
        depends_on: dict[str, str] = {}

        for line in lines:
            line = line.strip()
            # enurd (528) -> gpaljor, ncuksjv, ozdrm, qkmsfo
            name, weight, children_part = parse_line(line, "$ ($)$")
            children = children_part.removeprefix(' -> ').split(', ') if children_part else []
            protos[name] = (int(weight), children)
            for child in children:
                depends_on[child] = name

        def find_root_tower_name(dependences: dict[str, str]) -> str:
            # take any name ...
            key = next(iter(dependences.keys()))
            # ... and traverse to parents until you reach the root (bottom) tower,
            while key in dependences:
                key = dependences[key]
            # ... which is not dependent on any other tower
            return key

        return cls._from_protos(find_root_tower_name(depends_on), protos)
示例#12
0
 def from_str(cls, line: str) -> 'Claim':
     id_, left, top, width, height = parse_line(line, "#$ @ $,$: $x$")
     return cls(id_=int(id_),
                top=int(top),
                left=int(left),
                width=int(width),
                height=int(height))
示例#13
0
def data_from_lines(
        lines: Iterable[str]) -> Iterable[tuple[PasswordRule, str]]:
    for line in lines:
        # 1-3 b: cdefg
        min_count, max_count, character, password = parse_line(
            line.strip(), "$-$ $: $")
        yield PasswordRule(int(min_count), int(max_count), character), password
示例#14
0
 def from_str(cls, line: str) -> 'Reindeer':
     # 'Vixen can fly 19 km/s for 7 seconds, but then must rest for 124 seconds.'
     args = parse_line(
         line,
         "$ can fly $ km/s for $ seconds, but then must rest for $ seconds."
     )
     # pylint: disable=no-value-for-parameter
     return cls(*args)
示例#15
0
    def command(self, cmd: str) -> None:
        # rect 2x1
        if cmd.startswith("rect "):
            rect_width, rect_height = parse_line(cmd, "rect $x$")
            self.rect(int(rect_width), int(rect_height))

        # rotate row y=0 by 3
        elif cmd.startswith("rotate row"):
            row_y, right = parse_line(cmd, "rotate row y=$ by $")
            self.rotate_row(int(row_y), int(right))

        # rotate column x=8 by 2
        elif cmd.startswith("rotate column"):
            column_x, down = parse_line(cmd, "rotate column x=$ by $")
            self.rotate_column(int(column_x), int(down))

        else:
            raise ValueError(cmd)
示例#16
0
def discs_from_lines(lines: Iterable[str]) -> Iterable[Disc]:
    # "Disc #1 has 5 positions; at time=0, it is at position 4."
    for line_no, line in enumerate(lines):
        disc_no, size, pos0 = parse_line(
            line=line.strip(),
            pattern="Disc #$ has $ positions; at time=0, it is at position $."
        )
        assert line_no + 1 == int(disc_no)
        yield int(pos0), int(size)
示例#17
0
def move_from_str(line: str) -> Move:
    match line[0]:
        case 's':
            # s1
            length, = parse_line(line, "s$")
            return Spin(int(length))
        case 'x':
            # x3/4
            pos_1, pos_2 = parse_line(line, "x$/$")
            return Exchange(int(pos_1), int(pos_2))
        case 'p':
            # pe/b
            dancer_1, dancer_2 = parse_line(line, "p$/$")
            return Partner(dancer_1, dancer_2)
        case _:
            raise ValueError(line)

    # TODO: remove when mypy realizes this is unreachable
    assert False
示例#18
0
def input_from_lines(lines: Iterable[str]) -> tuple[State, Rules]:
    lines_it = (line.strip() for line in lines)
    # initial state
    initial_state_str, = parse_line(next(lines_it), "initial state: $")
    initial_state = State.from_line(initial_state_str)
    # empty line
    assert not next(lines_it)
    # rules
    rules = {rule[0] for line in lines_it if (rule := rule_from_line(line))[1]}

    return initial_state, rules
示例#19
0
def rule_from_str(line: str) -> Rule:
    # 'Alice would gain 54 happiness units by sitting next to Bob.'
    # 'Alice would lose 79 happiness units by sitting next to Carol.'
    name1, verb, amount_str, name2 = parse_line(
        line, pattern="$ would $ $ happiness units by sitting next to $.")

    amount = int(amount_str)
    assert verb in ("lose", "gain")
    if verb == "lose":
        amount = -amount

    return (name1, name2), amount
示例#20
0
    def from_str(cls, line: str) -> 'Node':
        # '/dev/grid/node-x0-y2  85T  73T  12T  85%'
        name, size_str, used_str, avail_str, _ = [p for p in line.strip().split(' ') if p]
        x, y = parse_line(name, "/dev/grid/node-x$-y$")

        pos = (int(x), int(y))
        size = int(size_str.rstrip('T'))
        used = int(used_str.rstrip('T'))
        avail = int(avail_str.rstrip('T'))
        assert size == used + avail

        return Node(pos, size, used)
示例#21
0
    def from_str(cls, line: str) -> 'Command':
        line = line.strip()

        if line.startswith("swap position "):
            # swap position 4 with position 0
            pos_1, pos_2 = parse_line(line, "swap position $ with position $")
            return cls('swap_pos', int(pos_1), int(pos_2))

        elif line.startswith("swap letter "):
            # swap letter d with letter b
            letter_1, letter_2 = parse_line(line, "swap letter $ with letter $")
            return cls('swap_letter', letter_1, letter_2)

        elif line.startswith("rotate left ") or line.startswith("rotate right "):
            # rotate left 1 step
            direction, steps, _ = parse_line(line, "rotate $ $ step$")
            return cls('rot_steps', direction, int(steps))

        elif line.startswith("rotate based "):
            # rotate based on position of letter b
            letter, = parse_line(line, "rotate based on position of letter $")
            return cls('rot_letter', letter)

        elif line.startswith("reverse positions "):
            # reverse positions 0 through 4
            pos_1, pos_2 = parse_line(line, "reverse positions $ through $")
            return cls('reverse', int(pos_1), int(pos_2))

        elif line.startswith("move position "):
            # move position 1 to position 4
            pos_1, pos_2 = parse_line(line, "move position $ to position $")
            return cls('move', int(pos_1), int(pos_2))

        else:
            raise ValueError(line)
示例#22
0
    def from_str(cls, line: str) -> 'Cuboid':
        def parse_edge(edge: str) -> tuple[int, int]:
            if '..' in edge:
                left, right = edge.split('..')
                return int(left), int(right)
            else:
                return int(edge), int(edge)

        e_x, e_y, e_z = parse_line(line.strip(), 'x=$,y=$,z=$')
        x_0, x_1 = parse_edge(e_x)
        y_0, y_1 = parse_edge(e_y)
        z_0, z_1 = parse_edge(e_z)
        return cls(corner_0=(x_0, y_0, z_0), corner_1=(x_1, y_1, z_1))
示例#23
0
    def from_lines(cls, lines: Iterable[str]) -> 'Machine':
        lines_it = (line.strip() for line in lines)

        init_state, = parse_line(next(lines_it), "Begin in state $.")
        steps, = parse_line(next(lines_it),
                            "Perform a diagnostic checksum after $ steps.")

        def rules() -> Iterable[Rule]:
            moves = {'left': -1, 'right': +1}
            while True:
                try:
                    empty_line = next(lines_it)
                    assert not empty_line
                except StopIteration:
                    # all rules read
                    return

                try:
                    current_state, = parse_line(next(lines_it), "In state $:")
                    for current_value in range(2):
                        assert next(
                            lines_it
                        ) == f"If the current value is {current_value}:"
                        write, = parse_line(next(lines_it),
                                            "- Write the value $.")
                        move_dir, = parse_line(next(lines_it),
                                               "- Move one slot to the $.")
                        next_state, = parse_line(next(lines_it),
                                                 "- Continue with state $.")
                        action = Action(int(write), moves[move_dir],
                                        next_state)
                        yield (current_state, current_value), action
                except StopIteration as stop:
                    # unexpected end of file/lines
                    raise EOFError() from stop

        return cls(init_state=init_state, steps=int(steps), rules=rules())
示例#24
0
def instructions_from_lines(lines: Iterable[str]) -> Instructions:
    # 'value 5 goes to bot 2'
    # 'bot 2 gives low to bot 1 and high to bot 0'

    init: MutableState = defaultdict(list)
    comparisons: Comparisons = {}

    for line in lines:
        line = line.strip()

        if "goes to" in line:
            value, target = parse_line(line, "value $ goes to $")
            init[target].append(int(value))

        elif "gives low" in line:
            bot, low, high = parse_line(line, "$ gives low to $ and high to $")
            assert bot.startswith("bot ")
            assert bot not in comparisons
            comparisons[bot] = (low, high)

        else:
            raise ValueError(line)

    return frozen_state(init), comparisons
示例#25
0
def parse_rule(text: str) -> Rule:
    """
    >>> parse_rule("light red bags contain 1 bright white bag, 2 muted yellow bags.")
    ('light red', [(1, 'bright white'), (2, 'muted yellow')])
    >>> parse_rule("bright white bags contain 1 shiny gold bag.")
    ('bright white', [(1, 'shiny gold')])
    >>> parse_rule("faded blue bags contain no other bags.")
    ('faded blue', [])
    """

    color, inside_text = parse_line(text.strip(), "$ bags contain $.")

    if inside_text != "no other bags":
        inside = [parse_count_color(part) for part in inside_text.split(", ")]
    else:
        inside = []

    return color, inside
示例#26
0
 def from_line(cls, line: str):
     # mxmxvkd kfcds sqjhc nhms (contains dairy, fish)
     ingredients, allergens = parse_line(line, "$ (contains $)")
     return cls(ingredients.split(" "), allergens.split(", "))
示例#27
0
 def parse_line(cls, line: str) -> 'Instruction':
     # fold along x=655
     axis, value = parse_line(line, 'fold along $=$')
     return cls(**{axis: int(value)})
示例#28
0
 def from_str(cls, line: str) -> 'Instruction':
     phrases = ('turn on', 'turn off', 'toggle')
     phrase = next(p for p in phrases if line.startswith(p + ' '))
     rect_str = line[len(phrase):]
     x1, y1, x2, y2 = parse_line(rect_str, '$,$ through $,$')
     return cls(phrase, Rect((int(x1), int(y1)), (int(x2), int(y2))))
示例#29
0
 def from_str(cls, line: str) -> 'Rule':
     # "../.# => ##./#../..."
     g_from, g_to = parse_line(line.strip(), "$ => $")
     return cls(Grid.from_line(g_from), Grid.from_line(g_to))
示例#30
0
 def parse_day_number(fn: str) -> int:
     num, _ = parse_line(fn, "day$_$.py")
     return int(num)