• # En Python, modélisé

    Posté par (site web personnel) . En réponse au message Avent du Code, jour 10. Évalué à 5. Dernière modification le 10 décembre 2022 à 13:03.

    Sans surprise, j'ai implémenté un CPU selon les spécifications fournies. Mais pour ce qui est de faire quelque chose à partir des états qu'il atteint à des cycles donnés, je me suis amusé à y introduire ce que j'ai appelé un débogueur, je vous laisse découvrir ça.

    import io
    from enum import Enum
    from typing import Callable, Iterable, Iterator, List, Optional, Tuple
    class Operation(Enum):
     addx = ('addx', 1, 2)
     noop = ('noop', 0, 1)
     def __init__(self, word: str, nargs: int, cycles: int) -> None:
     self.word = word
     self.nargs = nargs
     self.cycles = cycles
    class Instruction:
     def __init__(self, op: Operation, *args: int) -> None:
     self.op = op
     if len(args) != op.nargs:
     raise ValueError(
     "operator {} expect {} arguments".format(op, op.nargs))
     self.args = args
    class CPU:
     def __init__(self, program: Iterable[Instruction],
     debug: Optional[Callable[[int, int], None]] = None) -> None:
     self.X = 1
     self.cycle = 1
     self.program = program
     self.debug = debug
     def _apply(self, instruction: Instruction) -> None:
     if instruction.op is Operation.addx:
     self.X += instruction.args[0]
     elif instruction.op is Operation.noop:
     pass
     def _cycle(self) -> None:
     if self.debug is not None:
     self.debug(self.cycle, self.X)
     self.cycle += 1
     def run(self) -> None:
     last_instruction = None
     for instruction in self.program:
     for _ in range(instruction.op.cycles):
     self._cycle()
     self._apply(instruction)
    def import_program(lines: Iterable[str]) -> Iterator[Instruction]:
     for line in lines:
     words = line.split()
     op = Operation[words[0]]
     args = [int(word) for word in words[1:]]
     yield Instruction(op, *args)
    class StrengthSum:
     def __init__(self) -> None:
     self.strength = 0
     def __call__(self, cycle: int, value: int) -> None:
     if cycle in (20, 60, 100, 140, 180, 220):
     self.strength += cycle * value
    class CRTDrawer:
     def __init__(self) -> None:
     self.crt = io.StringIO()
     @staticmethod
     def position(cycle: int) -> int:
     return (cycle - 1) % 40
     def __call__(self, cycle: int, value: int) -> None:
     position = self.position(cycle)
     if position == 0:
     self.crt.write('\n')
     if value - 1 <= position <= value + 1:
     self.crt.write('█')
     else:
     self.crt.write(' ')
    class MultiDebug:
     def __init__(self, *debugs: Callable[[int, int], None]) -> None:
     self.debugs = debugs
     def __call__(self, cycle: int, value: int) -> None:
     for debug in self.debugs:
     debug(cycle, value)
    def solve_both(lines: Iterable[str]) -> Tuple[int, str]:
     """Solve part 1 of today's puzzle"""
     program = import_program(lines)
     strength_report = StrengthSum()
     crt_drawer = CRTDrawer()
     cpu = CPU(program, debug=MultiDebug(strength_report, crt_drawer))
     cpu.run()
     return strength_report.strength, crt_drawer.crt.getvalue()