1from __future__ import annotations
2
3import typing
4import dataclasses
5import re
6import collections
7
8@dataclasses.dataclass
9class Vector:
10 x: int
11 y: int
12
13 def __add__(self, b: typing.Self):
14 return Vector(self.x + b.x, self.y + b.y)
15
16@dataclasses.dataclass
17class Guard:
18 pos: Vector
19 vel: Vector
20
21 def move(self, shape: Vector):
22 self.pos.x = (self.pos.x + self.vel.x) % shape.x
23 self.pos.y = (self.pos.y + self.vel.y) % shape.y
24
25
26 def get_quadrant(self, shape: Vector):
27 if self.pos.x < shape.x // 2:
28 x = 0
29 elif shape.x - shape.x // 2 <= self.pos.x:
30 x = 1
31 else:
32 return None
33
34 if self.pos.y < shape.y // 2:
35 y = 0
36 elif shape.y - shape.y // 2 <= self.pos.y:
37 y = 1
38 else:
39 return None
40
41 return (x, y)
42
43def get_data(path):
44 with open(path) as f:
45 data = f.read()
46
47 for guard_line in data.strip().splitlines():
48 if m := re.search(r"p=(-?\d+),(-?\d+) v=(-?\d+),(-?\d+)", guard_line):
49 pos = Vector(int(m.group(1)), int(m.group(2)))
50 vel = Vector(int(m.group(3)), int(m.group(4)))
51 yield Guard(pos, vel)
52 else:
53 raise Exception("Could not parse line `{line}`")
54
55
56def main(guards: list[Guard], shape: Vector, iterations=100):
57 for _ in range(iterations):
58 for guard in guards:
59 guard.move(shape)
60
61 counter = collections.Counter(g.get_quadrant(shape) for g in guards)
62 total = 1
63 for k, v in counter.items():
64 if k is not None:
65 total *= v
66 return total
67
68if __name__ == "__main__":
69 # print(main(list(get_data("day_14_input_test.txt")), Vector(11, 7)))
70 print(main(list(get_data("day_14_input.txt")), Vector(101, 103)))
1from __future__ import annotations
2
3import typing
4import dataclasses
5import re
6import collections
7
8@dataclasses.dataclass
9class Vector:
10 x: int
11 y: int
12
13 def __add__(self, b: typing.Self):
14 return Vector(self.x + b.x, self.y + b.y)
15
16@dataclasses.dataclass
17class Guard:
18 pos: Vector
19 vel: Vector
20
21 def move(self, shape: Vector):
22 self.pos.x = (self.pos.x + self.vel.x) % shape.x
23 self.pos.y = (self.pos.y + self.vel.y) % shape.y
24
25
26 def get_quadrant(self, shape: Vector):
27 if self.pos.x < shape.x // 2:
28 x = 0
29 elif shape.x - shape.x // 2 <= self.pos.x:
30 x = 1
31 else:
32 return None
33
34 if self.pos.y < shape.y // 2:
35 y = 0
36 elif shape.y - shape.y // 2 <= self.pos.y:
37 y = 1
38 else:
39 return None
40
41 return (x, y)
42
43def get_data(path):
44 with open(path) as f:
45 data = f.read()
46
47 for guard_line in data.strip().splitlines():
48 if m := re.search(r"p=(-?\d+),(-?\d+) v=(-?\d+),(-?\d+)", guard_line):
49 pos = Vector(int(m.group(1)), int(m.group(2)))
50 vel = Vector(int(m.group(3)), int(m.group(4)))
51 yield Guard(pos, vel)
52 else:
53 raise Exception("Could not parse line `{line}`")
54
55
56def format_grid(guards, shape):
57 grid = [["." for _ in range(shape.x)] for _ in range(shape.y)]
58 for g in guards:
59 grid[g.pos.y][g.pos.x] = "x"
60
61 return "\n".join("".join(row) for row in grid)
62
63def main(guards: list[Guard], shape: Vector, iterations=10000):
64 for i in range(iterations):
65 for guard in guards:
66 guard.move(shape)
67 grid_str = format_grid(guards, shape)
68 if "x" * 10 in grid_str:
69 print(i + 1)
70 print(grid_str)
71
72if __name__ == "__main__":
73 # print(main(list(get_data("day_14_input_test.txt")), Vector(11, 7)))
74 print(main(list(get_data("day_14_input.txt")), Vector(101, 103)))