|
| 1 | +from GhostyUtils import aoc |
| 2 | +from GhostyUtils.vec2 import Vec2 |
| 3 | +from GhostyUtils.grid import Grid |
| 4 | +from collections import Counter |
| 5 | +import math |
| 6 | + |
| 7 | + |
| 8 | +aoc.argparser.add_argument("-d", "--dimensions", type=str, default="101,103", |
| 9 | + help="width,height") |
| 10 | +aoc.argparser.add_argument("-s", "--seconds", type=int, default=100) |
| 11 | + |
| 12 | + |
| 13 | +class Rectangle: |
| 14 | + def __init__(self, top_left: Vec2, bottom_right: Vec2) -> 'Rectangle': |
| 15 | + self.tl = top_left |
| 16 | + self.br = bottom_right |
| 17 | + |
| 18 | + def contains(self, other: Vec2) -> bool: |
| 19 | + return ((self.tl.x <= other.x < self.br.x) and |
| 20 | + (self.tl.y <= other.y < self.br.y)) |
| 21 | + |
| 22 | + |
| 23 | +class Robot: |
| 24 | + def __init__(self, position: Vec2, velocity: Vec2) -> 'Robot': |
| 25 | + self.pos = position |
| 26 | + self.v = velocity |
| 27 | + |
| 28 | + def __str__(self) -> str: |
| 29 | + return f"p={self.pos.x},{self.pos.y} v={self.v.x},{self.v.y}" |
| 30 | + |
| 31 | + def step(self, grid: Grid): |
| 32 | + self.pos += self.v |
| 33 | + self.pos.x %= grid.width() |
| 34 | + self.pos.y %= grid.height() |
| 35 | + |
| 36 | + |
| 37 | +def count_quadrants(robots: list[Robot], grid: Grid) -> list[int]: |
| 38 | + q_width = grid.width() // 2 |
| 39 | + q_height = grid.height() // 2 |
| 40 | + quadrants = [ |
| 41 | + Rectangle(Vec2(0, 0), Vec2(q_width, q_height)), |
| 42 | + Rectangle(Vec2(q_width + 1, 0), Vec2(grid.width(), q_height)), |
| 43 | + Rectangle(Vec2(0, q_height + 1), Vec2(q_width, grid.height())), |
| 44 | + Rectangle(Vec2(q_width + 1, q_height + 1), Vec2(grid.width(), grid.height())), |
| 45 | + ] |
| 46 | + |
| 47 | + count = [0]*4 |
| 48 | + for robot in robots: |
| 49 | + for q, quad in enumerate(quadrants): |
| 50 | + count[q] += 1 if quad.contains(robot.pos) else 0 |
| 51 | + |
| 52 | + return count |
| 53 | + |
| 54 | + |
| 55 | +def render_robots(robots: list[Robot], grid: Grid) -> str: |
| 56 | + return grid.render_with_overlays([Counter(robot.pos.as_tuple() for robot in robots)]) |
| 57 | + |
| 58 | + |
| 59 | +def main(): |
| 60 | + inputs = aoc.read_lines() |
| 61 | + |
| 62 | + robots = [] |
| 63 | + for line in inputs: |
| 64 | + robots.append(Robot(*(Vec2.from_str(vec[2:], ',') for vec in line.split()))) |
| 65 | + |
| 66 | + dimensions = Vec2.from_str(aoc.args.dimensions, ',') |
| 67 | + grid = Grid(['.' * dimensions.x] * dimensions.y) |
| 68 | + |
| 69 | + print("Initial state:") |
| 70 | + print(render_robots(robots, grid)) |
| 71 | + print() |
| 72 | + for s in range(aoc.args.seconds): |
| 73 | + for robot in robots: |
| 74 | + robot.step(grid) |
| 75 | + |
| 76 | + print(f"After {s+1} second{'s' if s > 1 else ''}:") |
| 77 | + print(render_robots(robots, grid)) |
| 78 | + print() |
| 79 | + |
| 80 | + print(f"p1: {math.prod(count_quadrants(robots, grid))}") |
| 81 | + |
| 82 | + |
| 83 | +if __name__ == "__main__": |
| 84 | + main() |
0 commit comments