import os from tools.stopwatch import StopWatch from typing import Any, Callable, Dict, List, Tuple, Type, Union from .tools import get_script_dir BASE_PATH = get_script_dir() INPUTS_PATH = os.path.join(BASE_PATH, 'inputs') class AOCDay: year: int day: int input: List[str] # our input is always a list of str/lines inputs: List[List[Tuple[Any, str]]] part_func: List[Callable] def __init__(self, year: int, day: int): self.day = day self.year = year self.part_func = [self.part1, self.part2] def part1(self) -> Any: pass def part2(self) -> Any: pass def runPart(self, part: int, verbose: bool = False, measure_runtime: bool = False, timeit_number: int = 50): case_count = 0 for solution, input_file in self.inputs[part]: exec_time = None answer = None self._loadInput(input_file) if not measure_runtime or case_count < len(self.inputs[part]) - 1: answer = self.part_func[part]() else: stopwatch = StopWatch() for _ in range(timeit_number): answer = self.part_func[part]() stopwatch.stop() exec_time = str(stopwatch) if solution is None: printSolution(self.day, part + 1, answer, solution, case_count, exec_time) # FIXME: self._submit(part + 1, answer) else: if verbose or answer != solution: printSolution(self.day, part + 1, answer, solution, case_count, exec_time) if answer != solution: return False case_count += 1 if case_count == len(self.inputs[part]) and not verbose: printSolution(self.day, part + 1, answer, exec_time=exec_time) def run(self, parts: int = 3, verbose: bool = False, measure_runtime: bool = False, timeit_number: int = 50): if parts & 1: self.runPart(0, verbose, measure_runtime, timeit_number) if parts & 2: self.runPart(1, verbose, measure_runtime, timeit_number) def _loadInput(self, filename): with open(os.path.join(INPUTS_PATH, filename)) as f: self.input = f.read().splitlines() def _downloadInput(self, filename: str): pass def _submit(self, part: int, answer: Any): pass def test_part1(self, silent: bool = False) -> bool: live_input = self.input.copy() for case, solution in enumerate(self.inputs_p1): with open(os.path.join(INPUTS_PATH, "test_input%02d_1_%d" % (self.day, case))) as f: self.input = f.read().splitlines() check = self.part1() if not silent: printSolution(self.day, 1, check, solution, case) if check != solution: if silent: printSolution(self.day, 1, check, solution, case) return False self.input = live_input return True def test_part2(self, silent: bool = False) -> bool: live_input = self.input.copy() for case, solution in enumerate(self.inputs_p2): with open(os.path.join(INPUTS_PATH, "test_input%02d_2_%d" % (self.day, case))) as f: self.input = f.read().splitlines() check = self.part2() if not silent: printSolution(self.day, 2, check, solution, case) if check != solution: if silent: printSolution(self.day, 2, check, solution, case) return False self.input = live_input return True def getInput(self) -> Union[str, List]: if len(self.input) == 1: return self.input[0] else: return self.input.copy() def getInputListAsType(self, return_type: Type) -> List: """ get input as list casted to return_type, each line representing one list entry """ return [return_type(i) for i in self.input] def getMultiLineInputAsArray(self, return_type: Type = None, join_char: str = None) -> List: """ get input for day x as 2d array, split by empty lines """ lines = self.input.copy() lines.append('') return_array = [] line_array = [] for line in lines: if not line: if join_char: return_array.append(join_char.join(line_array)) else: return_array.append(line_array) line_array = [] continue if return_type: line_array.append(return_type(line)) else: line_array.append(line) return return_array def getInputAsArraySplit(self, split_char: str = ',', return_type: Union[Type, List[Type]] = None) -> List: """ get input for day x with the lines split by split_char if input has only one line, returns a 1d array with the values if input has multiple lines, returns a 2d array (a[line][values]) """ if len(self.input) == 1: return splitLine(line=self.input[0], split_char=split_char, return_type=return_type) else: return_array = [] for line in self.input: return_array.append(splitLine(line=line, split_char=split_char, return_type=return_type)) return return_array def printSolution(day: int, part: int, solution: Any, test: Any = None, test_case: int = 0, exec_time: str = None): if test is not None: print( "%s (TEST day%d/part%d/case%d): got '%s'; expected '%s'" % ("OK" if test == solution else "FAIL", day, part, test_case, solution, test) ) else: print( "Solution to day %s, part %s: %s" % ( day, part, solution, ) ) if exec_time: print("Day %s, Part %s - Average run time: %s" % (day, part, exec_time)) def splitLine(line, split_char: str = ',', return_type: Union[Type, List[Type]] = None): if split_char: line = line.split(split_char) if return_type is None: return line elif isinstance(return_type, list): return [return_type[x](i) if len(return_type) > x else i for x, i in enumerate(line)] else: return [return_type(i) for i in line]