Skip to content

Commit af748b0

Browse files
committed
Refactor to SolutionBase
1 parent 068fabd commit af748b0

File tree

13 files changed

+790
-738
lines changed

13 files changed

+790
-738
lines changed

src/main/python/AoC2015_05.py

Lines changed: 62 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -4,60 +4,77 @@
44
#
55

66
import re
7-
from aoc import my_aocd
7+
import sys
88

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

10-
def _count_matches(regexp: str, string: str) -> int:
11-
return len(re.findall(regexp, string))
13+
Input = list[str]
14+
Output1 = int
15+
Output2 = int
1216

1317

14-
def part_1(inputs: tuple[str]) -> int:
15-
return sum(1 for input_ in inputs
16-
if _count_matches(r"(a|e|i|o|u)", input_) >= 3
17-
and _count_matches(r"([a-z])\1", input_) >= 1
18-
and _count_matches(r"(ab|cd|pq|xy)", input_) == 0
19-
)
18+
TEST1 = "ugknbfddgicrmopn"
19+
TEST2 = "aaa"
20+
TEST3 = "jchzalrnumimnmhp"
21+
TEST4 = "haegwjzuvuyypxyu"
22+
TEST5 = "dvszwmarrgswjxmb"
23+
TEST6 = "qjhvhtzxzqqjkmpb"
24+
TEST7 = "xxyxx"
25+
TEST8 = "uurcxstgmygtbstg"
26+
TEST9 = "ieodomkazucvgmuy"
27+
TEST10 = "xyxy"
2028

2129

22-
def part_2(inputs: tuple[str]) -> int:
23-
return sum(1 for input_ in inputs
24-
if _count_matches(r"([a-z]{2})[a-z]*\1", input_) >= 1
25-
and _count_matches(r"([a-z])[a-z]\1", input_) >= 1
26-
)
30+
class Solution(SolutionBase[Input, Output1, Output2]):
31+
def parse_input(self, input_data: InputData) -> Input:
32+
return list(input_data)
2733

34+
def count_matches(self, regexp: str, string: str) -> int:
35+
return len(re.findall(regexp, string))
2836

29-
TEST1 = "ugknbfddgicrmopn".splitlines()
30-
TEST2 = "aaa".splitlines()
31-
TEST3 = "jchzalrnumimnmhp".splitlines()
32-
TEST4 = "haegwjzuvuyypxyu".splitlines()
33-
TEST5 = "dvszwmarrgswjxmb".splitlines()
34-
TEST6 = "qjhvhtzxzqqjkmpb".splitlines()
35-
TEST7 = "xxyxx".splitlines()
36-
TEST8 = "uurcxstgmygtbstg".splitlines()
37-
TEST9 = "ieodomkazucvgmuy".splitlines()
38-
TEST10 = "xyxy".splitlines()
37+
def part_1(self, inputs: Input) -> Output1:
38+
return sum(
39+
1
40+
for line in inputs
41+
if self.count_matches(r"(a|e|i|o|u)", line) >= 3
42+
and self.count_matches(r"([a-z])\1", line) >= 1
43+
and self.count_matches(r"(ab|cd|pq|xy)", line) == 0
44+
)
45+
46+
def part_2(self, inputs: Input) -> Output2:
47+
return sum(
48+
1
49+
for line in inputs
50+
if self.count_matches(r"([a-z]{2})[a-z]*\1", line) >= 1
51+
and self.count_matches(r"([a-z])[a-z]\1", line) >= 1
52+
)
53+
54+
@aoc_samples(
55+
(
56+
("part_1", TEST1, 1),
57+
("part_1", TEST2, 1),
58+
("part_1", TEST3, 0),
59+
("part_1", TEST4, 0),
60+
("part_1", TEST5, 0),
61+
("part_2", TEST6, 1),
62+
("part_2", TEST7, 1),
63+
("part_2", TEST8, 0),
64+
("part_2", TEST9, 0),
65+
("part_2", TEST10, 1),
66+
)
67+
)
68+
def samples(self) -> None:
69+
pass
70+
71+
72+
solution = Solution(2015, 5)
3973

4074

4175
def main() -> None:
42-
my_aocd.print_header(2015, 5)
43-
44-
assert part_1(TEST1) == 1
45-
assert part_1(TEST2) == 1
46-
assert part_1(TEST3) == 0
47-
assert part_1(TEST4) == 0
48-
assert part_1(TEST5) == 0
49-
assert part_2(TEST6) == 1
50-
assert part_2(TEST7) == 1
51-
assert part_2(TEST8) == 0
52-
assert part_2(TEST9) == 0
53-
assert part_2(TEST10) == 1
54-
55-
inputs = my_aocd.get_input(2015, 5, 1000)
56-
result1 = part_1(inputs)
57-
print(f"Part 1: {result1}")
58-
result2 = part_2(inputs)
59-
print(f"Part 2: {result2}")
60-
61-
62-
if __name__ == '__main__':
76+
solution.run(sys.argv)
77+
78+
79+
if __name__ == "__main__":
6380
main()

src/main/python/AoC2015_11.py

Lines changed: 56 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -4,78 +4,81 @@
44
#
55

66
import re
7+
import sys
78

8-
import aocd
9-
10-
from aoc import my_aocd
9+
from aoc.common import InputData
10+
from aoc.common import SolutionBase
11+
from aoc.common import aoc_samples
1112

1213
ALPH = "abcdefghijklmnopqrstuvwxyz"
1314
NEXT = "bcdefghjjkmmnppqrstuvwxyza"
1415
CONFUSING_LETTERS = {"i", "o", "l"}
1516
RE = re.compile(r"([a-z])\1")
1617

18+
Input = str
19+
Output1 = str
20+
Output2 = str
1721

18-
def _is_ok(password: str) -> bool:
19-
if len({p for p in RE.findall(password)}) < 2:
20-
return False
21-
trio = False
22-
for i in range(len(password)):
23-
if password[i] in CONFUSING_LETTERS:
24-
return False
25-
trio = (
26-
trio
27-
or i < len(password) - 3
28-
and ord(password[i]) == ord(password[i + 1]) - 1
29-
and ord(password[i + 1]) == ord(password[i + 2]) - 1
30-
)
31-
return trio
3222

23+
class Solution(SolutionBase[Input, Output1, Output2]):
24+
def parse_input(self, input_data: InputData) -> Input:
25+
return next(iter(input_data))
3326

34-
def _get_next(password: str) -> str:
35-
def _increment(password: str, i: int) -> str:
36-
password = (
37-
password[:i]
38-
+ NEXT[ALPH.index(password[i])]
39-
+ password[i + 1 :] # noqa E203
40-
)
41-
if password[i] == "a":
42-
password = _increment(password, i - 1)
27+
def is_ok(self, password: str) -> bool:
28+
if len(set(RE.findall(password))) < 2:
29+
return False
30+
trio = False
31+
for i in range(len(password)):
32+
if password[i] in CONFUSING_LETTERS:
33+
return False
34+
trio = trio or (
35+
i < len(password) - 3
36+
and ord(password[i]) == ord(password[i + 1]) - 1
37+
and ord(password[i + 1]) == ord(password[i + 2]) - 1
38+
)
39+
return trio
40+
41+
def get_next(self, password: str) -> str:
42+
def increment(password: str, i: int) -> str:
43+
password = (
44+
password[:i]
45+
+ NEXT[ALPH.index(password[i])]
46+
+ password[i + 1 :]
47+
)
48+
if password[i] == "a":
49+
password = increment(password, i - 1)
50+
return password
51+
52+
while True:
53+
password = increment(password, len(password) - 1)
54+
if self.is_ok(password):
55+
break
4356
return password
4457

45-
while True:
46-
password = _increment(password, len(password) - 1)
47-
if _is_ok(password):
48-
break
49-
return password
58+
def part_1(self, password: Input) -> Output1:
59+
return self.get_next(password)
5060

61+
def part_2(self, password: Input) -> Output2:
62+
return self.get_next(self.get_next(password))
5163

52-
def part_1(inputs: tuple[str]) -> str:
53-
assert len(inputs) == 1
54-
return _get_next(inputs[0])
64+
@aoc_samples(
65+
(
66+
("part_1", "abcdefgh", "abcdffaa"),
67+
("part_1", "ghijklmn", "ghjaabcc"),
68+
)
69+
)
70+
def samples(self) -> None:
71+
assert not self.is_ok("abci")
72+
assert not self.is_ok("hijklmmn")
73+
assert not self.is_ok("abbceffg")
74+
assert not self.is_ok("abbcegjk")
5575

5676

57-
def part_2(inputs: tuple[str]) -> str:
58-
assert len(inputs) == 1
59-
return _get_next(_get_next(inputs[0]))
77+
solution = Solution(2015, 11)
6078

6179

6280
def main() -> None:
63-
puzzle = aocd.models.Puzzle(2015, 11)
64-
my_aocd.print_header(puzzle.year, puzzle.day)
65-
66-
assert not _is_ok("abci")
67-
assert not _is_ok("hijklmmn")
68-
assert not _is_ok("abbceffg")
69-
assert not _is_ok("abbcegjk")
70-
assert _get_next("abcdefgh") == "abcdffaa"
71-
assert _get_next("ghijklmn") == "ghjaabcc"
72-
73-
inputs = my_aocd.get_input_data(puzzle, 1)
74-
result1 = part_1(inputs)
75-
print(f"Part 1: {result1}")
76-
result2 = part_2(inputs)
77-
print(f"Part 2: {result2}")
78-
my_aocd.check_results(puzzle, result1, result2)
81+
solution.run(sys.argv)
7982

8083

8184
if __name__ == "__main__":

src/main/python/AoC2015_12.py

Lines changed: 70 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -4,69 +4,78 @@
44
#
55

66
import json
7-
from aoc import my_aocd
7+
import sys
8+
from typing import Any
9+
10+
from aoc.common import InputData
11+
from aoc.common import SolutionBase
12+
from aoc.common import aoc_samples
13+
14+
TEST1 = "[1,2,3]"
15+
TEST2 = '{"a":2,"b":4}'
16+
TEST3 = "[[[3]]]"
17+
TEST4 = '{"a":{"b":4},"c":-1}'
18+
TEST5 = '{"a":[-1,1]}'
19+
TEST6 = '[-1,{"a":1}]'
20+
TEST7 = "[]"
21+
TEST8 = "{}"
22+
TEST9 = '[1,{"c":"red","b":2},3]'
23+
TEST10 = '{"d":"red","e":[1,2,3,4],"f":5}'
24+
TEST11 = '[1,"red",5]'
25+
26+
Input = str
27+
Output1 = int
28+
Output2 = int
29+
30+
31+
class Solution(SolutionBase[Input, Output1, Output2]):
32+
def parse_input(self, input_data: InputData) -> Input:
33+
return next(iter(input_data))
34+
35+
def add_all_numbers(self, string: str) -> int:
36+
numbers = "".join(
37+
c for c in string if c in {",", "-"} or c.isnumeric()
38+
)
39+
return sum(int(n) for n in numbers.split(",") if len(n) > 0)
40+
41+
def part_1(self, json_str: Input) -> Output1:
42+
return self.add_all_numbers(json_str)
43+
44+
def part_2(self, json_str: Input) -> Output2:
45+
def object_hook(obj: dict[Any, Any]) -> Any: # noqa: ANN401
46+
if "red" in obj.values():
47+
return None
48+
return obj
49+
50+
string = json.dumps(json.loads(json_str, object_hook=object_hook))
51+
return self.add_all_numbers(string)
52+
53+
@aoc_samples(
54+
(
55+
("part_1", TEST1, 6),
56+
("part_1", TEST2, 6),
57+
("part_1", TEST3, 3),
58+
("part_1", TEST4, 3),
59+
("part_1", TEST5, 0),
60+
("part_1", TEST6, 0),
61+
("part_1", TEST7, 0),
62+
("part_2", TEST8, 0),
63+
("part_2", TEST1, 6),
64+
("part_2", TEST9, 4),
65+
("part_2", TEST10, 0),
66+
("part_2", TEST11, 6),
67+
)
68+
)
69+
def samples(self) -> None:
70+
pass
71+
72+
73+
solution = Solution(2015, 12)
874

975

10-
def _add_all_numbers(string: str) -> int:
11-
numbers = ""
12-
for c in string:
13-
if c in {',', '-'} or c.isnumeric():
14-
numbers += c
15-
return sum([int(_) for _ in numbers.split(',') if len(_) > 0])
16-
17-
18-
def part_1(inputs: tuple[str]) -> int:
19-
assert len(inputs) == 1
20-
return _add_all_numbers(inputs[0])
21-
22-
23-
def part_2(inputs: tuple[str]) -> int:
24-
def object_hook(obj: dict) -> dict:
25-
if "red" in obj.values():
26-
return None
27-
return obj
28-
29-
assert len(inputs) == 1
30-
string = json.dumps(
31-
json.loads(inputs[0], object_hook=object_hook))
32-
return _add_all_numbers(string)
33-
34-
35-
TEST1 = "[1,2,3]".splitlines()
36-
TEST2 = "{\"a\":2,\"b\":4}".splitlines()
37-
TEST3 = "[[[3]]]".splitlines()
38-
TEST4 = "{\"a\":{\"b\":4},\"c\":-1}".splitlines()
39-
TEST5 = "{\"a\":[-1,1]}".splitlines()
40-
TEST6 = "[-1,{\"a\":1}]".splitlines()
41-
TEST7 = "[]".splitlines()
42-
TEST8 = "{}".splitlines()
43-
TEST9 = "[1,{\"c\":\"red\",\"b\":2},3]".splitlines()
44-
TEST10 = "{\"d\":\"red\",\"e\":[1,2,3,4],\"f\":5}".splitlines()
45-
TEST11 = "[1,\"red\",5]".splitlines()
76+
def main() -> None:
77+
solution.run(sys.argv)
4678

4779

48-
def main() -> None:
49-
my_aocd.print_header(2015, 12)
50-
51-
assert part_1(TEST1) == 6
52-
assert part_1(TEST2) == 6
53-
assert part_1(TEST3) == 3
54-
assert part_1(TEST4) == 3
55-
assert part_1(TEST5) == 0
56-
assert part_1(TEST6) == 0
57-
assert part_1(TEST7) == 0
58-
assert part_1(TEST8) == 0
59-
assert part_2(TEST1) == 6
60-
assert part_2(TEST9) == 4
61-
assert part_2(TEST10) == 0
62-
assert part_2(TEST11) == 6
63-
64-
inputs = my_aocd.get_input(2015, 12, 1)
65-
result1 = part_1(inputs)
66-
print(f"Part 1: {result1}")
67-
result2 = part_2(inputs)
68-
print(f"Part 2: {result2}")
69-
70-
71-
if __name__ == '__main__':
80+
if __name__ == "__main__":
7281
main()

0 commit comments

Comments
 (0)