summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--aoc/__init__.py12
-rw-r--r--day8/test_init.py4
-rw-r--r--day9/__init__.py49
-rw-r--r--day9/example_part_1.txt (renamed from day9/example.txt)0
-rw-r--r--day9/example_part_2.txt8
-rw-r--r--day9/test_init.py128
6 files changed, 169 insertions, 32 deletions
diff --git a/aoc/__init__.py b/aoc/__init__.py
index 3662e3a..8368f7b 100644
--- a/aoc/__init__.py
+++ b/aoc/__init__.py
@@ -16,10 +16,20 @@ class BaseAssignment(ABC):
16 def parse_item(self, item: str) -> Any: 16 def parse_item(self, item: str) -> Any:
17 return item 17 return item
18 18
19 @property
20 def part(self):
21 return 1 if self.__class__.__name__.endswith("One") else 2
22
19 def read_input(self, example=False) -> Generator: 23 def read_input(self, example=False) -> Generator:
20 file = f"{self.path}/input.txt" 24 file = f"{self.path}/input.txt"
25
21 if example or not os.path.isfile(file): 26 if example or not os.path.isfile(file):
22 file = f"{self.path}/example.txt" 27 for file in [
28 f"{self.path}/example_part_{self.part}.txt",
29 f"{self.path}/example.txt",
30 ]:
31 if os.path.exists(file):
32 break
23 33
24 with open(file, "r") as input_file: 34 with open(file, "r") as input_file:
25 for line in input_file.readlines(): 35 for line in input_file.readlines():
diff --git a/day8/test_init.py b/day8/test_init.py
index 6fd0e2b..16be2ec 100644
--- a/day8/test_init.py
+++ b/day8/test_init.py
@@ -3,5 +3,5 @@ from day8 import AssignmentTwo
3 3
4 4
5def test_calculate_1d_scenic_score(): 5def test_calculate_1d_scenic_score():
6 assert 1 * 2 == AssignmentTwo.calculate_1d_scenic_score([3, 5, 3, 5, 3], 1) 6 assert AssignmentTwo.calculate_1d_scenic_score([3, 5, 3, 5, 3], 1) == 1 * 2
7 assert 1 * 2 == AssignmentTwo.calculate_1d_scenic_score([2, 5, 5, 1, 2], 2) 7 assert AssignmentTwo.calculate_1d_scenic_score([2, 5, 5, 1, 2], 2) == 1 * 2
diff --git a/day9/__init__.py b/day9/__init__.py
index f198deb..4846468 100644
--- a/day9/__init__.py
+++ b/day9/__init__.py
@@ -34,6 +34,8 @@ class Coordinate:
34 34
35 35
36class Assignment(BaseAssignment, ABC): 36class Assignment(BaseAssignment, ABC):
37 rope_length = NotImplemented
38
37 def parse_line(self, item: str) -> Tuple[str, int]: 39 def parse_line(self, item: str) -> Tuple[str, int]:
38 direction, amount = item.split(" ") 40 direction, amount = item.split(" ")
39 return direction, int(amount) 41 return direction, int(amount)
@@ -54,7 +56,7 @@ class Assignment(BaseAssignment, ABC):
54 head.y -= 1 56 head.y -= 1
55 57
56 @staticmethod 58 @staticmethod
57 def next_tail(head: Coordinate, tail: Coordinate): 59 def next_knot(head: Coordinate, tail: Coordinate):
58 delta = head - tail 60 delta = head - tail
59 61
60 if abs(delta.x) > 1 and delta.y == 0: 62 if abs(delta.x) > 1 and delta.y == 0:
@@ -62,37 +64,43 @@ class Assignment(BaseAssignment, ABC):
62 if abs(delta.x) > 1 and abs(delta.y) == 1: 64 if abs(delta.x) > 1 and abs(delta.y) == 1:
63 tail.x += delta.x - delta.polarity_x 65 tail.x += delta.x - delta.polarity_x
64 tail.y += delta.y 66 tail.y += delta.y
65 if abs(delta.y > 1) and delta.x == 0: 67 if abs(delta.y) > 1 and delta.x == 0:
66 tail.y += delta.y - delta.polarity_y 68 tail.y += delta.y - delta.polarity_y
67 if abs(delta.y) > 1 and abs(delta.x) == 1: 69 if abs(delta.y) > 1 and abs(delta.x) == 1:
68 tail.y += delta.y - delta.polarity_y 70 tail.y += delta.y - delta.polarity_y
69 tail.x += delta.x 71 tail.x += delta.x
70 72
71 def tail_positions(self, input: Iterator[str]) -> Iterator[Coordinate]: 73 def tail_positions(self, input: Iterator[str], length: int) -> Iterator[Coordinate]:
72 head = Coordinate(0, 0) 74 knots = [Coordinate(0, 0) for _ in range(length)]
73 tail = Coordinate(0, 0)
74 75
75 for line in input: 76 for line in input:
76 direction, amount = self.parse_line(line) 77 direction, amount = self.parse_line(line)
77 78
78 for _ in range(amount): 79 for _ in range(amount):
79 self.next_head(head, direction) 80 for index in range(length):
80 self.next_tail(head, tail) 81 if index == 0:
81 yield tail 82 self.next_head(knots[index], direction)
83 continue
84
85 self.next_knot(knots[index - 1], knots[index])
86
87 yield knots[length - 1]
82 88
83 def unique_tail_positions(self, input: Iterator[str]) -> Set[Coordinate]: 89 def unique_tail_positions(
90 self, input: Iterator[str], length: int
91 ) -> Set[Coordinate]:
84 unique_tail_positions = set() 92 unique_tail_positions = set()
85 93
86 for position in self.tail_positions(input): 94 for position in self.tail_positions(input, length):
87 unique_tail_positions.add(copy(position)) 95 unique_tail_positions.add(copy(position))
88 96
89 return unique_tail_positions 97 return unique_tail_positions
90 98
91 def visualize(self, width: int, height: int, positions: Set[Coordinate]): 99 def visualize(self, width: range, height: range, positions: Set[Coordinate]):
92 rows = [] 100 rows = []
93 for y in range(height): 101 for y in height:
94 items = [] 102 items = []
95 for x in range(width): 103 for x in width:
96 if x == 0 and y == 0: 104 if x == 0 and y == 0:
97 items.append("s") 105 items.append("s")
98 elif Coordinate(x, y) in positions: 106 elif Coordinate(x, y) in positions:
@@ -103,17 +111,16 @@ class Assignment(BaseAssignment, ABC):
103 111
104 return "\n".join(["".join(row) for row in reversed(rows)]) 112 return "\n".join(["".join(row) for row in reversed(rows)])
105 113
114 def run(self, input: Iterator) -> int:
115 unique_positions = self.unique_tail_positions(input, length=self.rope_length)
116 return len(unique_positions)
117
106 118
107class AssignmentOne(Assignment): 119class AssignmentOne(Assignment):
108 example_result = 13 120 example_result = 13
109 121 rope_length = 2
110 def run(self, input: Iterator) -> int:
111 unique_positions = self.unique_tail_positions(input)
112 print()
113 print(self.visualize(6, 5, unique_positions))
114
115 return len(unique_positions)
116 122
117 123
118class AssignmentTwo(Assignment): 124class AssignmentTwo(Assignment):
119 pass 125 example_result = 36
126 rope_length = 10
diff --git a/day9/example.txt b/day9/example_part_1.txt
index 9874df2..9874df2 100644
--- a/day9/example.txt
+++ b/day9/example_part_1.txt
diff --git a/day9/example_part_2.txt b/day9/example_part_2.txt
new file mode 100644
index 0000000..60bd43b
--- /dev/null
+++ b/day9/example_part_2.txt
@@ -0,0 +1,8 @@
1R 5
2U 8
3L 8
4D 3
5R 17
6D 10
7L 25
8U 20
diff --git a/day9/test_init.py b/day9/test_init.py
index b36eed6..eb889c7 100644
--- a/day9/test_init.py
+++ b/day9/test_init.py
@@ -4,11 +4,123 @@ import os.path
4import day9 4import day9
5 5
6 6
7def test_output_visualization(): 7class TestAssignment:
8 expected = "..##..\n" "...##.\n" ".####.\n" "....#.\n" "s###.." 8 def test_right(self):
9 9 expected = "\n".join(
10 assignment = day9.AssignmentOne(path=os.path.dirname(day9.__file__)) 10 [
11 unique_positions = assignment.unique_tail_positions( 11 "s###..",
12 input=assignment.read_input(True) 12 ]
13 ) 13 )
14 assert expected == assignment.visualize(6, 5, unique_positions) 14
15 input = ["R 4"]
16
17 assignment = day9.AssignmentOne(path=os.path.dirname(day9.__file__))
18 unique_positions = assignment.unique_tail_positions(input=input, length=2)
19
20 assert assignment.visualize(range(6), range(1), unique_positions) == expected
21
22 def test_left(self):
23 expected = "\n".join(
24 [
25 "..###s",
26 ]
27 )
28
29 input = ["L 4"]
30
31 assignment = day9.AssignmentOne(path=os.path.dirname(day9.__file__))
32 unique_positions = assignment.unique_tail_positions(input=input, length=2)
33
34 assert (
35 assignment.visualize(range(-5, 1), range(1), unique_positions) == expected
36 )
37
38 def test_up(self):
39 expected = "\n".join(
40 [
41 ".",
42 ".",
43 "#",
44 "#",
45 "#",
46 "s",
47 ]
48 )
49
50 input = ["U 4"]
51
52 assignment = day9.AssignmentOne(path=os.path.dirname(day9.__file__))
53 unique_positions = assignment.unique_tail_positions(input=input, length=2)
54
55 assert assignment.visualize(range(1), range(6), unique_positions) == expected
56
57 def test_down(self):
58 expected = "\n".join(
59 [
60 "s",
61 "#",
62 "#",
63 "#",
64 ".",