import functools | |||||
import itertools | |||||
import collections | import collections | ||||
import re | |||||
text = open(0).read() | text = open(0).read() | ||||
grid = {} | |||||
match = {} | |||||
for x, y, a, b in [[int(n) for n in re.findall(r'-?\d+', ln)] for ln in text.splitlines()]: | |||||
match[complex(x, y)] = complex(a, b) | |||||
ns = [int(n) for n in ''.join(n if n.isdigit() else ' ' for n in text).split()] | |||||
circles = {(x, y, abs(x - a) + abs(y - b)) for x, y , a, b in zip(*[iter(ns)] * 4)} | |||||
goal = 2000000 | |||||
bop = 4000000 | |||||
seen = set() | |||||
count = collections.Counter() | |||||
for aa, bb in match.items(): | |||||
diff = bb - aa | |||||
reach = int(abs(diff.real) + abs(diff.imag)) | |||||
dist = int(abs(aa.imag - goal)) | |||||
if reach > dist: | |||||
df = reach - dist | |||||
seen |= set(range(int(aa.real - df), int(aa.real + df))) | |||||
for rot in [1j ** i for i in range(4)]: | |||||
for dx in range(reach): | |||||
dy = reach - dx | |||||
edgy = aa + complex(dx, dy) * rot | |||||
for dot in [rot, rot * 1j]: | |||||
bip = edgy + dot | |||||
if 0 <= bip.real <= bop and 0 <= bip.imag <= bop: | |||||
count[bip] += 1 | |||||
y_lim = 2_000_000 | |||||
seen = set() | |||||
for x, y, r in circles: | |||||
dy = abs(y_lim - y) | |||||
dx = r - dy | |||||
seen |= set(range(x - dx, x + dx)) | |||||
print(len(seen)) | print(len(seen)) | ||||
fin = count.most_common()[0][0] | |||||
print(fin, int(fin.real * bop + fin.imag)) | |||||
criss = [] | |||||
cross = [] | |||||
for x, y, r in circles: | |||||
p = complex(x, y) | |||||
for i in range(4): | |||||
line = (p + r * 1j ** i), (1j ** (i + 1) + 1j ** (i + 2) ), r + 1 | |||||
if i % 2: | |||||
criss.append(line) | |||||
else: | |||||
cross.append(line) | |||||
def intersection(l1, l2): | |||||
(p1, d1, r1), (p2, d2, r2) = l1, l2 | |||||
for x in range(r1 + 1): | |||||
# (p1 + d1 * x) = (p2 + d2 * ?) | |||||
if (p1 + d1 * x) - p2 == d2: | |||||
if ((p1 + d1 * x - p2) / d2).real <= r2: | |||||
yield p1 + d1 * x | |||||
counter = collections.Counter() | |||||
for l1, l2 in itertools.product(criss, cross): | |||||
for p in intersection(l1, l2): | |||||
counter[p] += 1 | |||||
print(max(counter, key=counter.get)) |
import collections | |||||
import itertools | |||||
import re | |||||
text = open(0).read() | |||||
graph = collections.defaultdict(dict) | |||||
rates = {} | |||||
for ln in text.splitlines(): | |||||
source, rate, *leads_to = re.findall(r'[A-Z]{2}|\d+', ln) | |||||
for out in leads_to: | |||||
graph[out][source] = 1 | |||||
graph[source][out] = 1 | |||||
rates[source] = int(rate) | |||||
costs = {} | |||||
for k in graph: | |||||
edge = set(graph[k]) | |||||
seen = set() | |||||
cost = 1 | |||||
while True: | |||||
for e in edge: | |||||
if (k, e) in costs: | |||||
costs[k, e] = min(costs[k, e], cost) | |||||
else: | |||||
costs[k, e] = cost | |||||
cost += 1 | |||||
edge = {n for e in edge for n in graph[e]} - seen | |||||
if seen == set(graph): | |||||
break | |||||
seen |= edge | |||||
def for_one(combo, lim): | |||||
tt = 0 | |||||
bob = [0] | |||||
for a, b in zip(('AA',) + combo, combo): | |||||
tt = costs[a, b] | |||||
for _ in range(tt): | |||||
bob.append(bob[-1]) | |||||
bob.append(bob[-1] + rates[b]) | |||||
pending = max(0, lim - len(bob)) | |||||
bob += [bob[-1]] * pending | |||||
return sum(bob[:lim]) | |||||
def generate_combos(pending, left, carry=('AA',)): | |||||
if left < 0 or not pending: | |||||
yield carry[1:] | |||||
else: | |||||
for k in pending: | |||||
yield from generate_combos(pending - {k}, left - costs[carry[-1], k], carry + (k,)) | |||||
combos = generate_combos({k for k, v in rates.items() if v}, lim=30) | |||||
best = max(combos, key=for_one) | |||||
print(best) | |||||
print(for_one(best)) | |||||
# 2615 |
from itertools import cycle | |||||
SCHEME = ''' | |||||
#### | |||||
.#. | |||||
### | |||||
.#. | |||||
..# | |||||
..# | |||||
### | |||||
# | |||||
# | |||||
# | |||||
# | |||||
## | |||||
## | |||||
''' | |||||
def generate_blocks(scheme): | |||||
blocks = [] | |||||
for block in scheme.strip().split('\n\n'): | |||||
blocks.append(set()) | |||||
lns = block.splitlines() | |||||
for y, row in enumerate(lns[::-1]): | |||||
for x, v in enumerate(row, 2): | |||||
if v == '#': | |||||
blocks[-1].add(complex(x, y)) | |||||
return blocks | |||||
def play_tetris(width, gusts, blocks, lim=None): | |||||
top = 0 | |||||
solid = {j for j in range(width)} | |||||
contribs = [] | |||||
block_cycle = cycle(enumerate(blocks)) | |||||
gust_cycle = cycle(enumerate(gusts)) | |||||
states = {} | |||||
gust_idx = None | |||||
for block_idx, block in block_cycle: | |||||
# position block 4 up from top | |||||
block = {(p + 1j * top) + 4j for p in block} | |||||
# identify cycle | |||||
if lim is None: | |||||
state = tuple(complex(x, top - dy) in solid for x in range(7) for dy in range(1)) | |||||
key = block_idx, gust_idx, state | |||||
if key in states: | |||||
cycle_idx = states.get(key) | |||||
return contribs[:cycle_idx], contribs[cycle_idx:] | |||||
states[key] = len(states) | |||||
# play | |||||
for gust_idx, gust in gust_cycle: | |||||
# move sideways | |||||
dx = {'<': -1, '>': 1}[gust] | |||||
new = {p + dx for p in block} | |||||
if not any(p in solid or p.real in {-1, width} for p in new): | |||||
block = new | |||||
# move down | |||||
dy = -1j | |||||
new = {p + dy for p in block} | |||||
if any(p in solid for p in new): | |||||
solid |= block | |||||
break | |||||
else: | |||||
block = new | |||||
contribs.append(int(max(abs(p.imag) for p in solid)) - top) | |||||
top = sum(contribs) | |||||
if len(contribs) == lim: | |||||
return contribs | |||||
def main(): | |||||
width = 7 | |||||
gusts = open(0).read().strip() | |||||
blocks = generate_blocks(SCHEME) | |||||
contribs = play_tetris(width, gusts, blocks, lim=2022) | |||||
print(sum(contribs)) | |||||
head, tail = play_tetris(width, gusts, blocks) | |||||
N = 1_000_000_000_000 - len(head) | |||||
print(sum(head) + sum(tail) * N // len(tail) + sum(tail[:N % len(tail)])) | |||||
main() |