Skip to content

Commit

Permalink
update code base update auto table + update solution
Browse files Browse the repository at this point in the history
  • Loading branch information
vsedov committed Dec 1, 2024
1 parent 1e09c7d commit 417f172
Show file tree
Hide file tree
Showing 7 changed files with 146 additions and 151 deletions.
54 changes: 0 additions & 54 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -74,60 +74,6 @@ select = [ # https://docs.astral.sh/ruff/rules/ # TODO: enable additional linter
]

[tool.pytest.ini_options]
# skip slow tests by default
addopts = "-m 'not slow'"
markers = [
"slow: marks tests as slow",
]
xfail_strict = true


strict = true

# Disallow dynamic typing
disallow_any_unimported = true
disallow_any_decorated = true
disallow_any_generics = true
disallow_subclassing_any = true

# Untyped definitions and calls
disallow_untyped_calls = true
disallow_untyped_defs = true
disallow_incomplete_defs = true
check_untyped_defs = true
disallow_untyped_decorators = true

# Configuring warnings
warn_redundant_casts = true
warn_unused_ignores = true
warn_no_return = true
warn_return_any = true
warn_unreachable = true

# Miscellaneous strictness flags
allow_untyped_globals = false
allow_redefinition = true
enable_error_code = [ # https://mypy.readthedocs.io/en/stable/error_code_list2.html#error-codes-optional
'redundant-self',
'redundant-expr',
'possibly-undefined',
'truthy-bool',
'truthy-iterable',
'ignore-without-code',
'unused-awaitable',
'unused-ignore',
'explicit-override',
'unimported-reveal',
]
implicit_reexport = true
strict_equality = true

# Configuring error messages
show_column_numbers = true
pretty = true

# Miscellaneous
warn_unused_configs = true

[[tool.mypy.overrides]]
# aocd doesn't provide types https://github.com/wimglenn/advent-of-code-data/issues/78
Expand Down
23 changes: 14 additions & 9 deletions src/aoc/aoc2024/day_01.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,26 @@
from src.aoc.aoc_helper import Aoc


def parse(txt: str) -> Tuple[Tuple[int, ...], Tuple[int, ...]]:
cols = tuple(zip(*(map(int, line.split()) for line in txt.splitlines())))
if len(cols) != 2:
raise ValueError("Input must yield exactly two columns")
return cols
def parse(txt: str) -> tuple[tuple[int, ...], tuple[int, ...]]:
l, r = list(
map(
list,
zip(*[list(map(int, line.split())) for line in txt.splitlines()]),
)
)
return l, r


def part_a(txt: str) -> int:
left, right = parse(txt)
return sum(map(abs, map(sub, sorted(left), sorted(right))))
left_col, right_col = parse(txt)
return sum(
abs(l - r) for l, r in zip(sorted(sorted(left_col)), sorted(sorted(right_col)))
)


def part_b(txt: str) -> int:
left, right = parse(txt)
return sum(l * count for l, count in zip(left, Counter(right).values()))
l, r = parse(txt)
return sum(x * r.count(x) for x in l)


def main(txt: str) -> None:
Expand Down
29 changes: 29 additions & 0 deletions src/aoc/aoc2024/readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Advent of Code Solutions

| Day | Problem | Solution | Part A | Part B |
|-----|---------|----------|---------|---------|
| 01 | [Historian Hysteria](https://adventofcode.com/2024/day/1) | [Solution](day_01.py) | :x: | :heavy_check_mark: | :heavy_check_mark: |
| 02 | [?](https://adventofcode.com/2024/day/2) | [Solution](day_02.py) | :x: | :x: |
| 03 | [?](https://adventofcode.com/2024/day/3) | [Solution](day_03.py) | :x: | :x: |
| 04 | [?](https://adventofcode.com/2024/day/4) | [Solution](day_04.py) | :x: | :x: |
| 05 | [?](https://adventofcode.com/2024/day/5) | [Solution](day_05.py) | :x: | :x: |
| 06 | [?](https://adventofcode.com/2024/day/6) | [Solution](day_06.py) | :x: | :x: |
| 07 | [?](https://adventofcode.com/2024/day/7) | [Solution](day_07.py) | :x: | :x: |
| 08 | [?](https://adventofcode.com/2024/day/8) | [Solution](day_08.py) | :x: | :x: |
| 09 | [?](https://adventofcode.com/2024/day/9) | [Solution](day_09.py) | :x: | :x: |
| 10 | [?](https://adventofcode.com/2024/day/10) | [Solution](day_10.py) | :x: | :x: |
| 11 | [?](https://adventofcode.com/2024/day/11) | [Solution](day_11.py) | :x: | :x: |
| 12 | [?](https://adventofcode.com/2024/day/12) | [Solution](day_12.py) | :x: | :x: |
| 13 | [?](https://adventofcode.com/2024/day/13) | [Solution](day_13.py) | :x: | :x: |
| 14 | [?](https://adventofcode.com/2024/day/14) | [Solution](day_14.py) | :x: | :x: |
| 15 | [?](https://adventofcode.com/2024/day/15) | [Solution](day_15.py) | :x: | :x: |
| 16 | [?](https://adventofcode.com/2024/day/16) | [Solution](day_16.py) | :x: | :x: |
| 17 | [?](https://adventofcode.com/2024/day/17) | [Solution](day_17.py) | :x: | :x: |
| 18 | [?](https://adventofcode.com/2024/day/18) | [Solution](day_18.py) | :x: | :x: |
| 19 | [?](https://adventofcode.com/2024/day/19) | [Solution](day_19.py) | :x: | :x: |
| 20 | [?](https://adventofcode.com/2024/day/20) | [Solution](day_20.py) | :x: | :x: |
| 21 | [?](https://adventofcode.com/2024/day/21) | [Solution](day_21.py) | :x: | :x: |
| 22 | [?](https://adventofcode.com/2024/day/22) | [Solution](day_22.py) | :x: | :x: |
| 23 | [?](https://adventofcode.com/2024/day/23) | [Solution](day_23.py) | :x: | :x: |
| 24 | [?](https://adventofcode.com/2024/day/24) | [Solution](day_24.py) | :x: | :x: |
| 25 | [?](https://adventofcode.com/2024/day/25) | [Solution](day_25.py) | :x: | :x: |
128 changes: 102 additions & 26 deletions src/aoc/aoc_helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
PROJECT_ROOT = Path(__file__).parent.parent.parent



def warn(s):
logging.warning(s)

Expand All @@ -28,12 +27,15 @@ def info(s):


class Aoc:

def __init__(self, day: int = int(datetime.now().day), years: int = int(datetime.now().year)):
def __init__(
self, day: int = int(datetime.now().day), years: int = int(datetime.now().year)
):
self.day = day
self.year = years
self.data = get_data(day=self.day, year=self.year)
self.test_module = importlib.import_module(f"tests.aoc{self.year}.{self.year}_day_{self.day:02d}_test")
self.test_module = importlib.import_module(
f"tests.aoc{self.year}.{self.year}_day_{self.day:02d}_test"
)

def submit(self, answer, part=None) -> None:
submit(answer, part=part, day=self.day, year=self.year)
Expand All @@ -52,7 +54,13 @@ def wrapper(self, func) -> Any:
self.submit(answer)
return answer

def run(self, func=None, submit: bool = False, part: Union[None, str] = None, readme_update: bool = False) -> None:
def run(
self,
func=None,
submit: bool = False,
part: Union[None, str] = None,
readme_update: bool = False,
) -> None:
"""Run a function and submit the answer to the website.
func : Main Function to run
This need to be the outside function, although it can be None
Expand All @@ -69,7 +77,9 @@ def run(self, func=None, submit: bool = False, part: Union[None, str] = None, re
func(self.get_data())

if submit:
modules = importlib.import_module(f"src.aoc.aoc{self.year}.day_{self.day:02d}")
modules = importlib.import_module(
f"src.aoc.aoc{self.year}.day_{self.day:02d}"
)
tests = (self.run_test("a"), self.run_test("b"))
options = {
"a": (0,),
Expand All @@ -83,7 +93,10 @@ def run(self, func=None, submit: bool = False, part: Union[None, str] = None, re
current_part = "a" if i == 0 else "b"
ic(f"Part {current_part} is not passing the test cases")
if tests[i]:
self.submit(getattr(modules, f"part_{current_part}")(self.get_data()), part=current_part)
self.submit(
getattr(modules, f"part_{current_part}")(self.get_data()),
part=current_part,
)
else:
ic.configureOutput(outputFunction=warn)
ic(f"Part {current_part} is not passing the test cases")
Expand All @@ -96,37 +109,100 @@ def run_test(self, part: Literal["a", "b"]) -> bool:
assert inspect.isfunction(f)
try:
f()
print(f"Test {part} Passed")
return True
except AssertionError:
print(f"Test {part} failed")
return False
print(f"Test {part} failed")
return False

def update_readme(self) -> None:

writeer_path = os.path.join(PROJECT_ROOT, f"src/aoc/aoc{self.year}", "readme.md")
with open(writeer_path, "r+") as f:
lines = f.readlines()
for i, line in enumerate(lines):
removed_leading_zero = str(self.day).lstrip("0")
if f"| [?](https://adventofcode.com/{self.year}/day/{removed_leading_zero})" in line:
print(f"Found line {i}")
lines[i] = line.replace(
f"| [?](https://adventofcode.com/{self.year}/day/{removed_leading_zero})",
f"| [{self.get_problem_name()}](https://adventofcode.com/{self.year}/day/{removed_leading_zero})",
).replace(" :x: ", ":heavy_check_mark:")

break
f.seek(0)
readme_path = os.path.join(PROJECT_ROOT, f"src/aoc/aoc{self.year}", "readme.md")

# Create directory if it doesn't exist
os.makedirs(os.path.dirname(readme_path), exist_ok=True)

# Initialize table headers
table_headers = [
"# Advent of Code Solutions\n",
"\n",
"| Day | Problem | Solution | Part A | Part B |\n",
"|-----|---------|----------|---------|---------|",
]

# Generate default table content for all 25 days
default_table = []
for day in range(1, 26):
default_table.append(
f"| {day:02d} | [?](https://adventofcode.com/{self.year}/day/{day}) | [Solution](day_{day:02d}.py) | :x: | :x: |\n"
)

try:
# Try to read existing content
with open(readme_path, "r") as f:
lines = f.readlines()

# If file exists but doesn't have table, create new
if not any("| Day | Problem |" in line for line in lines):
lines = table_headers + ["\n"] + default_table

except FileNotFoundError:
# Create new file with default table
lines = table_headers + ["\n"] + default_table

# Update the specific day's entry
removed_leading_zero = str(self.day).lstrip("0")
day_found = False

for i, line in enumerate(lines):
if f"day/{removed_leading_zero})" in line:
day_found = True
# Get current status
current_line = line

# Update problem name
if "[?]" in current_line:
current_line = current_line.replace(
f"[?](https://adventofcode.com/{self.year}/day/{removed_leading_zero})",
f"[{self.get_problem_name()}](https://adventofcode.com/{self.year}/day/{removed_leading_zero})",
)

# Update completion status based on test results
test_a_passed = self.run_test("a")
test_b_passed = self.run_test("b")

# Replace status markers
parts = current_line.split("|")
if test_a_passed:
parts[-2] = " :heavy_check_mark: "
if test_b_passed:
parts[-1] = " :heavy_check_mark: |\n"

lines[i] = "|".join(parts)
break

# If day wasn't found in existing table, add it
if not day_found:
new_line = (
f"| {self.day:02d} "
f"| [{self.get_problem_name()}](https://adventofcode.com/{self.year}/day/{removed_leading_zero}) "
f"| [Solution](day_{self.day:02d}.py) "
f"| {'✓' if self.run_test('a') else '❌'} "
f"| {'✓' if self.run_test('b') else '❌'} |\n"
)
lines.append(new_line)

# Write back to file
with open(readme_path, "w") as f:
f.writelines(lines)
f.truncate()

def run_all_tests(self) -> None:
__import__("os").system("poetry run pytest")
__import__("os").system("uv run pytest")

def custom_solve(self) -> None:
solve(year=self.year, day=self.day, data=self.get_data())

def get_problem_name(self) -> str:
puzzle = Puzzle(year=self.year, day=self.day)
soup = puzzle._soup()
return soup.find("h2").text.replace("---", "").replace(f"Day {self.day}:", "").strip()
return puzzle.title
41 changes: 0 additions & 41 deletions tests/aoc2022/2022_day_11_test.py

This file was deleted.

18 changes: 0 additions & 18 deletions tests/aoc2023/2023_day_01_test.py

This file was deleted.

4 changes: 1 addition & 3 deletions tests/aoc2024/2024_day_01_test.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import pytest

from src.aoc.aoc2024 import day_01 as d

TEST_INPUT = """
Expand All @@ -17,4 +15,4 @@ def test_a() -> None:


def test_b() -> None:
assert d.part_b(TEST_INPUT) == 21
assert d.part_b(TEST_INPUT) == 31

0 comments on commit 417f172

Please sign in to comment.