diff --git a/Makefile b/Makefile index 4f7fed4..012b309 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ .PHONY: all clean black flake test solutions # Default target -all: black flake test solutions clean +all: ruff test solutions clean # Clean temporary and generated files clean: @@ -9,25 +9,15 @@ clean: find . \( -type d -name '.eggs' -or -type d -name '*.egg-info' -or -type d -name '.pytest_cache' \) | xargs rm -rf # Format code using black -black: - black ./src ./tests - -# Run flake8 for linting -flake: - flake8 ./src ./tests +ruff: + ruff check . # Run tests test: python3 -m pytest -# Run specific solutions for Advent of Code 2023 solutions: - python3 src/advent_of_code/year_2023/days/1.py --input_file inputs/2023/1.txt --part 1 - python3 src/advent_of_code/year_2023/days/1.py --input_file inputs/2023/1.txt --part 2 - python3 src/advent_of_code/year_2023/days/2.py --input_file inputs/2023/2.txt - python3 src/advent_of_code/year_2023/days/3.py --input_file inputs/2023/3.txt - python3 src/advent_of_code/year_2023/days/4.py --input_file inputs/2023/4.txt - python3 src/advent_of_code/year_2023/days/6.py --input_file inputs/2023/6.txt + python3 scripts/run_all_solutions.py .PHONY: new-day-skeleton-files-from-template new-day-skeleton-files-from-template: diff --git a/inputs/2023/1.txt b/inputs/year_2023/01.dat similarity index 100% rename from inputs/2023/1.txt rename to inputs/year_2023/01.dat diff --git a/inputs/2023/2.txt b/inputs/year_2023/02.dat similarity index 100% rename from inputs/2023/2.txt rename to inputs/year_2023/02.dat diff --git a/inputs/2023/3.txt b/inputs/year_2023/03.dat similarity index 100% rename from inputs/2023/3.txt rename to inputs/year_2023/03.dat diff --git a/inputs/2023/4.txt b/inputs/year_2023/04.dat similarity index 100% rename from inputs/2023/4.txt rename to inputs/year_2023/04.dat diff --git a/inputs/2023/6.txt b/inputs/year_2023/06.dat similarity index 100% rename from inputs/2023/6.txt rename to inputs/year_2023/06.dat diff --git a/requirements.txt b/requirements.txt index 40ed6fb..7f0065e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,4 @@ -black==23.12.1 -flake8==6.1.0 +ruff==0.1.9 numpy==1.26.2 pytest==7.4.3 setuptools==68.2.2 diff --git a/src/advent_of_code/scripts/generate_new_day_skeleton_files_from_templates.py b/scripts/generate_new_day_skeleton_files_from_templates.py similarity index 100% rename from src/advent_of_code/scripts/generate_new_day_skeleton_files_from_templates.py rename to scripts/generate_new_day_skeleton_files_from_templates.py diff --git a/scripts/run_all_solutions.py b/scripts/run_all_solutions.py new file mode 100644 index 0000000..47d4ea2 --- /dev/null +++ b/scripts/run_all_solutions.py @@ -0,0 +1,48 @@ +import os +import re +import subprocess + +BASE_DIR = "src/advent_of_code" +YEARS_DIR = os.path.join(BASE_DIR, "year_") +RUN_DAY_SCRIPT = "scripts/run_day.py" +INPUTS_DIR = "inputs" + +def discover_and_run_solutions(): + # Regex to match "day_.py" + day_pattern = re.compile(r"day_(\d{2})\.py") + + for year in sorted(os.listdir(BASE_DIR)): + if year.startswith("year_"): + year_path = os.path.join(BASE_DIR, year) + year_number = year.split("_")[1] + + # Look for days in the "days" directory + days_dir = os.path.join(year_path) + for file in sorted(os.listdir(days_dir)): + match = day_pattern.match(file) + if match: + day_number = match.group(1) + + # Build input file path + input_file = os.path.join(INPUTS_DIR, f"year_{year_number}", f"{day_number}.dat") + if not os.path.exists(input_file): + print(f"Input file for {year_number} Day {day_number} not found, skipping.") + continue + + # Run the solution using run_day.py + try: + print(f"Running {year_number} Day {day_number}...") + subprocess.run( + [ + "python3", RUN_DAY_SCRIPT, + "--year", year_number, + "--day", str(int(day_number)), # Remove leading zero for argument + ], + check=True + ) + print("\n") + except subprocess.CalledProcessError as e: + print(f"Error running {year_number} Day {day_number}: {e}") + +if __name__ == "__main__": + discover_and_run_solutions() diff --git a/scripts/run_day.py b/scripts/run_day.py new file mode 100644 index 0000000..dea9cee --- /dev/null +++ b/scripts/run_day.py @@ -0,0 +1,44 @@ +import importlib +import sys +import argparse + +def main(): + parser = argparse.ArgumentParser(description="Run a specific Advent of Code solution.") + # parser.add_argument( + # "--input_file", + # required=True, + # type=str, + # help="Path to the input file for the day's solution." + # ) + parser.add_argument( + "--year", + required=True, + type=int, + ) + parser.add_argument( + "--day", + required=True, + type=str, + ) + + args = parser.parse_args() + day_zero_padded_str = str(args.day).zfill(2) + input_file = f"inputs/year_{args.year}/{day_zero_padded_str}.dat" + + day_module = f"advent_of_code.year_{args.year}.day_{day_zero_padded_str}" + + try: + # Dynamically import the module for the specified day + module = importlib.import_module(day_module) + # Run the solution (assumes each solver module has a `main()` function) + if hasattr(module, "main"): + module.main(input_file) + else: + print("man") + print(f"The module {day_module} does not have a 'main(input_file)' function.") + except ModuleNotFoundError: + print(f"Could not find module: {day_module}") + sys.exit(1) + +if __name__ == "__main__": + main() diff --git a/src/advent_of_code/scripts/templates/days_template.txt b/scripts/templates/days_template.txt similarity index 100% rename from src/advent_of_code/scripts/templates/days_template.txt rename to scripts/templates/days_template.txt diff --git a/src/advent_of_code/scripts/templates/solvers_template.txt b/scripts/templates/solvers_template.txt similarity index 100% rename from src/advent_of_code/scripts/templates/solvers_template.txt rename to scripts/templates/solvers_template.txt diff --git a/src/advent_of_code/scripts/templates/tests_template.txt b/scripts/templates/tests_template.txt similarity index 100% rename from src/advent_of_code/scripts/templates/tests_template.txt rename to scripts/templates/tests_template.txt diff --git a/src/advent_of_code/year_2023/solvers/day_1_solvers.py b/src/advent_of_code/year_2023/day_01.py similarity index 78% rename from src/advent_of_code/year_2023/solvers/day_1_solvers.py rename to src/advent_of_code/year_2023/day_01.py index 9fea4cd..53ccbf2 100644 --- a/src/advent_of_code/year_2023/solvers/day_1_solvers.py +++ b/src/advent_of_code/year_2023/day_01.py @@ -1,3 +1,7 @@ +from advent_of_code.utils.input_handling import ( + read_input, +) + import re SPELLED_NUMBERS_MAPPING = { @@ -90,3 +94,27 @@ def find_indices_of_patterns(line, patterns_to_find): patterns_and_indices[pattern] = [m.start() for m in matches] return patterns_and_indices + +def run_part(input_data, accept_written_digits): + + patterns_to_find = get_patterns(accept_written_digits) + calibration_value_sum = solve_all_calibration_values(input_data, patterns_to_find) + return calibration_value_sum + +def main(input_file): + # args = parse_args() + # input = read_input(args.input_file) + input_data = read_input(input_file) + + calibration_value_sum = run_part(input_data, accept_written_digits = False) + + print(f"Part 1: Solution is {calibration_value_sum}.") + + calibration_value_sum = run_part(input_data, accept_written_digits = True) + + print(f"Part 2: Solution is {calibration_value_sum}.") + + + +if __name__ == "__main__": + main() diff --git a/src/advent_of_code/year_2023/solvers/day_2_solvers.py b/src/advent_of_code/year_2023/day_02.py similarity index 85% rename from src/advent_of_code/year_2023/solvers/day_2_solvers.py rename to src/advent_of_code/year_2023/day_02.py index c9fb734..2dc2f6b 100644 --- a/src/advent_of_code/year_2023/solvers/day_2_solvers.py +++ b/src/advent_of_code/year_2023/day_02.py @@ -1,3 +1,5 @@ +from advent_of_code.utils.input_handling import read_input + import re BAG_CONSTRAINTS = {"red": 12, "green": 13, "blue": 14} @@ -52,3 +54,14 @@ def parse_game_string(full_game_str): def calculate_game_power(max_colour_1, max_colour_2, max_colour_3): return max_colour_1 * max_colour_2 * max_colour_3 + + +def main(input_file): + input = read_input(input_file) + part_1_solution, part_2_solution = solve_day_2(input) + print(f"Day 2: Part 1 solution is {part_1_solution}.") + print(f"Part 2 solution is {part_2_solution}.") + + +if __name__ == "__main__": + main() diff --git a/src/advent_of_code/year_2023/solvers/day_3_solvers.py b/src/advent_of_code/year_2023/day_03.py similarity index 94% rename from src/advent_of_code/year_2023/solvers/day_3_solvers.py rename to src/advent_of_code/year_2023/day_03.py index def9617..391d74b 100644 --- a/src/advent_of_code/year_2023/solvers/day_3_solvers.py +++ b/src/advent_of_code/year_2023/day_03.py @@ -1,3 +1,5 @@ +from advent_of_code.utils.input_handling import read_input + import re @@ -189,3 +191,17 @@ def solve_day_3(input) -> int: gear_collection = identify_gears(parts_collection, symbol_collection) sum_of_gear_ratios = calculate_sum_of_gear_ratios(gear_collection) return (sum_of_part_numbers, sum_of_gear_ratios) + + + +def main(input_file): + input = read_input(input_file) + result_part_1, result_part_2 = solve_day_3(input) + print( + f"Day 3: The sum of part numbers is {result_part_1}. " + f"The sum of gear ratios is {result_part_2}" + ) + + +if __name__ == "__main__": + main() diff --git a/src/advent_of_code/year_2023/solvers/day_4_solvers.py b/src/advent_of_code/year_2023/day_04.py similarity index 86% rename from src/advent_of_code/year_2023/solvers/day_4_solvers.py rename to src/advent_of_code/year_2023/day_04.py index a401d90..1287e7a 100644 --- a/src/advent_of_code/year_2023/solvers/day_4_solvers.py +++ b/src/advent_of_code/year_2023/day_04.py @@ -1,3 +1,5 @@ +from advent_of_code.utils.input_handling import read_input + import re @@ -75,3 +77,17 @@ def solve_day_4(input) -> int: cards_with_copies = compute_copies(cards) n_scratchcards = sum([card.n_copies for card in cards_with_copies]) return (total_score, n_scratchcards) + + +def main(input_file): + input = read_input(input_file) + result_part_1, result_part_2 = solve_day_4(input) + print( + f"Day 4: " + f" Total points for part 1 is {result_part_1}. " + f" Total points for part 2 is {result_part_2}. " + ) + + +if __name__ == "__main__": + main() diff --git a/src/advent_of_code/year_2023/solvers/day_6_solvers.py b/src/advent_of_code/year_2023/day_06.py similarity index 82% rename from src/advent_of_code/year_2023/solvers/day_6_solvers.py rename to src/advent_of_code/year_2023/day_06.py index 5dea5f9..a54a364 100644 --- a/src/advent_of_code/year_2023/solvers/day_6_solvers.py +++ b/src/advent_of_code/year_2023/day_06.py @@ -1,3 +1,5 @@ +from advent_of_code.utils.input_handling import read_input + import math import re @@ -59,3 +61,17 @@ def solve_day_6(input): races_part_1 = create_races(input, part=1) races_part_2 = create_races(input, part=2) return (races_part_1.solve(), races_part_2.solve()) + + +def main(input_file): + input = read_input(input_file) + result_part_1, result_part_2 = solve_day_6(input) + print( + f"Day 6: " + f" Result for part 1 is {result_part_1}. " + f" Result for part 2 is {result_part_2}. " + ) + + +if __name__ == "__main__": + main() diff --git a/src/advent_of_code/year_2023/days/1.py b/src/advent_of_code/year_2023/days/1.py deleted file mode 100644 index f0a609e..0000000 --- a/src/advent_of_code/year_2023/days/1.py +++ /dev/null @@ -1,31 +0,0 @@ -from advent_of_code.year_2023.solvers.day_1_solvers import ( - get_patterns, - solve_all_calibration_values, -) - - -from advent_of_code.utils.input_handling import ( - read_input, - parse_args, -) - - -def main(): - args = parse_args() - input = read_input(args.input_file) - - if args.part == 1: - accept_written_digits = False - elif args.part == 2: - accept_written_digits = True - - patterns_to_find = get_patterns(accept_written_digits) - calibration_value_sum = solve_all_calibration_values(input, patterns_to_find) - print( - f"Day 1: Solution with accept_written_digits={accept_written_digits}" - f" is {calibration_value_sum}." - ) - - -if __name__ == "__main__": - main() diff --git a/src/advent_of_code/year_2023/days/2.py b/src/advent_of_code/year_2023/days/2.py deleted file mode 100644 index d57baf9..0000000 --- a/src/advent_of_code/year_2023/days/2.py +++ /dev/null @@ -1,16 +0,0 @@ -from advent_of_code.utils.input_handling import read_input, parse_args -from advent_of_code.year_2023.solvers.day_2_solvers import solve_day_2 - - -def main(): - args = parse_args() - input = read_input(args.input_file) - part_1_solution, part_2_solution = solve_day_2(input) - print( - f"Day 2: Part 1 solution is {part_1_solution}." - f"Part 2 solution is {part_2_solution}." - ) - - -if __name__ == "__main__": - main() diff --git a/src/advent_of_code/year_2023/days/3.py b/src/advent_of_code/year_2023/days/3.py deleted file mode 100644 index a65131e..0000000 --- a/src/advent_of_code/year_2023/days/3.py +++ /dev/null @@ -1,18 +0,0 @@ -from advent_of_code.year_2023.solvers.day_3_solvers import ( - solve_day_3, -) -from advent_of_code.utils.input_handling import read_input, parse_args - - -def main(): - args = parse_args() - input = read_input(args.input_file) - result_part_1, result_part_2 = solve_day_3(input) - print( - f"Day 3: The sum of part numbers is {result_part_1}. " - f"The sum of gear ratios is {result_part_2}" - ) - - -if __name__ == "__main__": - main() diff --git a/src/advent_of_code/year_2023/days/4.py b/src/advent_of_code/year_2023/days/4.py deleted file mode 100644 index d700f20..0000000 --- a/src/advent_of_code/year_2023/days/4.py +++ /dev/null @@ -1,19 +0,0 @@ -from advent_of_code.year_2023.solvers.day_4_solvers import ( - solve_day_4, -) -from advent_of_code.utils.input_handling import read_input, parse_args - - -def main(): - args = parse_args() - input = read_input(args.input_file) - result_part_1, result_part_2 = solve_day_4(input) - print( - f"Day 4: " - f" Total points for part 1 is {result_part_1}. " - f" Total points for part 2 is {result_part_2}. " - ) - - -if __name__ == "__main__": - main() diff --git a/src/advent_of_code/year_2023/days/6.py b/src/advent_of_code/year_2023/days/6.py deleted file mode 100644 index 9f2d95a..0000000 --- a/src/advent_of_code/year_2023/days/6.py +++ /dev/null @@ -1,19 +0,0 @@ -from advent_of_code.year_2023.solvers.day_6_solvers import ( - solve_day_6, -) -from advent_of_code.utils.input_handling import read_input, parse_args - - -def main(): - args = parse_args() - input = read_input(args.input_file) - result_part_1, result_part_2 = solve_day_6(input) - print( - f"Day 6: " - f" Result for part 1 is {result_part_1}. " - f" Result for part 2 is {result_part_2}. " - ) - - -if __name__ == "__main__": - main() diff --git a/src/advent_of_code/year_2023/days/__init__.py b/src/advent_of_code/year_2023/days/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/src/advent_of_code/year_2023/solvers/__init__.py b/src/advent_of_code/year_2023/solvers/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/tests/year_2023/test_day_1_solvers.py b/tests/year_2023/test_day_01.py similarity index 98% rename from tests/year_2023/test_day_1_solvers.py rename to tests/year_2023/test_day_01.py index 11f508e..60c9f9f 100644 --- a/tests/year_2023/test_day_1_solvers.py +++ b/tests/year_2023/test_day_01.py @@ -1,5 +1,5 @@ import pytest -from advent_of_code.year_2023.solvers.day_1_solvers import ( +from advent_of_code.year_2023.day_01 import ( convert_str_to_numerical, get_patterns, solve_all_calibration_values, diff --git a/tests/year_2023/test_day_2_solvers.py b/tests/year_2023/test_day_02.py similarity index 96% rename from tests/year_2023/test_day_2_solvers.py rename to tests/year_2023/test_day_02.py index 5fd9667..0c32b3c 100644 --- a/tests/year_2023/test_day_2_solvers.py +++ b/tests/year_2023/test_day_02.py @@ -1,5 +1,5 @@ import pytest -from advent_of_code.year_2023.solvers.day_2_solvers import ( +from advent_of_code.year_2023.day_02 import ( solve_day_2, check_game_is_possible, parse_game_string, diff --git a/tests/year_2023/test_day_3_solvers.py b/tests/year_2023/test_day_03.py similarity index 98% rename from tests/year_2023/test_day_3_solvers.py rename to tests/year_2023/test_day_03.py index 1bc6c7b..9edf633 100644 --- a/tests/year_2023/test_day_3_solvers.py +++ b/tests/year_2023/test_day_03.py @@ -1,6 +1,6 @@ import pytest -from advent_of_code.year_2023.solvers.day_3_solvers import ( +from advent_of_code.year_2023.day_03 import ( Part, Symbol, check_char_is_number, diff --git a/tests/year_2023/test_day_4_solvers.py b/tests/year_2023/test_day_04.py similarity index 96% rename from tests/year_2023/test_day_4_solvers.py rename to tests/year_2023/test_day_04.py index a453ffc..7540be4 100644 --- a/tests/year_2023/test_day_4_solvers.py +++ b/tests/year_2023/test_day_04.py @@ -1,5 +1,5 @@ import pytest -from advent_of_code.year_2023.solvers.day_4_solvers import ( +from advent_of_code.year_2023.day_04 import ( solve_day_4, compute_copies, create_cards, diff --git a/tests/year_2023/test_day_6_solvers.py b/tests/year_2023/test_day_06.py similarity index 97% rename from tests/year_2023/test_day_6_solvers.py rename to tests/year_2023/test_day_06.py index a586498..4ce3697 100644 --- a/tests/year_2023/test_day_6_solvers.py +++ b/tests/year_2023/test_day_06.py @@ -1,6 +1,6 @@ import pytest import math -from advent_of_code.year_2023.solvers.day_6_solvers import ( +from advent_of_code.year_2023.day_06 import ( Race, Races, create_races,