Skip to content

Commit b10b68b

Browse files
committed
Refactor to SolutionBase
1 parent 8afa8a3 commit b10b68b

22 files changed

+1561
-1224
lines changed

src/main/python/AoC2022_02.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
#
55

66
import sys
7-
from typing import Callable
7+
from collections.abc import Callable
88

99
from aoc.common import InputData
1010
from aoc.common import SolutionBase

src/main/python/AoC2022_03.py

Lines changed: 53 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -4,37 +4,11 @@
44
#
55

66

7-
from aoc.common import aoc_main
8-
9-
10-
def _priority(ch: str) -> int:
11-
if "a" <= ch <= "z":
12-
return ord(ch) - ord("a") + 1
13-
else:
14-
return ord(ch) - ord("A") + 27
15-
16-
17-
def part_1(inputs: tuple[str, ...]) -> int:
18-
ans = 0
19-
for input_ in inputs:
20-
ln = len(input_) // 2
21-
s1 = {_ for _ in input_[:ln]}
22-
s2 = {_ for _ in input_[ln:]}
23-
ch = (s1 & s2).pop()
24-
ans += _priority(ch)
25-
return ans
26-
27-
28-
def part_2(inputs: tuple[str, ...]) -> int:
29-
ans = 0
30-
for i in range(0, len(inputs), 3):
31-
s1 = {_ for _ in inputs[i]}
32-
s2 = {_ for _ in inputs[i + 1]}
33-
s3 = {_ for _ in inputs[i + 2]}
34-
ch = (s1 & s2 & s3).pop()
35-
ans += _priority(ch)
36-
return ans
7+
import sys
378

9+
from aoc.common import InputData
10+
from aoc.common import SolutionBase
11+
from aoc.common import aoc_samples
3812

3913
TEST = """\
4014
vJrwpWtwJgWrhcsFMMfFFhFp
@@ -43,13 +17,58 @@ def part_2(inputs: tuple[str, ...]) -> int:
4317
wMqvLMZHhHMvwLHjbvcjnnSBnvTQFn
4418
ttgJtRGJQctTZtZT
4519
CrZsJsPPZsGzwwsLwLmpwMDw
46-
""".splitlines()
20+
"""
21+
22+
23+
Input = tuple[str, ...]
24+
Output1 = int
25+
Output2 = int
26+
27+
28+
class Solution(SolutionBase[Input, Output1, Output2]):
29+
def parse_input(self, input_data: InputData) -> Input:
30+
return tuple(input_data)
31+
32+
def priority(self, ch: str) -> int:
33+
if "a" <= ch <= "z":
34+
return ord(ch) - ord("a") + 1
35+
return ord(ch) - ord("A") + 27
36+
37+
def part_1(self, inputs: Input) -> Output1:
38+
ans = 0
39+
for input_ in inputs:
40+
ln = len(input_) // 2
41+
s1 = set(input_[:ln])
42+
s2 = set(input_[ln:])
43+
ch = (s1 & s2).pop()
44+
ans += self.priority(ch)
45+
return ans
46+
47+
def part_2(self, inputs: Input) -> Output2:
48+
ans = 0
49+
for i in range(0, len(inputs), 3):
50+
s1 = set(inputs[i])
51+
s2 = set(inputs[i + 1])
52+
s3 = set(inputs[i + 2])
53+
ch = (s1 & s2 & s3).pop()
54+
ans += self.priority(ch)
55+
return ans
56+
57+
@aoc_samples(
58+
(
59+
("part_1", TEST, 157),
60+
("part_2", TEST, 70),
61+
)
62+
)
63+
def samples(self) -> None:
64+
pass
65+
66+
67+
solution = Solution(2022, 3)
4768

4869

49-
@aoc_main(2022, 3, part_1, part_2)
5070
def main() -> None:
51-
assert part_1(TEST) == 157 # type:ignore[arg-type]
52-
assert part_2(TEST) == 70 # type:ignore[arg-type]
71+
solution.run(sys.argv)
5372

5473

5574
if __name__ == "__main__":

src/main/python/AoC2022_04.py

Lines changed: 53 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -7,32 +7,16 @@
77
from __future__ import annotations
88

99
import re
10-
from typing import Callable
10+
from typing import TYPE_CHECKING
1111

12-
from aoc.common import aoc_main
12+
if TYPE_CHECKING:
13+
from collections.abc import Callable
1314

15+
import sys
1416

15-
def _solve(
16-
inputs: tuple[str, ...], f: Callable[[set[int], set[int]], bool]
17-
) -> int:
18-
return sum(
19-
f(
20-
set(range(n1, n2 + 1)),
21-
set(range(n3, n4 + 1)),
22-
)
23-
for n1, n2, n3, n4 in (
24-
(int(n) for n in re.findall(r"[0-9]+", line)) for line in inputs
25-
)
26-
)
27-
28-
29-
def part_1(inputs: tuple[str, ...]) -> int:
30-
return _solve(inputs, lambda s1, s2: s1.issubset(s2) or s2.issubset(s1))
31-
32-
33-
def part_2(inputs: tuple[str, ...]) -> int:
34-
return _solve(inputs, lambda s1, s2: not s1.isdisjoint(s2))
35-
17+
from aoc.common import InputData
18+
from aoc.common import SolutionBase
19+
from aoc.common import aoc_samples
3620

3721
TEST = """\
3822
2-4,6-8
@@ -41,13 +25,55 @@ def part_2(inputs: tuple[str, ...]) -> int:
4125
2-8,3-7
4226
6-6,4-6
4327
2-6,4-8
44-
""".splitlines()
28+
"""
29+
30+
31+
Input = tuple[str, ...]
32+
Output1 = int
33+
Output2 = int
34+
RE = re.compile(r"[0-9]+")
35+
36+
37+
class Solution(SolutionBase[Input, Output1, Output2]):
38+
def parse_input(self, input_data: InputData) -> Input:
39+
return tuple(input_data)
40+
41+
def solve(
42+
self, inputs: tuple[str, ...], f: Callable[[set[int], set[int]], bool]
43+
) -> int:
44+
return sum(
45+
f(
46+
set(range(n1, n2 + 1)),
47+
set(range(n3, n4 + 1)),
48+
)
49+
for n1, n2, n3, n4 in (
50+
(int(n) for n in RE.findall(line)) for line in inputs
51+
)
52+
)
53+
54+
def part_1(self, inputs: Input) -> Output1:
55+
return self.solve(
56+
inputs, lambda s1, s2: s1.issubset(s2) or s2.issubset(s1)
57+
)
58+
59+
def part_2(self, inputs: Input) -> Output2:
60+
return self.solve(inputs, lambda s1, s2: not s1.isdisjoint(s2))
61+
62+
@aoc_samples(
63+
(
64+
("part_1", TEST, 2),
65+
("part_2", TEST, 4),
66+
)
67+
)
68+
def samples(self) -> None:
69+
pass
70+
71+
72+
solution = Solution(2022, 4)
4573

4674

47-
@aoc_main(2022, 4, part_1, part_2)
4875
def main() -> None:
49-
assert part_1(TEST) == 2 # type:ignore[arg-type]
50-
assert part_2(TEST) == 4 # type:ignore[arg-type]
76+
solution.run(sys.argv)
5177

5278

5379
if __name__ == "__main__":

src/main/python/AoC2022_05.py

Lines changed: 78 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -5,80 +5,94 @@
55

66

77
import re
8+
import sys
89

910
from aoc import my_aocd
10-
from aoc.common import aoc_main
11+
from aoc.common import InputData
12+
from aoc.common import SolutionBase
13+
from aoc.common import aoc_samples
14+
15+
TEST = """\
16+
[D] \n\
17+
[N] [C] \n\
18+
[Z] [M] [P]\n\
19+
1 2 3 \n\
20+
21+
move 1 from 2 to 1
22+
move 3 from 1 to 3
23+
move 2 from 2 to 1
24+
move 1 from 1 to 2
25+
"""
1126

12-
Move = tuple[int, int, int]
1327

28+
Move = tuple[int, int, int]
29+
Stack = tuple[str, ...]
30+
Input = tuple[tuple[Stack, ...], tuple[Move, ...]]
31+
Output1 = str
32+
Output2 = str
33+
RE = re.compile(r"[0-9]+")
1434
CM_9000 = "CrateMover 9000"
1535
CM_9001 = "CrateMover 9001"
1636

1737

18-
def _parse(inputs: tuple[str, ...]) -> tuple[list[list[str]], list[Move]]:
19-
blocks = my_aocd.to_blocks(inputs)
20-
size = int(blocks[0][-1].replace(" ", "")[-1])
21-
stacks: list[list[str]] = [[] for _ in range(size)]
22-
for i in range(len(blocks[0]) - 2, -1, -1):
23-
line = blocks[0][i]
24-
for j in range(len(line)):
25-
if j % 4 == 1 and line[j] != " ":
26-
stacks[j // 4].append(line[j])
27-
moves = [
28-
(n1, n2 - 1, n3 - 1)
29-
for n1, n2, n3 in (
30-
(int(n) for n in re.findall(r"[0-9]+", line)) for line in blocks[1]
38+
class Solution(SolutionBase[Input, Output1, Output2]):
39+
def parse_input(self, input_data: InputData) -> Input:
40+
blocks = my_aocd.to_blocks(list(input_data))
41+
size = int(blocks[0][-1].replace(" ", "")[-1])
42+
stacks: list[list[str]] = [[] for _ in range(size)]
43+
for i in range(len(blocks[0]) - 2, -1, -1):
44+
line = blocks[0][i]
45+
for j in range(len(line)):
46+
if j % 4 == 1 and line[j] != " ":
47+
stacks[j // 4].append(line[j])
48+
moves = tuple(
49+
(n1, n2 - 1, n3 - 1)
50+
for n1, n2, n3 in (
51+
(int(n) for n in RE.findall(line)) for line in blocks[1]
52+
)
53+
)
54+
return tuple(tuple(stack) for stack in stacks), moves
55+
56+
def simulate_for(
57+
self,
58+
stacks: tuple[Stack, ...],
59+
moves: tuple[Move, ...],
60+
crate_mover: str,
61+
) -> str:
62+
new_stacks = [list(stack) for stack in stacks]
63+
for amount, _from, to in moves:
64+
tmp = []
65+
for _ in range(amount):
66+
tmp.append(new_stacks[_from][-1])
67+
new_stacks[_from] = new_stacks[_from][:-1]
68+
new_stacks[to].extend(
69+
tmp if crate_mover == CM_9000 else reversed(tmp)
70+
)
71+
return "".join(stack[-1] for stack in new_stacks)
72+
73+
def part_1(self, inputs: Input) -> Output1:
74+
stacks, moves = inputs
75+
return self.simulate_for(stacks, moves, crate_mover=CM_9000)
76+
77+
def part_2(self, inputs: Input) -> Output2:
78+
stacks, moves = inputs
79+
return self.simulate_for(stacks, moves, crate_mover=CM_9001)
80+
81+
@aoc_samples(
82+
(
83+
("part_1", TEST, "CMZ"),
84+
("part_2", TEST, "MCD"),
3185
)
32-
]
33-
return stacks, moves
34-
35-
36-
def _simulate_for(
37-
stacks: list[list[str]], moves: list[Move], crate_mover: str
38-
) -> str:
39-
for amount, _from, to in moves:
40-
tmp = []
41-
for _ in range(amount):
42-
crate = stacks[_from][-1]
43-
stacks[_from] = stacks[_from][:-1]
44-
if crate_mover == CM_9000:
45-
tmp.append(crate)
46-
else:
47-
tmp.insert(0, crate)
48-
for c in tmp:
49-
stacks[to].append(c)
50-
return "".join(stack[-1] for stack in stacks)
51-
52-
53-
def part_1(inputs: tuple[str, ...]) -> str:
54-
stacks, moves = _parse(inputs)
55-
return _simulate_for(stacks, moves, CM_9000)
56-
57-
58-
def part_2(inputs: tuple[str, ...]) -> str:
59-
stacks, moves = _parse(inputs)
60-
return _simulate_for(stacks, moves, CM_9001)
61-
62-
63-
TEST = tuple(
64-
[
65-
" [D] ",
66-
"[N] [C] ",
67-
"[Z] [M] [P]",
68-
" 1 2 3 ",
69-
"",
70-
"move 1 from 2 to 1",
71-
"move 3 from 1 to 3",
72-
"move 2 from 2 to 1",
73-
"move 1 from 1 to 2",
74-
]
75-
)
76-
77-
78-
@aoc_main(2022, 5, part_1, part_2)
86+
)
87+
def samples(self) -> None:
88+
pass
89+
90+
91+
solution = Solution(2022, 5)
92+
93+
7994
def main() -> None:
80-
assert part_1(TEST) == "CMZ"
81-
assert part_2(TEST) == "MCD"
95+
solution.run(sys.argv)
8296

8397

8498
if __name__ == "__main__":

0 commit comments

Comments
 (0)