<commit_before>"""Day 13: Care Package"""
from __future__ import annotations

from enum import Enum
from typing import Dict, List, Tuple

import aoc_common
from intcode import IntCode, parse_program

DAY = 13


class Tile(Enum):
    Empty = 0
    Wall = 1
    Block = 2
    HorizontalPaddle = 3
    Ball = 4


def render_screen(computer: IntCode) -> Dict[Tuple[int, int], Tile]:
    screen: Dict[Tuple[int, int], Tile] = {}
    while computer.has_output():
        x, y = computer.read_output(), computer.read_output()
        tile = Tile(computer.read_output())
        screen[(x, y)] = tile
    return screen


def main(program: List[int]) -> int:
    computer = IntCode(program)
    computer.run_until_halt()
    screen = render_screen(computer)
    num_blocks = len([pos for pos, tile in screen.items() if tile is Tile.Block])
    return num_blocks


if __name__ == "__main__":
    program = parse_program(aoc_common.load_puzzle_input(DAY))
    part_one_solution = main(program)

    assert (
        part_one_solution == 247
    ), "Part one solution doesn't match known-correct answer."

    aoc_common.report_solution(
        puzzle_title=__doc__, part_one_solution=part_one_solution
    )<commit_msg>2019 13 Python: Refactor with ArcadeCabinet class<commit_after>"""Day 13: Care Package"""
from __future__ import annotations

from enum import Enum
from typing import Dict, List, Tuple

import aoc_common
from intcode import HaltExecution, IntCode, parse_program

DAY = 13


class Tile(Enum):
    Empty = 0
    Wall = 1
    Block = 2
    HorizontalPaddle = 3
    Ball = 4


class JoystickPosition(Enum):
    Neutral = 0
    Left = -1
    Right = 1


class ArcadeCabinet:
    screen: Dict[Tuple[int, int], Tile] = {}
    computer: IntCode

    def __init__(self, program: List[int], play_for_free: bool = False):
        program = program[:]
        if play_for_free:
            program[0] = 2
        self.computer = IntCode(program)

    def play_until_game_over(self) -> None:
        try:
            while True:
                self.computer.step()
                self.render_screen()
        except HaltExecution:
            pass

    def render_screen(self) -> None:
        while len(self.computer.output_queue) >= 3:
            x, y = self.computer.read_output(), self.computer.read_output()
            tile = Tile(self.computer.read_output())
            self.screen[(x, y)] = tile


def main(program: List[int]) -> int:
    cabinet = ArcadeCabinet(program)
    cabinet.play_until_game_over()
    num_blocks = len(
        [pos for pos, tile in cabinet.screen.items() if tile is Tile.Block]
    )

    return num_blocks


if __name__ == "__main__":
    program = parse_program(aoc_common.load_puzzle_input(DAY))
    part_one_solution = main(program)

    assert (
        part_one_solution == 247
    ), "Part one solution doesn't match known-correct answer."

    aoc_common.report_solution(
        puzzle_title=__doc__, part_one_solution=part_one_solution
    )
{'fraction_non_alphanumeric': 0.09979906229068988, 'fraction_numerical': 0.012056262558606833, 'mean_word_length': 2.930263157894737, 'pattern_counts': {'":': 2, '<': 3, '<?xml version=': 0, '>': 9, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2942308', 'n_tokens_mistral': 1004, 'n_tokens_neox': 927, 'n_words': 248}