193 lines
6.4 KiB
Python
193 lines
6.4 KiB
Python
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]
|