4
4
5
5
from collections import namedtuple
6
6
from functools import partial
7
- from itertools import zip_longest
8
7
from math import lcm
9
- from typing import Generator
8
+ from typing import Generator , Iterable
10
9
11
10
SAMPLE_INPUT = """
12
11
p=0,4 v=3,-3
26
25
WIDTH , HEIGHT = 101 , 103
27
26
28
27
29
- class _Robot (namedtuple ("Robot" , ("x0" , "y0" , "vx" , "vy" ))):
30
- def __getitem__ (
31
- self , t : int , width : int = WIDTH , height : int = HEIGHT
32
- ) -> tuple [int , int ]:
33
- return (self .x0 + t * self .vx ) % width , (self .y0 + t * self .vy ) % height
28
+ class _Robot (namedtuple ("Robot1" , ("p0" , "v" , "m" ))):
29
+ def __getitem__ (self , t : int ) -> int :
30
+ return (self .p0 + t * self .v ) % self .m
34
31
35
32
36
- def _parse (data : str ) -> Generator [_Robot ]:
33
+ def _parse (
34
+ data : str , width : int = WIDTH , height : int = HEIGHT
35
+ ) -> Generator [tuple [_Robot , _Robot ]]:
37
36
for line in filter (None , data .splitlines ()):
38
37
p , v = line .split (maxsplit = 1 )
39
38
p = p [p .index ("=" ) + 1 :]
40
39
v = v [v .index ("=" ) + 1 :]
41
- yield _Robot (
42
- int (p [: p .index ("," )]),
43
- int (p [p .index ("," ) + 1 :]),
44
- int (v [: v .index ("," )]),
45
- int (v [v .index ("," ) + 1 :]),
40
+ yield (
41
+ _Robot (int (p [: p .index ("," )]), int (v [: v .index ("," )]), width ),
42
+ _Robot (int (p [p .index ("," ) + 1 :]), int (v [v .index ("," ) + 1 :]), height ),
46
43
)
47
44
48
45
@@ -53,8 +50,8 @@ def part1(data: str, width: int = WIDTH, height: int = HEIGHT) -> int:
53
50
"""
54
51
midpoint_x , midpoint_y = width // 2 , height // 2
55
52
q1 , q2 , q3 , q4 = 0 , 0 , 0 , 0
56
- for robot in _parse (data ):
57
- x , y = robot . __getitem__ ( 100 , width = width , height = height )
53
+ for xrobot , yrobot in _parse (data , width , height ):
54
+ x , y = xrobot [ 100 ], yrobot [ 100 ]
58
55
if x > midpoint_x and y > midpoint_y :
59
56
q1 += 1
60
57
if x < midpoint_x and y > midpoint_y :
@@ -66,19 +63,26 @@ def part1(data: str, width: int = WIDTH, height: int = HEIGHT) -> int:
66
63
return q1 * q2 * q3 * q4
67
64
68
65
69
- def _part2_key (robots : tuple [_Robot ], t : int ) -> int :
70
- positions = sorted (( robot [ t ] for robot in robots ))
71
- line , max_line = 1 , 0
72
- for ( x , y ), next in zip_longest ( positions , positions [ 1 :]):
73
- if ( x , y + 1 ) == next :
74
- line += 1
75
- else :
76
- line , max_line = 1 , max ( line , max_line )
77
- return - max_line , t
66
+ def _part2_key (robots : Iterable [_Robot ], t : int ) -> int :
67
+ h1 , h2 = 0 , 0
68
+ for robot in robots :
69
+ p = robot [ t ]
70
+ if p < robot . m // 2 :
71
+ h1 += 1
72
+ elif p > robot . m // 2 :
73
+ h2 + = 1
74
+ return max ( h1 , h2 )
78
75
79
76
80
77
def part2 (data : str ) -> int :
81
- return min (range (lcm (WIDTH * HEIGHT )), key = partial (_part2_key , tuple (_parse (data ))))
78
+ xrobots = []
79
+ yrobots = []
80
+ for xrobot , yrobot in _parse (data ):
81
+ xrobots .append (xrobot )
82
+ yrobots .append (yrobot )
83
+ x = max (range (WIDTH ), key = partial (_part2_key , xrobots ))
84
+ y = max (range (HEIGHT ), key = partial (_part2_key , yrobots ))
85
+ return (x + (y - x ) * pow (WIDTH , - 1 , HEIGHT ) * WIDTH ) % lcm (WIDTH , HEIGHT )
82
86
83
87
84
88
parts = (part1 , part2 )
0 commit comments