From 7013c5b0a0136c6d8786b498d9df1cb736b0caa6 Mon Sep 17 00:00:00 2001 From: felixm Date: Mon, 30 Dec 2024 23:08:00 -0500 Subject: [PATCH] Solve 2024 day 21 to finish the year --- 2024/d21.py | 190 ++++++++++++++++++---------------------------------- README.md | 4 +- 2 files changed, 69 insertions(+), 125 deletions(-) diff --git a/2024/d21.py b/2024/d21.py index af13d5e..b5442fc 100644 --- a/2024/d21.py +++ b/2024/d21.py @@ -1,143 +1,87 @@ -import heapq from lib import get_data +from collections import deque +from functools import cache data = get_data(__file__) +# data = """029A +# 980A +# 179A +# 456A +# 379A""" def new_dir(current, move): - return {"<": - {">": "v"}, - "^": - {">": "A", "v": "v"}, - "v": - {"<": "<", ">": ">", "^": "^"}, - ">": - {"^": "A", "<": "v"}, - "A": - {"<": "^", "v": ">"} + return { + "<": {">": "v"}, + "^": {">": "A", "v": "v"}, + "v": {"<": "<", ">": ">", "^": "^"}, + ">": {"^": "A", "<": "v"}, + "A": {"<": "^", "v": ">"}, }[current].get(move, None) + def new_num(current, move): - return {"7": {">": "8", "v": "4",}, - "8": {"<": "7", ">": "9", "v": "5"}, - "9": {"<": "8", "v": "6"}, - "4": {"^": "7", ">": "5", "v": "1"}, - "5": {"^": "8", ">": "6", "<": "4", "v": "2"}, - "6": {"^": "9", "<": "5", "v": "3"}, - "1": {"^": "4", ">": "2"}, - "2": {"^": "5", ">": "3", "<": "1", "v": "0"}, - "3": {"^": "6", "<": "2", "v": "A"}, - "0": {"^": "2", ">": "A"}, - "A": {"^": "3", "<": "0"}, + return { + "7": {">": "8", "v": "4", }, + "8": {"<": "7", ">": "9", "v": "5"}, + "9": {"<": "8", "v": "6"}, + "4": {"^": "7", ">": "5", "v": "1"}, + "5": {"^": "8", ">": "6", "<": "4", "v": "2"}, + "6": {"^": "9", "<": "5", "v": "3"}, + "1": {"^": "4", ">": "2"}, + "2": {"^": "5", ">": "3", "<": "1", "v": "0"}, + "3": {"^": "6", "<": "2", "v": "A"}, + "0": {"^": "2", ">": "A"}, + "A": {"^": "3", "<": "0"}, }[current].get(move, None) -def new_state(press, node, has_numpad): - key_pads, num_pad, typed, pressed = node - key_pads = list(key_pads) - pressed = pressed + press - - for i in range(len(key_pads)): - if press == "A": - press = key_pads[i] - else: - key_pads[i] = new_dir(key_pads[i], press) - if key_pads[i] is None: - return None - press = None - break - - if press is not None: - if has_numpad: - if press == "A": - typed = typed + num_pad - else: - num_pad = new_num(num_pad, press) - if num_pad is None: - return None - else: - typed = typed + press - nnode = tuple(key_pads), num_pad, typed, pressed - return nnode - - # if press == "A": - # if key_pads[0] == "A": - # if key_pads[1] == "A": - # entered += num_pad - # else: - # num_pad = new_num(num_pad, key_pads[1]) - # else: - # key_pads[1] = new_dir(key_pads[1], key_pads[0]) - # else: - # key_pads[0] = new_dir(key_pads[0], press) - # - # if key_pads[0] is None or key_pads[1] is None or num_pad is None: - # return None - # return tuple(key_pads), num_pad, typed - -def find(code, key_pads=2, has_numpad=False): - _, _, TYPED, ENTERED = 0, 1, 2, 3 - - def dist(n1, n2): - if n1 == 0: return 0 - return 1 - - def h(node): - return len(code) - len(node[TYPED]) - - def is_goal(node): - return node[TYPED] == code - - def neighbors(node): - nbs = [] - for move in '<^v>A': - nb = new_state(move, node, has_numpad) - if nb is None: +@cache +def find(code, depth, lookup): + if depth == 0: + return len(code) + code = list(code) + t = 0 + current = "A" + while code: + solutions = [] + target = code.pop(0) + start = (current, "") + seen = set() + queue = deque([start]) + while queue: + node = queue.popleft() + if node in seen: continue - if not code.startswith(nb[TYPED]): + seen.add(node) + if node[0] == target: + solution = node[1] + "A" + if len(solutions) == 0: + solutions.append(solution) + elif len(solutions[-1]) == len(solution): + solutions.append(solution) + else: + break continue - nbs.append(nb) - return nbs - def hash(node): - return node[:-1] + for c in "<>v^": + next = lookup(node[0], c) + if next is not None: + queue.append((next, node[1] + c)) + current = target + t += min(find(code, depth - 1, new_dir) for code in solutions) + return t - key_pads = tuple(['A' for _ in range(key_pads)]) - start = (key_pads, 'A', '', '') - starts = [start] - open_set = [] - g_score = {} - cost = None - for start in starts: - heapq.heappush(open_set, (h(start), start)) - g_score[hash(start)] = dist(0, start) - - while open_set: - current_f_score, current = heapq.heappop(open_set) - if is_goal(current): - assert current_f_score == g_score[hash(current)] - # print(current) - cost = g_score[hash(current)] - cost = current[ENTERED] - break - - # print(current) - for neighbor in neighbors(current): - tentative_g_score = g_score[hash(current)] + dist(current, neighbor) - if tentative_g_score < g_score.get(hash(neighbor), 10**15): - # print(" ", neighbor, end="") - # input() - g_score[hash(neighbor)] = tentative_g_score - f_score = g_score[hash(neighbor)] + h(neighbor) - heapq.heappush(open_set, (f_score, neighbor)) - return cost - -t = 0 +p1, p2 = 0, 0 for line in data.splitlines(): - s = find(line, 2, True) i = int("".join([c for c in line if c.isdigit()])) - s = len(s) - t += s * i -print(t) + s1 = find(line, 3, new_num) + s2 = find(line, 26, new_num) + p1 += s1 * i + p2 += s2 * i + + +print(p1) +print(p2) diff --git a/README.md b/README.md index f63187d..9970f48 100644 --- a/README.md +++ b/README.md @@ -311,9 +311,9 @@ and focus. Of course, learning more algorithms and techniques helps. - Day 18: `14:35:01 20398 0 14:37:18 19550 0` - Day 19: `00:14:37 2001 0 00:19:43 1584 0` - Day 20: `01:08:53 3637 0 01:53:01 2837 0` -- Day 21: `00:48:40 215 0 - - -` +- Day 21: `00:48:40 215 0 >24h 16427 0` - Day 22: `00:13:04 1930 0 00:28:29 739 0` - Day 23: ` >24h 20096 0 >24h 17620 0` - Day 24: `15:57:01 17307 0 >24h 11326 0` -- Day 25: `10:41:54 14140 0 - - -` +- Day 25: `10:41:54 14140 0 >24h 13631 0`