1
+ import re
2
+
3
+ from dataclasses import dataclass
4
+ from itertools import chain
5
+ from operator import attrgetter
6
+ from sys import argv as args
7
+
8
+ path = args [1 ] if len (args ) >= 2 else "sample-input.txt"
9
+ with open (path ) as f :
10
+ lines = f .read ().splitlines ()
11
+
12
+ @dataclass (frozen = True )
13
+ class Point :
14
+ x : int
15
+ y : int
16
+
17
+ def distance (self , other ):
18
+ return abs (self .x - other .x ) + abs (self .y - other .y )
19
+
20
+ @dataclass (frozen = True )
21
+ class Sensor :
22
+ pos : Point
23
+ beacon_pos : Point
24
+
25
+ def in_range (self , p ):
26
+ return self .pos .distance (p ) <= self .pos .distance (self .beacon_pos )
27
+
28
+ sensors = []
29
+ beacons = set ()
30
+
31
+ for line in lines :
32
+ nums = [int (x ) for x in re .findall ("[-\d]+" , line )]
33
+ beacon = Point (nums [2 ], nums [3 ])
34
+ beacons .add (beacon )
35
+ sensors .append (Sensor (
36
+ pos = Point (nums [0 ], nums [1 ]),
37
+ beacon_pos = beacon
38
+ ))
39
+
40
+ Y = 2000000
41
+
42
+ xlo = min (chain ((s .pos .x for s in sensors ), (s .beacon_pos .x for s in sensors ))) * 2
43
+ xhi = max (chain ((s .pos .x for s in sensors ), (s .beacon_pos .x for s in sensors ))) * 2
44
+
45
+ count = 0
46
+ for x in range (xlo , xhi + 1 ):
47
+ p = Point (x , Y )
48
+
49
+ if p in beacons :
50
+ continue
51
+
52
+ if any (s .in_range (p ) for s in sensors ):
53
+ count += 1
54
+
55
+ print (count )
56
+
57
+ @dataclass (frozen = True )
58
+ class Range :
59
+ lo : int
60
+ hi : int
61
+
62
+ def analyze_column (x ):
63
+ ranges = []
64
+ for sensor in sensors :
65
+ xDelta = abs (sensor .pos .x - x )
66
+ distance = sensor .pos .distance (sensor .beacon_pos )
67
+ yPos = sensor .pos .y
68
+ yDelta = distance - xDelta
69
+
70
+ yLo = yPos - yDelta
71
+ yHi = yPos + yDelta
72
+ if yLo <= yHi :
73
+ ranges .append (Range (yLo , yHi ))
74
+
75
+ ranges .sort (key = attrgetter ("lo" ))
76
+
77
+ for i in range (len (ranges ) - 1 ):
78
+ r1 = ranges [i ]
79
+ r2 = ranges [i + 1 ]
80
+ maybe_gap = r1 .hi < r2 .lo - 1
81
+ if maybe_gap :
82
+ gap_y = r1 .hi + 1
83
+ if all (gap_y < r .lo or gap_y > r .hi for r in ranges ):
84
+ return Point (x , gap_y )
85
+ return None
86
+
87
+ def tuning_frequency (p ):
88
+ return p .x * 4000000 + p .y
89
+
90
+ for x in range (4000001 ):
91
+ p = analyze_column (x )
92
+ if p :
93
+ print (tuning_frequency (p ))
94
+ break
0 commit comments