chore: create new project structure and aoc.py runner script

This commit is contained in:
thib8956 committed 2025-08-04 16:33:33 +02:00
1 parent f76375d835
commit e2964c6c36
91 files changed
+177 -113

No files matched your search

-17
View File
@@ -1,17 +0,0 @@
ran day1/day1.py in 0.016s
ran day2/day2.py in 0.021s
ran day3/day3.py in 0.017s
ran day4/day4.py in 0.039s
ran day5/day5.py in 0.191s
ran day6/day6.py in 9.655s
ran day7/day7.py in 2.227s
ran day8/day8.py in 0.017s
ran day9/day9.py in 11.159s
ran day10/day10.py in 0.052s
ran day11/day11.py in 0.116s
ran day12/day12.py in 0.170s
ran day13/day13.py in 0.014s
ran day14/day14.py in 3.729s
ran day16/day16.py in 10.082s
ran day18/day18.py in 0.872s
+26
View File
@@ -0,0 +1,26 @@
.PHONY: all venv install clean
# Define the virtual environment directory
VENV_DIR = ./venv
# Define the Python interpreter to use
PYTHON = $(VENV_DIR)/bin/python
# Default target
all: venv install
# Create virtual environment if it doesn't exist
venv:
@echo "Creating virtual environment..."
@if [ ! -d $(VENV_DIR) ]; then \
python3 -m venv $(VENV_DIR); \
fi
install: venv
@$(PYTHON) -m pip install --upgrade pip
@$(PYTHON) -m pip install -e .
clean:
@echo "Removing virtual environment..."
@rm -rf $(VENV_DIR)
+12 -2
View File
@@ -4,15 +4,25 @@ My solutions to the [advent of code](https://adventofcode.com/) challenges, writ
## How to run ## How to run
### Install project
Run `make install` or
Run from root directory (inside a virtualenv):
```shell
$ pip install -e .
```
### Run a single day ### Run a single day
```shell ```shell
$ python3 run.py <year> <day> $ aoc <year> <day>
``` ```
### Run a whole year ### Run a whole year
```shell ```shell
$ python3 run.py <year> $ aoc <year>
``` ```
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
View File
Whitespace-only changes.
File renamed without changes.
File renamed without changes.
View File
Whitespace-only changes.
File renamed without changes.
File renamed without changes.
@@ -1,79 +1,79 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
def manhattan_distance(p): def manhattan_distance(p):
return abs(p[0]) + abs(p[1]) return abs(p[0]) + abs(p[1])
def points_for_wire(wire): def points_for_wire(wire):
x, y, count = 0, 0, 0 x, y, count = 0, 0, 0
points = {} points = {}
# (x, y) # (x, y)
directions = {"R": (1, 0), "L": (-1, 0), "U": (0, 1), "D": (0, -1)} directions = {"R": (1, 0), "L": (-1, 0), "U": (0, 1), "D": (0, -1)}
for p in wire: for p in wire:
# D42 -> for _ in range(42) # D42 -> for _ in range(42)
for _ in range(int(p[1:])): for _ in range(int(p[1:])):
offset = directions[p[0]] offset = directions[p[0]]
x += offset[0] x += offset[0]
y += offset[1] y += offset[1]
count += 1 count += 1
points[(x ,y)] = count points[(x ,y)] = count
return points return points
def find_min_distance(wire1, wire2): def find_min_distance(wire1, wire2):
points1 = points_for_wire(wire1) points1 = points_for_wire(wire1)
points2 = points_for_wire(wire2) points2 = points_for_wire(wire2)
intersections = points1.keys() & points2.keys() intersections = points1.keys() & points2.keys()
closest = min((intersection for intersection in intersections), key=manhattan_distance) closest = min((intersection for intersection in intersections), key=manhattan_distance)
return manhattan_distance(closest) return manhattan_distance(closest)
def find_least_steps(wire1, wire2): def find_least_steps(wire1, wire2):
points1 = points_for_wire(wire1) points1 = points_for_wire(wire1)
points2 = points_for_wire(wire2) points2 = points_for_wire(wire2)
intersections = points1.keys() & points2.keys() intersections = points1.keys() & points2.keys()
# Intersection with the least steps # Intersection with the least steps
least_steps = min(intersections, key=lambda x: points1[x] + points2[x]) least_steps = min(intersections, key=lambda x: points1[x] + points2[x])
return points1[least_steps] + points2[least_steps] return points1[least_steps] + points2[least_steps]
def tests(): def tests():
inputs = ( inputs = (
(("R8", "U5", "L5", "D3"), ("U7", "R6", "D4", "L4")), (("R8", "U5", "L5", "D3"), ("U7", "R6", "D4", "L4")),
(("R75","D30","R83", "U83", "L12", "D49", "R71", "U7", "L72"), ("U62", "R66", "U55", "R34", "D71", "R55", "D58", "R83")), (("R75","D30","R83", "U83", "L12", "D49", "R71", "U7", "L72"), ("U62", "R66", "U55", "R34", "D71", "R55", "D58", "R83")),
(("R98", "U47", "R26", "D63", "R33", "U87", "L62", "D20", "R33", "U53", "R51"), ("U98", "R91", "D20", "R16", "D67", "R40", "U7", "R15", "U6", "R7")) (("R98", "U47", "R26", "D63", "R33", "U87", "L62", "D20", "R33", "U53", "R51"), ("U98", "R91", "D20", "R16", "D67", "R40", "U7", "R15", "U6", "R7"))
) )
# Part 1 # Part 1
expected = (6, 159, 135) expected = (6, 159, 135)
for i, inp in enumerate(inputs): for i, inp in enumerate(inputs):
result = find_min_distance(inp[0], inp[1]) result = find_min_distance(inp[0], inp[1])
assert result == expected[i], "Result for {} should be {}, was {}".format( assert result == expected[i], "Result for {} should be {}, was {}".format(
inp, expected[i], result inp, expected[i], result
) )
# Part 2 # Part 2
# expected number of steps # expected number of steps
expected_part2 = (30, 610, 410) expected_part2 = (30, 610, 410)
print("All tests passed.") print("All tests passed.")
for i, inp in enumerate(inputs): for i, inp in enumerate(inputs):
result = find_least_steps(inp[0], inp[1]) result = find_least_steps(inp[0], inp[1])
assert result == expected_part2[i], "Result for {} should be {}, was {}".format( assert result == expected_part2[i], "Result for {} should be {}, was {}".format(
inp, expected_part2[i], result inp, expected_part2[i], result
) )
if __name__ == "__main__": if __name__ == "__main__":
tests() tests()
import sys import sys
infile = sys.argv[1] if len(sys.argv) > 1 else "example.txt" infile = sys.argv[1] if len(sys.argv) > 1 else "example.txt"
with open(infile) as raw_input: with open(infile) as raw_input:
lines = raw_input.readlines() lines = raw_input.readlines()
wire1, wire2 = [line.strip("\n").split(",") for line in lines] wire1, wire2 = [line.strip("\n").split(",") for line in lines]
print("Part 1 -- distance = ", find_min_distance(wire1, wire2)) print("Part 1 -- distance = ", find_min_distance(wire1, wire2))
print("Part 2 -- steps = ", find_least_steps(wire1, wire2)) print("Part 2 -- steps = ", find_least_steps(wire1, wire2))
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -1,14 +1,11 @@
import sys import sys
from pathlib import Path from pathlib import Path
# TODO replace PYTHONPATH hack with a proper solution, like making intcode an
# installed module https://stackoverflow.com/a/50194143
sys.path.append(str(Path(__file__).absolute().parent.parent / "intcode")) sys.path.append(str(Path(__file__).absolute().parent.parent / "intcode"))
from itertools import cycle, permutations from itertools import cycle, permutations
from intcode import interpret_intcode, Interpreter from intcode import interpret_intcode, Interpreter
def main(inp): def main(inp):
mem = list(map(int, inp.readline().rstrip().split(","))) mem = list(map(int, inp.readline().rstrip().split(",")))
max_ret = 0 max_ret = 0
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
+41
View File
@@ -0,0 +1,41 @@
import argparse
from adventofcode.helper import run, get_input_file
def main():
parser = argparse.ArgumentParser(description="Advent of Code CLI")
subparsers = parser.add_subparsers(dest='command')
# Sous-commande init
init_parser = subparsers.add_parser('init', help='Init an aoc day')
init_parser.add_argument('year', type=int)
init_parser.add_argument('day', type=int)
# Sous-commande run
run_parser = subparsers.add_parser('run', help='Run an aoc day')
run_parser.add_argument('year', type=int)
run_parser.add_argument('day', type=int)
args = parser.parse_args()
if args.command == 'init':
handle_init(args.year, args.day)
elif args.command == 'run':
handle_run(args.year, args.day)
else:
parser.print_help()
def handle_init(year, day):
# TODO initialize directory if needed, download input file and create
# dayX.py from a template
raise NotImplementedError("init")
def handle_run(year, day):
run(year, day)
if __name__ == "__main__":
main()
+5 -12
View File
@@ -1,3 +1,4 @@
#!/usr/bin/env python3
import urllib.request import urllib.request
import getpass import getpass
import sys import sys
@@ -6,6 +7,7 @@ import subprocess
import os import os
from pathlib import Path from pathlib import Path
ROOTPATH = Path(os.path.dirname(os.path.realpath(__file__)))
_auth = None _auth = None
@@ -27,9 +29,9 @@ def get_input_file(year, day):
return res return res
def main(year, day): def run(year, day):
if day is not None: if day is not None:
path = Path(f"{year}/day{day}") path = ROOTPATH / Path(f"{year}/day{day}")
script_path = path / Path(f"day{day}.py") script_path = path / Path(f"day{day}.py")
input_path = path / Path("input.txt") input_path = path / Path("input.txt")
if not script_path.exists(): if not script_path.exists():
@@ -45,7 +47,7 @@ def main(year, day):
run_day(script_path, input_path) run_day(script_path, input_path)
else: else:
for day in range(1, 26): for day in range(1, 26):
path = Path(f"{year}/day{day}") path = ROOTPATH / Path(f"{year}/day{day}")
script_path = path / Path(f"day{day}.py") script_path = path / Path(f"day{day}.py")
input_path = path / Path("input.txt") input_path = path / Path("input.txt")
if script_path.exists(): if script_path.exists():
@@ -69,12 +71,3 @@ def run_day(script_path, input_path):
except subprocess.TimeoutExpired: except subprocess.TimeoutExpired:
print(f"> timeout {script_path} after 30s", file=sys.stderr) print(f"> timeout {script_path} after 30s", file=sys.stderr)
if __name__ == "__main__":
if len(sys.argv) <= 1:
print(f"Usage: {__file__} <year> [<day>]", file=sys.stderr)
exit(1)
year = sys.argv[1]
day = sys.argv[2] if len(sys.argv) > 2 else None
main(year, day)
+14
View File
@@ -0,0 +1,14 @@
[build-system]
requires = ["setuptools"]
build-backend = "setuptools.build_meta"
[project]
name = "adventofcode"
version = "1.0.0"
[project.scripts]
aoc = "adventofcode.aoc:main"
[tool.setuptools.packages.find]
where = ["."]