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