Roderic Day vor 5 Jahren
Ursprung
Commit
91fb671d30
5 geänderte Dateien mit 243 neuen und 19 gelöschten Zeilen
  1. +23
    -17
      toolkit.py
  2. +16
    -2
      y2019/p14.py
  3. +119
    -0
      y2019/p18.py
  4. +19
    -0
      y2019/p19.py
  5. +66
    -0
      y2019/p20.py

+ 23
- 17
toolkit.py Datei anzeigen

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]

+ 16
- 2
y2019/p14.py Datei anzeigen

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))

+ 119
- 0
y2019/p18.py Datei anzeigen

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)

+ 19
- 0
y2019/p19.py Datei anzeigen

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)

+ 66
- 0
y2019/p20.py Datei anzeigen

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)

Laden…
Abbrechen
Speichern