Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/copilot-instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ Project-specific conventions and gotchas (be precise):

- Zero-padding is significant: day files and input files are named with two-digit zero padding (e.g., `day_01.py` ↔ `01.dat`). `run_day.py` zero-pads the provided `--day` argument.
- `run_all_solutions.py` relies on simple `os.listdir` ordering; do not assume nested directories beyond `src/advent_of_code/year_<YYYY>/`.
- The repo uses `python3` in scripts; local development should use a Python 3.8+ interpreter (pyproject says >=3.8). Black config targets Python 3.9 but code is compatible with 3.8+.
- The repo uses `python3` in scripts; local development should use a Python 3.8+ interpreter (pyproject says >=3.8). Black config targets Python 3.12 but code is compatible with 3.8+.
- Tests import the package from `src/` via pytest config. When editing tests or modules, ensure the module path `advent_of_code.year_<YYYY>.day_<NN>` is importable.

Small implementation checklist for a new/updated day module:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/orchestrator.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ jobs:
strategy:
fail-fast: false
matrix:
python-version: ["3.9"]
python-version: ["3.12"]

steps:
- uses: actions/checkout@v3
Expand Down
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM python:3.9
FROM python:3.12

RUN mkdir -p /usr/src/app

Expand Down
19 changes: 16 additions & 3 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,19 @@ authors = [
]
description = "Code for Advent of Code 2023"
readme = "README.md"
requires-python = ">=3.8"
requires-python = ">=3.12,<4"
classifiers = [
"Programming Language :: Python :: 3",
"License :: OSI Approved :: MIT License",
"Operating System :: OS Independent",
]
dependencies = [
"numpy==1.26.2",
"pytest==7.4.3",
"ruff>=0.14.8",
"setuptools==68.2.2",
"uv>=0.9.16",
]

[project.urls]
Homepage = "https://github.com/jameslawlor/advent-of-code-python"
Expand All @@ -31,8 +38,14 @@ addopts = [

[tool.black]
line-length = 88
target-version = ['py39']
target-version = ['py312']

[tool.ruff]
line-length = 88
target-version = "py312"
exclude = ["__pycache__", ".venv", "build", "dist"]
lint.select = ["E", "F", "W", "C", "B", "I"]

[project.scripts]
# Console script for running day solutions: after `pip install -e .` you can run `aoc --year 2025 --day 1`
aoc = "advent_of_code.cli:main"
aoc = "advent_of_code.cli:main"
4 changes: 3 additions & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
ruff==0.1.9
numpy==1.26.2
pytest==7.4.3
ruff==0.14.8
setuptools==68.2.2
uv==0.9.16
black
3 changes: 1 addition & 2 deletions scripts/generate_new_day_skeleton_files_from_templates.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import os
import argparse

import os

TEMPLATES_PATH = os.path.join("scripts", "templates")

Expand Down
23 changes: 16 additions & 7 deletions scripts/run_all_solutions.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,11 @@
RUN_DAY_SCRIPT = "scripts/run_day.py"
INPUTS_DIR = "inputs"


def discover_and_run_solutions():
# Regex to match "day_<number>.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)
Expand All @@ -24,25 +25,33 @@ def discover_and_run_solutions():
day_number = match.group(1)

# Build input file path
input_file = os.path.join(INPUTS_DIR, f"year_{year_number}", f"{day_number}.dat")
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.")
print(f"Input for {year_number} {day_number} missing, 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
"python3",
RUN_DAY_SCRIPT,
"--year",
year_number,
"--day",
str(
int(day_number)
), # Remove leading zero for argument
],
check=True
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()
30 changes: 18 additions & 12 deletions scripts/run_day.py
Original file line number Diff line number Diff line change
@@ -1,24 +1,27 @@
import argparse
import importlib
import sys
import argparse


def main():
parser = argparse.ArgumentParser(description="Run a specific Advent of Code solution.")
parser = argparse.ArgumentParser(
description="Run a specific Advent of Code solution."
)
# parser.add_argument(
# "--input_file",
# required=True,
# type=str,
# "--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,
"--year",
required=True,
type=int,
)
parser.add_argument(
"--day",
required=True,
type=str,
"--day",
required=True,
type=str,
)

args = parser.parse_args()
Expand All @@ -35,10 +38,13 @@ def main():
module.main(input_file)
else:
print("man")
print(f"The module {day_module} does not have a 'main(input_file)' function.")
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()
8 changes: 7 additions & 1 deletion setup.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
from setuptools import setup

setup(name="advent-of-code-python", version="0.1", description="advent-of-code-python", packages=["src/advent_of_code"])
setup(
name="advent-of-code-python",
version="0.1",
description="advent-of-code-python",
packages=["src/advent_of_code"],
python_requires=">=3.12,<4",
)
2 changes: 1 addition & 1 deletion src/advent_of_code/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@

This file delegates to `advent_of_code.cli:main`.
"""
from .cli import main

from .cli import main

if __name__ == "__main__":
raise SystemExit(main())
20 changes: 14 additions & 6 deletions src/advent_of_code/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@

This duplicates the behavior of `scripts/run_day.py` but lives inside the package.
"""

from __future__ import annotations

import argparse
import importlib
import sys
from typing import Sequence, Optional
from typing import Optional, Sequence


def main(argv: Optional[Sequence[str]] = None) -> int:
Expand All @@ -24,8 +24,12 @@ def main(argv: Optional[Sequence[str]] = None) -> int:
Returns:
exit code (0 on success).
"""
parser = argparse.ArgumentParser(prog="aoc", description="Run an Advent of Code solution module")
parser.add_argument("--year", required=True, type=int, help="Year directory (e.g. 2025)")
parser = argparse.ArgumentParser(
prog="aoc", description="Run an Advent of Code solution module"
)
parser.add_argument(
"--year", required=True, type=int, help="Year directory (e.g. 2025)"
)
parser.add_argument("--day", required=True, type=str, help="Day number (1 or 01)")

args = parser.parse_args(argv)
Expand All @@ -39,8 +43,12 @@ def main(argv: Optional[Sequence[str]] = None) -> int:
module = importlib.import_module(day_module)
except ModuleNotFoundError as exc:
print(f"Could not find module: {day_module}")
# Helpful hint: if running from the repository root, make sure `src` is on PYTHONPATH or install the package editable
print("Hint: run with `PYTHONPATH=src python -m advent_of_code ...` or `pip install -e .` to make the package importable")
# Helpful hint: if running from the repository root,
# make sure `src` is on PYTHONPATH or install the package editable.
print(
"Hint: run with `PYTHONPATH=src python -m advent_of_code ...` "
"or `pip install -e .` to make the package importable"
)
if isinstance(exc, ModuleNotFoundError):
# show original message
print(str(exc))
Expand Down
14 changes: 7 additions & 7 deletions src/advent_of_code/year_2023/day_01.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import re

from advent_of_code.utils.input_handling import (
read_input,
)

import re

SPELLED_NUMBERS_MAPPING = {
"one": 1,
"two": 2,
Expand Down Expand Up @@ -95,26 +95,26 @@ def find_indices_of_patterns(line, patterns_to_find):

return patterns_and_indices

def run_part(input_data, accept_written_digits):

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)
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)
calibration_value_sum = run_part(input_data, accept_written_digits=True)

print(f"Part 2: Solution is {calibration_value_sum}.")



if __name__ == "__main__":
main()
4 changes: 2 additions & 2 deletions src/advent_of_code/year_2023/day_02.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from advent_of_code.utils.input_handling import read_input

import re

from advent_of_code.utils.input_handling import read_input

BAG_CONSTRAINTS = {"red": 12, "green": 13, "blue": 14}


Expand Down
5 changes: 2 additions & 3 deletions src/advent_of_code/year_2023/day_03.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from advent_of_code.utils.input_handling import read_input

import re

from advent_of_code.utils.input_handling import read_input


class Part:
def __init__(self, part_number):
Expand Down Expand Up @@ -193,7 +193,6 @@ def solve_day_3(input) -> int:
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)
Expand Down
4 changes: 2 additions & 2 deletions src/advent_of_code/year_2023/day_04.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from advent_of_code.utils.input_handling import read_input

import re

from advent_of_code.utils.input_handling import read_input


class Card:
def __init__(self, card_number, winning_numbers, numbers_i_have):
Expand Down
8 changes: 4 additions & 4 deletions src/advent_of_code/year_2023/day_06.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
from advent_of_code.utils.input_handling import read_input

import math
import re

from advent_of_code.utils.input_handling import read_input


class Race:
def __init__(self, time, distance_to_beat):
Expand All @@ -11,7 +11,7 @@ def __init__(self, time, distance_to_beat):
self._compute_ways_to_win()

def __str__(self):
return f"time: {self.time} | " f"distance_to_beat: {self.distance_to_beat} | "
return f"time: {self.time} | distance_to_beat: {self.distance_to_beat} | "

def _compute_ways_to_win(self):
possible_speeds = range(self.time + 1)
Expand Down Expand Up @@ -50,7 +50,7 @@ def create_races(input, part=1):
times = ["".join(times)]
distances = ["".join(distances)]

for time, distance in zip(times, distances):
for time, distance in zip(times, distances, strict=False):
race = Race(time=time, distance_to_beat=distance)
races.add_race(race)

Expand Down
13 changes: 8 additions & 5 deletions src/advent_of_code/year_2024/day_01.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,21 @@
from advent_of_code.utils.input_handling import read_input, read_side_by_side_list_format
from advent_of_code.utils.input_handling import (
read_input,
read_side_by_side_list_format,
)


def compute_distance(i, j):
return abs(i - j)


def solve_part_1(parsed_input):
(list1, list2) = parsed_input
list1_ordered = list(sorted(list1))
list2_ordered = list(sorted(list2))
list1_ordered = sorted(list1)
list2_ordered = sorted(list2)

total_distance = 0

for i, j in zip(list1_ordered, list2_ordered):
for i, j in zip(list1_ordered, list2_ordered, strict=False):
total_distance += compute_distance(i, j)

return total_distance
Expand All @@ -32,7 +36,6 @@ def solve_part_2(parsed_input):
return total_similarity_score



def solve(input):
parsed_input = read_side_by_side_list_format(input)
part_1_solution = solve_part_1(parsed_input)
Expand Down
Loading