-
Notifications
You must be signed in to change notification settings - Fork 0
/
day-15.py
212 lines (178 loc) · 5.07 KB
/
day-15.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
# import all the utils I might use
from __future__ import annotations
from aocd import get_data
from aocd import submit
import re
from dataclasses import dataclass, field
import typing as T
from collections import defaultdict
from pprint import pprint
import math
import itertools
aoc_data = get_data(day=15, year=2022)
# problem part is 1 or 2
problem_part = 2
# test
test_data = \
"""Sensor at x=2, y=18: closest beacon is at x=-2, y=15
Sensor at x=9, y=16: closest beacon is at x=10, y=16
Sensor at x=13, y=2: closest beacon is at x=15, y=3
Sensor at x=12, y=14: closest beacon is at x=10, y=16
Sensor at x=10, y=20: closest beacon is at x=10, y=16
Sensor at x=14, y=17: closest beacon is at x=10, y=16
Sensor at x=8, y=7: closest beacon is at x=2, y=10
Sensor at x=2, y=0: closest beacon is at x=2, y=10
Sensor at x=0, y=11: closest beacon is at x=2, y=10
Sensor at x=20, y=14: closest beacon is at x=25, y=17
Sensor at x=17, y=20: closest beacon is at x=21, y=22
Sensor at x=16, y=7: closest beacon is at x=15, y=3
Sensor at x=14, y=3: closest beacon is at x=15, y=3
Sensor at x=20, y=1: closest beacon is at x=15, y=3"""
use_real_data = False
# un-comment below when ready to use real data
use_real_data = True
if use_real_data:
print("Using AOC data")
data = aoc_data
else:
print("Using test data")
data = test_data
lines = data.split('\n')
"""
thought process:
- build list of sensors, and beacons
- get min_x, and max_x from sensors
- get max(m_d...) for sensor to beacon
- for row y=row, from left to right,
- check if beacon exists there
- for sensor in sensors, check if m_d(sensor, beacon) is higher than m_d(sensor, point)
- if beacon is close than current point, current point still possible
"""
# --------------- part 1 ------------
def m_d(a, b):
return sum(abs(a_c - b_c) for a_c, b_c in zip(a, b))
sensors = []
beacons = []
beacons_set = set()
s_b_m_d = []
min_x = None
max_x = None
max_radius = 0
for line in lines:
s_x, s_y, b_x, b_y = map(
int,
re.match(r'Sensor at x=(-?\d+), y=(-?\d+): closest beacon is at x=(-?\d+), y=(-?\d+)', line).groups()
)
s = (s_x, s_y)
b = (b_x, b_y)
sensors.append(s)
beacons.append(b)
beacons_set.add(b)
dist = m_d(s, b)
s_b_m_d.append(dist)
if min_x is None:
min_x = s_x
else:
min_x = min(min_x, s_x)
if max_x is None:
max_x = s_x
else:
max_x = max(max_x, s_x)
max_radius = max(max_radius, dist)
min_x -= max_radius
max_x += max_radius
print("sensors and beacons parsed")
def count_no_beacons(row):
no_beacons = 0
print(f"scanning min_x={min_x}, max_x={max_x}")
for x in range(min_x, max_x + 1):
if x % 100_000 == 0:
print(x)
no_beacon = (x, row) not in beacons_set and any(
m_d((x, row), s) <= s_b_m_d[i]
for i, s in enumerate(sensors)
)
if no_beacon:
no_beacons += 1
return no_beacons
# ans = count_no_beacons(10)
# ans = count_no_beacons(2000000)
# print(ans)
# --------------- part 2 ------------
# test data
# min_x = 0
# max_x = 20
# min_y = 0
# max_y = 20
# real data
min_x = 0
max_x = 4000000
min_y = 0
max_y = 4000000
# super inefficient method
# def find_beacon(row):
# print(f"scanning min_x={min_x}, max_x={max_x}")
# for x in range(min_x, max_x + 1):
# if x % 100_000 == 0:
# print(f'x={x}')
# found_beacon = (x, row) not in beacons_set and all(
# m_d((x, row), s) > s_b_m_d[i]
# for i, s in enumerate(sensors)
# )
# if found_beacon:
# return (x, row)
# for y in range(min_y, max_y + 1):
# print(f"scanning min_y={min_y}, min_y={max_y}")
# if y % 100_000 == 0:
# print(f'y={y}')
# beacon = find_beacon(y)
# if beacon:
# break
# print(beacon)
# x, y = beacon
# ans = x * 4000000 + y
# print(ans)
# I think I have to re-write my code
"""
idea:
for every sensor
check only the fringe (border) around it.
"""
candidates = set()
def check_has_beacon(c):
x, y = c
return (x, y) not in beacons_set and all(
m_d((x, y), s) > s_b_m_d[i]
for i, s in enumerate(sensors)
)
def in_range(c):
x, y = c
return min_x <= x <= max_x and min_y <= y <= max_y
checked = set()
beacon = None
for i, s in enumerate(sensors):
s_x, s_y = s
radius = s_b_m_d[i] + 1
# create a circle
candidates = itertools.chain(
zip(range(s_x - radius, s_x), range(s_y, s_y + radius)),
zip(range(s_x, s_x + radius), range(s_y + radius, s_y, -1)),
zip(range(s_x + radius, s_x, -1), range(s_y, s_y - radius, -1)),
zip(range(s_x, s_x - radius, -1), range(s_y - radius, s_y, 1))
)
print(f'i={i}, sensor={s}, radius={radius}')
for c in candidates:
# print(f'candidate={c}')
if c in checked:
continue
checked.add(c)
if in_range(c) and check_has_beacon(c):
beacon = c
print("found beacon:", beacon)
break
if beacon:
break
print(beacon)
x, y = beacon
ans = x * 4000000 + y
print(ans)