| @@ -1,33 +1,45 @@ | |||
| import functools | |||
| import itertools | |||
| import collections | |||
| import re | |||
| 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)) | |||
| 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)) | |||
| @@ -0,0 +1,61 @@ | |||
| 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 | |||
| @@ -0,0 +1,97 @@ | |||
| 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() | |||