refactor: move init command to helper.py

This commit is contained in:
2026-05-14 18:20:10 +02:00
parent 8bdde87767
commit 50edd189ac
6 changed files with 95 additions and 81 deletions
+3 -2
View File
@@ -11,10 +11,12 @@ all: venv install
# Create virtual environment if it doesn't exist # Create virtual environment if it doesn't exist
venv: venv:
@echo "Creating virtual environment..."
@if [ ! -d $(VENV_DIR) ]; then \ @if [ ! -d $(VENV_DIR) ]; then \
echo "Creating virtual environment..."; \
python3 -m venv $(VENV_DIR); \ python3 -m venv $(VENV_DIR); \
fi fi
@echo "Source virtualenv with:"
@echo "source $(VENV_DIR)/bin/activate"
install: venv install: venv
@$(PYTHON) -m pip install --upgrade pip @$(PYTHON) -m pip install --upgrade pip
@@ -23,4 +25,3 @@ install: venv
clean: clean:
@echo "Removing virtual environment..." @echo "Removing virtual environment..."
@rm -rf $(VENV_DIR) @rm -rf $(VENV_DIR)
+13 -2
View File
@@ -27,12 +27,23 @@ $ pip install -e .
### Run a single day ### Run a single day
```shell ```shell
$ aoc <year> <day> $ aoc run <year> <day>
``` ```
### Run a whole year ### Run a whole year
```shell ```shell
$ aoc <year> $ aoc run <year>
``` ```
## Run all years
```shell
$ aoc run all
```
## Init a new day
```shell
$ aoc init <year> <day>
```
View File
+13 -16
View File
@@ -1,14 +1,11 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
# TODO replace PYTHONPATH hack with a proper solution, like making intcode an
# installed module https://stackoverflow.com/a/50194143
import sys import sys
import time import time
from itertools import zip_longest
from pathlib import Path
from collections import defaultdict, Counter
from dataclasses import dataclass from dataclasses import dataclass
from itertools import zip_longest
from adventofcode.intcode import Interpreter, interpret_intcode
from adventofcode.intcode import interpret_intcode, Interpreter
@dataclass @dataclass
class State: class State:
@@ -28,7 +25,7 @@ def grouper(n, iterable):
def part1(program): def part1(program):
interpreter = interpret_intcode(program) interpreter = interpret_intcode(program)
game = { (x, y): tile for x, y, tile in grouper(3, interpreter.stdout) } game = {(x, y): tile for x, y, tile in grouper(3, interpreter.stdout)}
print("Part 1: ", len([x for x in game.values() if x == 2])) print("Part 1: ", len([x for x in game.values() if x == 2]))
@@ -39,11 +36,10 @@ def parse_map(output):
if (x, y) == (-1, 0): if (x, y) == (-1, 0):
score = tile score = tile
else: else:
grid[x,y] = tile grid[x, y] = tile
paddle = next((k for k, v in grid.items() if v == 3), None) paddle = next((k for k, v in grid.items() if v == 3), None)
ball = next((k for k, v in grid.items() if v == 4), None) ball = next((k for k, v in grid.items() if v == 4), None)
bounds = (max(x for x, y in grid.keys()), bounds = (max(x for x, y in grid.keys()), max(y for x, y in grid.keys()))
max(y for x, y in grid.keys()))
return grid, bounds, score, paddle, ball return grid, bounds, score, paddle, ball
@@ -64,7 +60,7 @@ def update_state(state: State, stdout):
else: else:
# merge grid # merge grid
for (x, y), v in grid.items(): for (x, y), v in grid.items():
state.grid[x,y] = v state.grid[x, y] = v
return state return state
@@ -77,7 +73,7 @@ def part2(program):
state = update_state(state, interpreter.stdout) state = update_state(state, interpreter.stdout)
if state.stopped: if state.stopped:
break break
#print_map(state) # print_map(state)
interpreter.stdout.clear() interpreter.stdout.clear()
paddle_x, ball_x = state.paddle_pos[0], state.ball_pos[0] paddle_x, ball_x = state.paddle_pos[0], state.ball_pos[0]
if paddle_x < ball_x: # move right if paddle_x < ball_x: # move right
@@ -91,16 +87,16 @@ def part2(program):
def print_map(state): def print_map(state):
clear = "\033[2J" clear = "\033[2J"
tiles = { 0: " ", 1: "#", 2: "~", 3: "_", 4: "O" } tiles = {0: " ", 1: "#", 2: "~", 3: "_", 4: "O"}
max_x, max_y = state.bounds max_x, max_y = state.bounds
print(clear) print(clear)
for y in range(max_y + 1): for y in range(max_y + 1):
l = [] l = []
for x in range(max_x + 1): for x in range(max_x + 1):
tile = state.grid[x,y] tile = state.grid[x, y]
l.append(tiles[tile]) l.append(tiles[tile])
print("".join(l)) print("".join(l))
time.sleep(1/60) time.sleep(1 / 60)
def main(inp): def main(inp):
@@ -108,9 +104,10 @@ def main(inp):
part1(program) part1(program)
part2(program) part2(program)
if __name__ == "__main__": if __name__ == "__main__":
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:
main(raw_input) main(raw_input)
+2 -53
View File
@@ -1,29 +1,6 @@
import argparse import argparse
from pathlib import Path
from adventofcode.helper import ( from adventofcode.helper import init, run, run_all
MAX_YEAR,
MIN_YEAR,
get_auth,
get_input_file,
get_max_day,
run,
run_all,
)
TEMPLATE = """#!/usr/bin/env python3
import fileinput
def main(inp):
for l in inp:
print(l)
if __name__ == '__main__':
lines = [x.rstrip() for x in fileinput.input()]
main(lines)
"""
def year_or_all(value): def year_or_all(value):
@@ -69,35 +46,7 @@ def main():
def handle_init(year, day): def handle_init(year, day):
if not MIN_YEAR <= year <= MAX_YEAR: init(year, day)
print(f"Invalid year: {year}. Must be between {MIN_YEAR} and {MAX_YEAR}.")
return
max_day = get_max_day(year)
if not 1 <= day <= max_day:
print(f"Invalid day: {day}. Must be between 1 and {max_day}.")
return
root = Path(__file__).parent
day_dir = root / str(year) / f"day{day}"
day_dir.mkdir(parents=True, exist_ok=True)
script_path = day_dir / f"day{day}.py"
if not script_path.exists():
script_path.write_text(TEMPLATE)
print(f"Created {script_path}")
else:
print(f"{script_path} already exists")
input_path = day_dir / "input.txt"
if not input_path.exists():
try:
get_auth()
res = get_input_file(year, day)
input_path.write_bytes(res.read())
print(f"Downloaded {input_path}")
except Exception as e:
print(f"Could not download input: {e}", file=__import__("sys").stderr)
def handle_run(year, day): def handle_run(year, day):
+64 -8
View File
@@ -9,6 +9,22 @@ from pathlib import Path
MIN_YEAR = 2015 MIN_YEAR = 2015
MAX_YEAR = 2025 MAX_YEAR = 2025
ROOTPATH = Path(os.path.dirname(os.path.realpath(__file__))) ROOTPATH = Path(os.path.dirname(os.path.realpath(__file__)))
TEMPLATE = """#!/usr/bin/env python3
import fileinput
def main(inp):
for l in inp:
print(l)
if __name__ == '__main__':
lines = [x.rstrip() for x in fileinput.input()]
main(lines)
"""
_auth = None
def get_max_day(year): def get_max_day(year):
@@ -16,7 +32,16 @@ def get_max_day(year):
return 25 if year < 2025 else 12 return 25 if year < 2025 else 12
_auth = None def validate_day(day, year):
if not 1 <= day <= get_max_day(year):
return f"Invalid day: {day} for year {year}. Must be between 1 and {get_max_day(year)}"
return None
def validate_year(year):
if not MIN_YEAR <= year <= MAX_YEAR:
return f"Invalid year: {year}. Must be between {MIN_YEAR} and {MAX_YEAR}"
return None
def _load_env_file(env_path: Path) -> dict: def _load_env_file(env_path: Path) -> dict:
@@ -69,25 +94,56 @@ def resolve_paths(year, day):
return script_path, input_path return script_path, input_path
def init(year, day):
if (err := validate_year(year)) is not None:
print(err)
return
if (err := validate_day(day, year)) is not None:
print(err)
return
path = ROOTPATH / Path(f"{year}/day{day}")
path.mkdir(parents=True, exist_ok=True)
script_path, input_path = resolve_paths(year, day)
if script_path.exists():
print(f"Script {script_path} already exists.")
else:
script_path.write_text(TEMPLATE)
print(f"Created {script_path}")
if input_path.exists():
print(f"Input {input_path} already exists.")
else:
try:
get_auth()
res = get_input_file(year, day)
input_path.write_bytes(res.read())
print(f"Downloaded {input_path}")
except Exception as e:
print(f"Could not download input: {e}", file=sys.stderr)
def run_all(): def run_all():
for year in range(2015, MAX_YEAR + 1): for year in range(MIN_YEAR, MAX_YEAR + 1):
print(f"Running year {year}") print(f"Running year {year}")
run(year, None) run(year, None)
def run(year, day): def run(year, day):
if not MIN_YEAR <= year <= MAX_YEAR: if (err := validate_year(year)) is not None:
print(f"Invalid year {year}", file=sys.stderr) print(err, file=sys.stderr)
exit(1) exit(1)
if day is not None: if day is not None:
if not 1 <= day <= get_max_day(year): if (err := validate_day(day, year)) is not None:
print(f"Invalid day {day}", file=sys.stderr) print(err, file=sys.stderr)
exit(1) exit(1)
script_path, input_path = resolve_paths(year, day) script_path, input_path = resolve_paths(year, day)
if not script_path.exists(): if not script_path.exists():
print(f"Invalid day {day}", file=sys.stderr) print(f"Script file {script_path} does not exist", file=sys.stderr)
exit(1) exit(1)
if not input_path.exists(): if not input_path.exists():
print(f"Downloading input file {input_path}") print(f"Downloading input file {input_path}")
@@ -98,7 +154,7 @@ def run(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, get_max_day(year) + 1):
script_path, input_path = resolve_paths(year, day) script_path, input_path = resolve_paths(year, day)
if script_path.exists(): if script_path.exists():
if not input_path.exists(): if not input_path.exists():