advent-of-code/2022/day05_supply_stacks.py

126 lines
3.4 KiB
Python
Raw Normal View History

2022-12-05 18:34:37 +01:00
from __future__ import annotations
2022-12-05 19:25:02 +01:00
import copy
2022-12-05 18:34:37 +01:00
import dataclasses
import re
def main(filename: str, expected_part_1: str = None, expected_part_2: str = None):
print(f"\n+ Running on {filename}")
with open(filename) as f:
data = f.read().rstrip().split("\n\n")
2022-12-05 19:25:02 +01:00
data_1, data_2 = parse_data(data)
solution_part_1 = solve_part_1(data_1)
2022-12-05 18:34:37 +01:00
print(f"1. Found {solution_part_1}")
if expected_part_1:
assert expected_part_1 == solution_part_1
2022-12-05 19:25:02 +01:00
solution_part_2 = solve_part_2(data_2)
2022-12-05 18:34:37 +01:00
print(f"2. Found {solution_part_2}")
if expected_part_2:
assert expected_part_2 == solution_part_2
@dataclasses.dataclass
class Instruction:
quantity: int
2022-12-05 19:14:28 +01:00
_source: int
_destination: int
2022-12-05 18:34:37 +01:00
@classmethod
def from_text(cls, text) -> Instruction:
2022-12-05 19:14:28 +01:00
match = re.match(r"move (\d+) from (\d+) to (\d+)", text)
2022-12-05 18:34:37 +01:00
quantity, source, destination = match.groups()
2022-12-05 19:14:28 +01:00
return cls(int(quantity), int(source), int(destination))
@property
def source(self) -> int:
return self._source - 1
@property
def destination(self) -> int:
return self._destination - 1
2022-12-05 18:34:37 +01:00
@dataclasses.dataclass
class Game:
stacks: list[list[str]]
instructions: list[Instruction]
2022-12-05 19:25:02 +01:00
def __str__(self):
return "\n".join(map(str, self.stacks))
2022-12-05 19:14:28 +01:00
def play_instructions(self) -> None:
for instruction in self.instructions:
self.execute(instruction)
def execute(self, instruction: Instruction) -> None:
2022-12-05 19:25:02 +01:00
raise NotImplementedError()
2022-12-05 19:14:28 +01:00
def message(self) -> str:
msg = ""
for stack in self.stacks:
if stack:
msg += stack[-1]
return msg
2022-12-05 19:25:02 +01:00
class Game1(Game):
def execute(self, instruction: Instruction) -> None:
for _ in range(instruction.quantity):
item = self.stacks[instruction.source].pop()
self.stacks[instruction.destination].append(item)
class Game2(Game):
def execute(self, instruction: Instruction) -> None:
temp_stack = []
for _ in range(instruction.quantity):
item = self.stacks[instruction.source].pop()
temp_stack.append(item)
for _ in range(instruction.quantity):
item = temp_stack.pop()
self.stacks[instruction.destination].append(item)
2022-12-05 19:14:28 +01:00
2022-12-05 18:34:37 +01:00
DataType = Game
2022-12-05 19:25:02 +01:00
def parse_data(data: list[str]) -> tuple[DataType, DataType]:
2022-12-05 18:34:37 +01:00
stacks, instructions = data
parsed_instructions = []
for instruction in instructions.split("\n"):
parsed_instructions.append(Instruction.from_text(instruction))
2022-12-05 19:14:28 +01:00
stacks = list(stacks.split("\n")[::-1])
header = stacks[0]
parsed_stacks = []
for index, char in enumerate(header):
if char != " ":
stack = []
for row in stacks[1:]:
char = row[index]
if char == " ":
break
stack.append(char)
parsed_stacks.append(stack)
2022-12-05 19:25:02 +01:00
return Game1(
copy.deepcopy(parsed_stacks), copy.deepcopy(parsed_instructions)
), Game2(copy.deepcopy(parsed_stacks), copy.deepcopy(parsed_instructions))
2022-12-05 18:34:37 +01:00
def solve_part_1(data: DataType) -> str:
2022-12-05 19:14:28 +01:00
data.play_instructions()
return data.message()
2022-12-05 18:34:37 +01:00
def solve_part_2(data: DataType) -> str:
2022-12-05 19:25:02 +01:00
data.play_instructions()
return data.message()
2022-12-05 18:34:37 +01:00
if __name__ == "__main__":
2022-12-05 19:25:02 +01:00
main("inputs/day05-test1", expected_part_1="CMZ", expected_part_2="MCD")
main("inputs/day05", expected_part_1="BWNCQRMDB", expected_part_2="NHWZCBNBF")