from enum import Enum from tools.aoc import AOCDay from tools.types import String from typing import Any class OP(int, Enum): SWAP = 0 ROTATE = 1 REVERSE = 2 MOVE = 3 def scramble(password: String, ops: list[tuple[OP, Any, ...]], reverse: bool = False) -> str: if reverse: ops = list(reversed(ops)) def get_rotate_num(c: str) -> int: return 1 + password.index(c) + (password.index(c) >= 4) for op in ops: if op[0] == OP.SWAP: x, y = op[1], op[2] if not isinstance(x, int) or not isinstance(y, int): x, y = password.index(x), password.index(y) password = password.swap(x, y) elif op[0] == OP.ROTATE: rot = op[1] if isinstance(rot, int): if reverse: rot *= -1 password = password.rotate(rot) else: if not reverse: rot = get_rotate_num(rot) password = password.rotate(rot) else: rot_count = 0 while rot_count != get_rotate_num(rot): rot_count += 1 password = password.rotate(-1) elif op[0] == OP.REVERSE: x, y = op[1], op[2] password = password.reverse(x, y) elif op[0] == OP.MOVE: x, y = op[1], op[2] if reverse: x, y = y, x to_move = password[x] password = String(password[:x] + password[x + 1 :]) password = String(password[:y] + to_move + password[y:]) return password class Day(AOCDay): inputs = [ [ ("decab", "input21_test"), ("gfdhebac", "input21"), ], [ ("dhaegfbc", "input21"), ], ] def parse_input(self) -> list[tuple[OP, Any, ...]]: instr = [] for line in self.getInput(): parts = line.split() if parts[0] == "swap": if parts[1] == "position": instr.append((OP.SWAP, int(parts[2]), int(parts[5]))) else: instr.append((OP.SWAP, parts[2], parts[5])) elif parts[0] == "rotate": if parts[1] == "based": instr.append((OP.ROTATE, parts[6])) else: instr.append((OP.ROTATE, int(parts[2]) * (-1 if parts[1] == "left" else 1))) elif parts[0] == "reverse": instr.append((OP.REVERSE, int(parts[2]), int(parts[4]))) else: instr.append((OP.MOVE, int(parts[2]), int(parts[5]))) return instr def part1(self) -> Any: to_scramble = String("abcde") if self.is_test() else String("abcdefgh") return scramble(to_scramble, self.parse_input()) def part2(self) -> Any: to_unscramble = String("fbgdceah") return scramble(to_unscramble, self.parse_input(), reverse=True) if __name__ == "__main__": day = Day(2016, 21) day.run(verbose=True)