include .env | include .env | ||||
FILE = $(shell find . -path "./y????/p??.py" -type f | xargs ls -rt | tail -n 1) | FILE = $(shell find . -path "./y????/p??.py" -type f | xargs ls -rt | tail -n 1) | ||||
DATA = $(shell echo $(FILE) | sed s/.py/.dat/) | |||||
PYTHONPATH = . | PYTHONPATH = . | ||||
export | export | ||||
main: venv/ $(DATA) | main: venv/ $(DATA) | ||||
venv/bin/python -u toolkit.py $(FILE) | |||||
@venv/bin/python -u toolkit.py $(FILE) | |||||
@cat $(DATA) | venv/bin/python -u $(FILE) | |||||
flake: venv/ | flake: venv/ | ||||
venv/bin/flake8 --exclude=venv/ | venv/bin/flake8 --exclude=venv/ |
if __name__ == '__main__': | if __name__ == '__main__': | ||||
data_file = Path(sys.argv[1]).with_suffix('.dat') | data_file = Path(sys.argv[1]).with_suffix('.dat') | ||||
ensure_data(data_file) | ensure_data(data_file) | ||||
builtins.df = data_file | |||||
builtins.text = data_file.read_text() | |||||
builtins.string = string | |||||
builtins.re = re | |||||
rel = re.sub(r'.+(y\d+)/(p\d+).+', r'\1.\2', os.environ['FILE']) | |||||
mod = importlib.import_module(rel) | |||||
print('ans1', getattr(mod, 'ans1', None)) | |||||
print('ans2', getattr(mod, 'ans2', None)) |
return final | return final | ||||
polymer, eqs = text.split('\n\n') | |||||
polymer, eqs = open(0).read().split('\n\n') | |||||
mapping = dict(eq.split(' -> ') for eq in eqs.splitlines()) | mapping = dict(eq.split(' -> ') for eq in eqs.splitlines()) | ||||
grow = {(a, b): ((a, x), (x, b)) for (a, b), x in mapping.items()} | grow = {(a, b): ((a, x), (x, b)) for (a, b), x in mapping.items()} | ||||
singles[polymer[-1]] += 1 | singles[polymer[-1]] += 1 | ||||
xmax, *_, xmin = [N // 2 for _, N in singles.most_common()] | xmax, *_, xmin = [N // 2 for _, N in singles.most_common()] | ||||
scores[n + 1] = xmax - xmin | scores[n + 1] = xmax - xmin | ||||
ans1 = scores[10] | |||||
ans2 = scores[40] | |||||
print(scores[10]) | |||||
print(scores[40]) |
import heapq | import heapq | ||||
def shortest_path(graph, X, Y): | |||||
risk = {0: 0} | |||||
seen = set() | |||||
heap = [(0, '', 0)] # string disambiguates cost ties for imaginary numbers | |||||
end = complex(X - 1, Y - 1) | |||||
tip = 0 | |||||
while end not in seen: | |||||
_, _, tip = heapq.heappop(heap) | |||||
seen.add(tip) | |||||
for y in (tip + dx for dx in [1, -1, 1j, -1j]): | |||||
if y in graph: | |||||
if y not in risk or risk[y] > graph[y] + risk[tip]: | |||||
risk[y] = graph[y] + risk[tip] | |||||
heapq.heappush(heap, (risk[y], str(y), y)) | |||||
return risk[end] | |||||
def shortest_path(graph, start, end): | |||||
risk_sum = {start: 0} | |||||
heap = [(0, *start)] | |||||
while end not in risk_sum: | |||||
risk, x, y = heapq.heappop(heap) | |||||
for dx, dy in [(1, 0), (-1, 0), (0, 1), (0, -1)]: | |||||
adj = (x + dx, y + dy) | |||||
if adj in graph: | |||||
if adj not in risk_sum or risk_sum[adj] > graph[adj] + risk: | |||||
risk_sum[adj] = graph[adj] + risk | |||||
heapq.heappush(heap, (risk_sum[adj], x + dx, y + dy)) | |||||
return risk_sum[end] | |||||
graph = {} | graph = {} | ||||
for y, line in enumerate(text.splitlines()): | |||||
for y, line in enumerate(open(0).read().splitlines()): | |||||
for x, char in enumerate(line): | for x, char in enumerate(line): | ||||
graph[complex(x, y)] = int(char) | |||||
graph[x, y] = int(char) | |||||
X, Y = x + 1, y + 1 | X, Y = x + 1, y + 1 | ||||
ans1 = shortest_path(graph, X, Y) | |||||
ans1 = shortest_path(graph, min(graph), max(graph)) | |||||
print(ans1) | |||||
graph = { | graph = { | ||||
pos + complex(X * dx, Y * dy): sum(divmod(graph[pos] + dx + dy, 10)) | |||||
for pos, value in graph.items() | |||||
(x + X * dx, y + Y * dy): sum(divmod(graph[x, y] + dx + dy, 10)) | |||||
for (x, y), value in graph.items() | |||||
for dx in range(5) | for dx in range(5) | ||||
for dy in range(5) | for dy in range(5) | ||||
} | } | ||||
ans2 = shortest_path(graph, 5 * X, 5 * Y) | |||||
ans2 = shortest_path(graph, min(graph), max(graph)) | |||||
print(ans2) |