import collections | import collections | ||||
def render(grid, brush): | |||||
def render(grid, brush=None): | |||||
if brush is None: | |||||
brush = {v: v for v in grid.values()} | |||||
if isinstance(brush, str): | if isinstance(brush, str): | ||||
brush = {i: c for i, c in enumerate(brush)} | brush = {i: c for i, c in enumerate(brush)} | ||||
xmin, *_, xmax = sorted(int(p.real) for p in grid) | xmin, *_, xmax = sorted(int(p.real) for p in grid) | ||||
return rendered | return rendered | ||||
def bsearch(fn, goal, lo, hi): | |||||
while hi - lo > 1: | |||||
mid = lo + (hi - lo) // 2 | |||||
if goal < fn(mid): | |||||
lo, hi = lo, mid | |||||
else: | |||||
lo, hi = mid, hi | |||||
# check | |||||
a, b = fn(lo), fn(hi) | |||||
assert a <= goal, 'lower bound too high' | |||||
assert goal <= b, 'higher bound too low' | |||||
return lo | |||||
def read_image(text): | def read_image(text): | ||||
grid = collections.defaultdict(str) | grid = collections.defaultdict(str) | ||||
for y, line in enumerate(text.splitlines()): | for y, line in enumerate(text.splitlines()): | ||||
for x, cell in enumerate(line): | for x, cell in enumerate(line): | ||||
grid[complex(x, y)] = cell | grid[complex(x, y)] = cell | ||||
return grid | return grid | ||||
def shortest_path(start, end, move): | |||||
seen = {} | |||||
edge = {start: None} | |||||
while end not in seen: | |||||
seen.update(edge) | |||||
edge = { | |||||
adj: pos | |||||
for pos in edge | |||||
for adj in move(pos) | |||||
if adj not in seen | |||||
} | |||||
if not edge: | |||||
raise RuntimeError('Path not found', seen) | |||||
path = [] | |||||
while end: | |||||
path.append(end) | |||||
end = seen[end] | |||||
return path[::-1] |
import re | import re | ||||
import sys | import sys | ||||
from toolkit import bsearch | |||||
text = sys.stdin.read() | text = sys.stdin.read() | ||||
cookbook = {} | cookbook = {} | ||||
return state | return state | ||||
def bsearch(fn, goal, lo, hi): | |||||
while hi - lo > 1: | |||||
mid = lo + (hi - lo) // 2 | |||||
if goal < fn(mid): | |||||
lo, hi = lo, mid | |||||
else: | |||||
lo, hi = mid, hi | |||||
# check | |||||
a, b = fn(lo), fn(hi) | |||||
assert a <= goal, 'lower bound too high' | |||||
assert goal <= b, 'higher bound too low' | |||||
return lo | |||||
print(fuel_to_ore({'FUEL': 1})['ORE']) | print(fuel_to_ore({'FUEL': 1})['ORE']) | ||||
print(bsearch(lambda n: fuel_to_ore({'FUEL': n})['ORE'], 1E12, 1, 10_000_000)) | print(bsearch(lambda n: fuel_to_ore({'FUEL': n})['ORE'], 1E12, 1, 10_000_000)) |
import collections | |||||
import itertools | |||||
import sys | |||||
from toolkit import read_image, render | |||||
def read_in(text): | |||||
grid = read_image(text) | |||||
elements = {v: k for k, v in grid.items()} | |||||
keys = frozenset(k for k in elements if 'a' <= k <= 'z') | |||||
return dict(grid), elements, keys | |||||
def move(pos): | |||||
for ori in [1, -1, 1j, -1j]: | |||||
if grid[pos + ori] != '#': | |||||
yield pos + ori | |||||
def shortest_path(start, end, move): | |||||
seen = {} | |||||
edge = {start: None} | |||||
while end not in seen: | |||||
edge = { | |||||
adj: pos | |||||
for pos in edge | |||||
for adj in move(pos) | |||||
if adj not in seen | |||||
} | |||||
seen.update(edge) | |||||
if not edge: | |||||
raise RuntimeError('No path found') | |||||
path = [] | |||||
while end != start: | |||||
path.append(end) | |||||
end = seen[end] | |||||
return path | |||||
def trim(states): | |||||
groups = collections.defaultdict(list) | |||||
for state in states: | |||||
tips = frozenset(seq[-1] for seq in state) | |||||
bulk = frozenset(' '.join(state)) | |||||
groups[bulk, tips].append(state) | |||||
return [min(groups[k], key=calc_total) for k in groups] | |||||
def precalc_moves(entrances): | |||||
requirements = collections.defaultdict(dict) | |||||
lengths = collections.defaultdict(dict) | |||||
for a, b in itertools.permutations(keys.union(entrances), 2): | |||||
try: | |||||
path = shortest_path(elements[a], elements[b], move) | |||||
except RuntimeError: | |||||
continue | |||||
lengths[a][b] = len(path) | |||||
requirements[a][b] = { | |||||
e.lower() | |||||
for e, v in elements.items() | |||||
if e.isupper() | |||||
and v in path | |||||
} | |||||
return requirements, lengths | |||||
def calc_total(state): | |||||
return sum(sum( | |||||
lengths[a][b] | |||||
for a, b in zip(s, s[1:]) | |||||
if {a, b} < set(lengths) | |||||
) for s in state) | |||||
def mod_grid(text): | |||||
grid, elements, keys = read_in(text) | |||||
pos = elements['@'] | |||||
grid[pos] = '#' | |||||
for char, ori in zip('*&^@', [1, -1, 1j, -1j]): | |||||
grid[pos + ori] = '#' | |||||
grid[pos + ori + ori * 1j] = char | |||||
return render(grid, {k: k for k in grid.values()}) | |||||
def solve(text): | |||||
global grid, elements, keys, lengths | |||||
grid, elements, keys = read_in(text) | |||||
stacks = {k for k in elements if not k.isalnum()} - {'#', '.'} | |||||
requirements, lengths = precalc_moves(stacks) | |||||
states = [stacks] | |||||
for _ in range(len(keys)): | |||||
states = trim( | |||||
state - {seq} | {seq + b} | |||||
for state in states | |||||
for seq in state | |||||
for b, reqs in requirements[seq[-1]].items() | |||||
if b not in seq and reqs.issubset(''.join(state)) | |||||
) | |||||
final = min(states, key=calc_total) | |||||
print(' '.join(sorted(final))) | |||||
print(calc_total(final)) | |||||
text = sys.stdin.read() | |||||
text = ''' | |||||
############# | |||||
#g#f.D#..h#l# | |||||
#F###e#E###.# | |||||
#dCba...BcIJ# | |||||
#####.@.##### | |||||
#nK.L...G...# | |||||
#M###N#H###.# | |||||
#o#m..#i#jk.# | |||||
############# | |||||
''' | |||||
solve(text) | |||||
text = mod_grid(text) | |||||
solve(text) |
import sys | |||||
from intcode import compute | |||||
def check(x, y): | |||||
return next(compute(text, iter([x, y]))) | |||||
text = sys.stdin.read() | |||||
tiles = {(x, y) for x in range(50) for y in range(50) if check(x, y)} | |||||
print(len(tiles)) | |||||
x, y = 0, 0 | |||||
while not check(x + 99, y): | |||||
y += 1 | |||||
while not check(x, y + 99): | |||||
x += 1 | |||||
print(x * 10_000 + y) |
import itertools | |||||
import sys | |||||
from toolkit import read_image, shortest_path | |||||
def move(pos): | |||||
for ori in [1, -1, 1j, -1j]: | |||||
adj = pos + ori | |||||
if adj in grid: | |||||
yield adj | |||||
if adj in duals: | |||||
yield from move(duals[adj]) | |||||
def move_with_depth(state): | |||||
pos, level = state | |||||
for ori in [1, -1, 1j, -1j]: | |||||
adj = pos + ori | |||||
if adj in grid: | |||||
yield adj, level | |||||
if adj in duals: | |||||
name = grid[adj] | |||||
if adj in inner: | |||||
yield from move_with_depth((duals[adj], level + 1)) | |||||
elif (level > 0) and (name not in {'AA', 'ZZ'}): | |||||
yield from move_with_depth((duals[adj], level - 1)) | |||||
# base | |||||
text = sys.stdin.read() | |||||
grid = {pos: val for pos, val in read_image(text).items() if val not in ' #'} | |||||
for pos, val in grid.copy().items(): | |||||
if val.isupper(): | |||||
for im in [1, 1j]: | |||||
A, B, C = [pos - im, pos, pos + im] | |||||
seq = ''.join(grid.get(p, ' ') for p in [A, B, C]) | |||||
if seq.endswith('.'): | |||||
grid[B] = grid.pop(A) + grid.pop(B) | |||||
if seq.startswith('.'): | |||||
grid[B] = grid.pop(B) + grid.pop(C) | |||||
elements = {v: k for k, v in grid.items()} | |||||
# matrix | |||||
duals = {} | |||||
portals = {pos for pos, cell in grid.items() if cell.isupper()} | |||||
for pA, pB in itertools.combinations(portals, 2): | |||||
if grid[pA] == grid[pB]: | |||||
duals[pA] = pB | |||||
duals[pB] = pA | |||||
# part1 | |||||
start, end = elements['AA'], elements['ZZ'] | |||||
path = shortest_path(start, end, move) | |||||
print(len(path) - 3) | |||||
# categorize portals | |||||
_, *xmid, _ = sorted({p.real for p in portals}) | |||||
_, *ymid, _ = sorted({p.imag for p in portals}) | |||||
inner = {pos for pos in portals if pos.real in xmid and pos.imag in ymid} | |||||
outer = set(portals) - inner | |||||
# part2 | |||||
start, end = (elements['AA'], 0), (elements['ZZ'], 0) | |||||
path = shortest_path(start, end, move_with_depth) | |||||
print(len(path) - 3) |