Spaces:
Running
Running
| def load_data(file): | |
| with open(file) as f: | |
| data = f.read() | |
| return [d for d in data.split("\n") if len(d) > 0] | |
| def sign(x): | |
| return 1 if abs(x) == x else -1 | |
| class Keypad: | |
| def __init__(self): | |
| keypad = self.build_keypad() | |
| self.M = len(keypad) | |
| self.N = len(keypad[0]) | |
| self.keypad = keypad | |
| def build_keypad(self): | |
| raise NotImplementedError() | |
| def get_pos(self, key: str): | |
| for i in range(self.M): | |
| for j in range(self.N): | |
| if self.keypad[i][j] == key: | |
| return (i, j) | |
| raise ValueError(f"Key {key} not found") | |
| def get_distance(self, key_a: str, key_b: str): | |
| pos_a = self.get_pos(key_a) | |
| pos_b = self.get_pos(key_b) | |
| dx = pos_b[0] - pos_a[0] | |
| dy = pos_b[1] - pos_a[1] | |
| return (dx, dy) | |
| def __repr__(self): | |
| return "\n".join([str(k) for k in self.keypad]) | |
| def pprint(self): | |
| print("\n".join([str(k) for k in self.keypad])) | |
| def find_sequence(self, key, next_key): | |
| """Finds the valid sequence between 2 key presses. Sequence returend is a string of button pushes.""" | |
| start_pos = self.get_pos(key) | |
| if key == next_key: | |
| return "" | |
| distance = self.get_distance(key, next_key) | |
| X = distance[0] | |
| Y = distance[1] | |
| # Strategy is to always try going >>>>, ^^^^^ until reaching, or other way around | |
| # Keep track of the things you pass by, if you pass by an illegal move, then no good | |
| seq_1 = [] # Actual button presses | |
| pass_by_1 = [] # Keeps track of keys visited in sequence | |
| i, j = start_pos | |
| for dx in range(abs(X)): | |
| i += 1*sign(X) | |
| pass_by_1.append(self.keypad[i][j]) | |
| seq_1.append("v" if sign(X) == 1 else "^") | |
| for dy in range(abs(Y)): | |
| j += 1*sign(Y) | |
| pass_by_1.append(self.keypad[i][j]) | |
| seq_1.append(">" if sign(Y) == 1 else "<") | |
| # if not None in pass_by: | |
| # return "".join(sequence) | |
| seq_2 = [] | |
| pass_by_2 = [] | |
| i, j = start_pos | |
| for dy in range(abs(Y)): | |
| j += 1*sign(Y) | |
| pass_by_2.append(self.keypad[i][j]) | |
| seq_2.append(">" if sign(Y) == 1 else "<") | |
| for dx in range(abs(X)): | |
| i += 1*sign(X) | |
| pass_by_2.append(self.keypad[i][j]) | |
| seq_2.append("v" if sign(X) == 1 else "^") | |
| if None in pass_by_1: | |
| return "".join(seq_2) | |
| elif None in pass_by_2: | |
| return "".join(seq_1) | |
| # We have a tie-break, so compute the cost of pressing the first key and tie-break based on that | |
| cost = { | |
| "^": 0, | |
| ">": 0, | |
| "v": 1, | |
| "<": 2, | |
| } | |
| cost_1 = cost[seq_1[0]] | |
| cost_2 = cost[seq_2[0]] | |
| # I swear i thought this should be <= but can't figure out why its >= | |
| # seq = seq_1 if cost_1 <= cost_2 else seq_2 | |
| seq = seq_1 if cost_1 >= cost_2 else seq_2 | |
| # if cost_1 != cost_2: | |
| # print(seq_1) | |
| # print(seq_2) | |
| # print(seq) | |
| # print() | |
| return "".join(seq) | |
| def find_full_sequence(self, sequence: str): | |
| # A robot always points at the A key first | |
| start_key = "A" | |
| full_sequence = [] | |
| key = start_key | |
| for next_key in sequence: | |
| full_sequence.append(self.find_sequence(key, next_key)) | |
| key = next_key | |
| return "A".join(full_sequence) + "A" # Account for each button press needed and final button press | |
| class NumericKeypad(Keypad): | |
| def build_keypad(self): | |
| return [ | |
| ["7", "8", "9"], | |
| ["4", "5", "6"], | |
| ["1", "2", "3"], | |
| [None, "0", "A"], | |
| ] | |
| class DirectionalKeypad(Keypad): | |
| def build_keypad(self): | |
| return [ | |
| [None, "^", "A"], | |
| ["<", "v", ">"], | |
| ] | |
| def complexity(code, sequence): | |
| l = len(sequence) | |
| n = int(code.split("A")[0]) | |
| # print(l, n) | |
| return n*l | |
| file = "input.txt" | |
| # file = "test.txt" | |
| data = load_data(file) | |
| dir_keypad = DirectionalKeypad() | |
| num_keypad = NumericKeypad() | |
| total = 0 | |
| for code in data: | |
| seq = num_keypad.find_full_sequence(code) | |
| # print(code) | |
| # print(seq) | |
| for _ in range(2): | |
| seq = dir_keypad.find_full_sequence(seq) | |
| # print(seq) | |
| c = complexity(code, seq) | |
| total += c | |
| # print(seq) | |
| print(total) | |
| ## Part 2 | |
| # Simulating the whole process won't work, grows exponentially, must be more clever than that | |