#!/usr/bin/env python import itertools import sys from typing import NamedTuple class Point(NamedTuple): x: int y: int class Light(NamedTuple): pos: Point vel: Point def at(self, time: int) -> Point: return Point(self.pos.x + self.vel.x * time, self.pos.y + self.vel.y * time) def solve(input: str) -> str: def parse_light(input: str) -> Light: pos, vel = map(lambda s: s.split("=<")[1].removesuffix(">"), input.split("> ")) return Light( Point(*map(int, map(str.strip, pos.split(",")))), Point(*map(int, map(str.strip, vel.split(",")))), ) def parse(input: list[str]) -> list[Light]: return [parse_light(line) for line in input] def move_lights(lights: list[Light], time: int) -> set[Point]: return {l.at(time) for l in lights} def bbox_size(points: set[Point]) -> int: min_x, max_x = min(p.x for p in points), max(p.x for p in points) min_y, max_y = min(p.y for p in points), max(p.y for p in points) return (max_x - min_x + 1) * (max_y - min_y + 1) def write_message(message: set[Point]) -> str: min_x, max_x = min(p.x for p in message), max(p.x for p in message) min_y, max_y = min(p.y for p in message), max(p.y for p in message) return "\n".join( "".join( "#" if Point(x, y) in message else "." for x in range(min_x, max_x + 1) ) for y in range(min_y, max_y + 1) ) lights = parse(input.splitlines()) max_time = max( map(abs, itertools.chain.from_iterable((l.pos.x, l.pos.y) for l in lights)) ) message = min((move_lights(lights, t) for t in range(max_time)), key=bbox_size) return write_message(message) def main() -> None: input = sys.stdin.read() print(solve(input)) if __name__ == "__main__": main()