Skip to content

Commit daef1e8

Browse files
committed
AoC 2017 Day 20
1 parent c66a498 commit daef1e8

File tree

4 files changed

+167
-1
lines changed

4 files changed

+167
-1
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@
7272
<!-- @BEGIN:ImplementationsTable:2017@ -->
7373
| | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 |
7474
| ---| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
75-
| python3 | [](src/main/python/AoC2017_01.py) | [](src/main/python/AoC2017_02.py) | [](src/main/python/AoC2017_03.py) | [](src/main/python/AoC2017_04.py) | [](src/main/python/AoC2017_05.py) | [](src/main/python/AoC2017_06.py) | | [](src/main/python/AoC2017_08.py) | | | | | [](src/main/python/AoC2017_13.py) | | [](src/main/python/AoC2017_15.py) | [](src/main/python/AoC2017_16.py) | | | | | | | | | |
75+
| python3 | [](src/main/python/AoC2017_01.py) | [](src/main/python/AoC2017_02.py) | [](src/main/python/AoC2017_03.py) | [](src/main/python/AoC2017_04.py) | [](src/main/python/AoC2017_05.py) | [](src/main/python/AoC2017_06.py) | | [](src/main/python/AoC2017_08.py) | | | | | [](src/main/python/AoC2017_13.py) | | [](src/main/python/AoC2017_15.py) | [](src/main/python/AoC2017_16.py) | | | | [](src/main/python/AoC2017_20.py) | | | | | |
7676
| java | [](src/main/java/AoC2017_01.java) | [](src/main/java/AoC2017_02.java) | [](src/main/java/AoC2017_03.java) | [](src/main/java/AoC2017_04.java) | [](src/main/java/AoC2017_05.java) | [](src/main/java/AoC2017_06.java) | [](src/main/java/AoC2017_07.java) | [](src/main/java/AoC2017_08.java) | [](src/main/java/AoC2017_09.java) | [](src/main/java/AoC2017_10.java) | | [](src/main/java/AoC2017_12.java) | [](src/main/java/AoC2017_13.java) | | [](src/main/java/AoC2017_15.java) | [](src/main/java/AoC2017_16.java) | [](src/main/java/AoC2017_17.java) | [](src/main/java/AoC2017_18.java) | [](src/main/java/AoC2017_19.java) | [](src/main/java/AoC2017_20.java) | | | | | |
7777
| bash | [](src/main/bash/AoC2017_01.sh) | [](src/main/bash/AoC2017_02.sh) | | [](src/main/bash/AoC2017_04.sh) | [](src/main/bash/AoC2017_05.sh) | | | | | | | | | | | | | | | | | | | | |
7878
| c++ | [](src/main/cpp/2017/01/AoC2017_01.cpp) | [](src/main/cpp/2017/02/AoC2017_02.cpp) | [](src/main/cpp/2017/03/AoC2017_03.cpp) | [](src/main/cpp/2017/04/AoC2017_04.cpp) | [](src/main/cpp/2017/05/AoC2017_05.cpp) | | | | | | | | [](src/main/cpp/2017/13/AoC2017_13.cpp) | | | | | [](src/main/cpp/2017/18/AoC2017_18.cpp) | | | | | | | |

src/main/python/AoC2017_20.py

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
#! /usr/bin/env python3
2+
#
3+
# Advent of Code 2017 Day 20
4+
#
5+
from __future__ import annotations
6+
import re
7+
from collections import defaultdict
8+
from typing import NamedTuple
9+
from aoc import my_aocd
10+
from aoc.geometry3d import Position3D, Vector3D
11+
import aocd
12+
13+
14+
TICKS = 400
15+
16+
17+
class Particle(NamedTuple):
18+
number: int
19+
position: Position3D
20+
velocity: Position3D
21+
acceleration: Position3D
22+
23+
@classmethod
24+
def create(cls, number: int, nums: list[int]) -> Particle:
25+
assert len(nums) == 9
26+
return Particle(
27+
number,
28+
Position3D(nums[0], nums[1], nums[2]),
29+
Position3D(nums[3], nums[4], nums[5]),
30+
Position3D(nums[6], nums[7], nums[8]),
31+
)
32+
33+
def next(self) -> Particle:
34+
v2 = self.velocity.translate(Vector3D.to_from(self.acceleration))
35+
p2 = self.position.translate(Vector3D.to_from(v2))
36+
return Particle(self.number, p2, v2, self.acceleration)
37+
38+
def distance(self) -> int:
39+
return self.position.manhattan_distance_to_origin()
40+
41+
42+
def _parse(inputs: tuple[str]) -> list[Particle]:
43+
return [
44+
Particle.create(i, list(map(int, re.findall(r"[-0-9]+", line))))
45+
for i, line in enumerate(inputs)
46+
]
47+
48+
49+
def part_1(inputs: tuple[str]) -> int:
50+
particles = _parse(inputs)
51+
for _ in range(TICKS):
52+
particles = [p.next() for p in particles]
53+
particles.sort(key=lambda p: p.distance())
54+
return particles[0].number
55+
56+
57+
def part_2(inputs: tuple[str]) -> int:
58+
particles = _parse(inputs)
59+
for _ in range(TICKS):
60+
m = defaultdict(list)
61+
[m[pn.position].append(pn) for pn in [p.next() for p in particles]]
62+
particles = [p for mv in m.values() for p in mv if len(mv) == 1]
63+
return len(m)
64+
65+
66+
TEST1 = """\
67+
p=<3,0,0>, v=<2,0,0>, a=<-1,0,0>
68+
p=<4,0,0>, v=<0,0,0>, a=<-2,0,0>
69+
""".splitlines()
70+
TEST2 = """\
71+
p=<-6,0,0>, v=< 3,0,0>, a=<0,0,0>
72+
p=<-4,0,0>, v=< 2,0,0>, a=<0,0,0>
73+
p=<-2,0,0>, v=< 1,0,0>, a=<0,0,0>
74+
p=< 3,0,0>, v=<-1,0,0>, a=<0,0,0>
75+
""".splitlines()
76+
77+
78+
def main() -> None:
79+
puzzle = aocd.models.Puzzle(2017, 20)
80+
my_aocd.print_header(puzzle.year, puzzle.day)
81+
82+
assert part_1(TEST1) == 0
83+
assert part_2(TEST2) == 1
84+
85+
inputs = my_aocd.get_input(2017, 20, 1_000)
86+
result1 = part_1(inputs)
87+
print(f"Part 1: {result1}")
88+
result2 = part_2(inputs)
89+
print(f"Part 2: {result2}")
90+
my_aocd.check_results(puzzle, result1, result2)
91+
92+
93+
if __name__ == "__main__":
94+
main()

src/main/python/aoc/geometry3d.py

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
from __future__ import annotations
2+
from typing import NamedTuple
3+
4+
5+
class Point3D(NamedTuple):
6+
x: int
7+
y: int
8+
z: int
9+
10+
11+
class Position3D(Point3D):
12+
@classmethod
13+
def copy(cls, position: Position3D) -> Position3D:
14+
return Position3D.of(position.x, position.y, position.z)
15+
16+
@classmethod
17+
def of(cls, x: int, y: int, z: int) -> Position3D:
18+
return Position3D(x, y, z)
19+
20+
def translate(self, vector: Vector3D, amplitude: int = 1) -> Position3D:
21+
return Position3D.of(
22+
self.x + vector.x * amplitude,
23+
self.y + vector.y * amplitude,
24+
self.z + vector.z * amplitude,
25+
)
26+
27+
def manhattan_distance(self, other: Position3D) -> int:
28+
return (
29+
abs(self.x - other.x)
30+
+ abs(self.y - other.y)
31+
+ abs(self.z - other.z)
32+
)
33+
34+
def manhattan_distance_to_origin(self) -> int:
35+
return self.manhattan_distance(Position3D.of(0, 0, 0))
36+
37+
38+
class Vector3D(Point3D):
39+
@classmethod
40+
def of(cls, x: int, y: int, z: int) -> Vector3D:
41+
return Vector3D(x, y, z)
42+
43+
@classmethod
44+
def to_from(
45+
cls, to: Position3D, from_: Position3D = Position3D.of(0, 0, 0)
46+
) -> Vector3D:
47+
return Vector3D(to.x - from_.x, to.y - from_.y, to.z - from_.z)

src/test/python/test_geometry3d.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
#! /usr/bin/env python3
2+
#
3+
4+
import unittest
5+
from aoc.geometry3d import Position3D
6+
7+
8+
class TestGeometry3D(unittest.TestCase):
9+
def test_manhattan_distance(self):
10+
self.assertEqual(
11+
Position3D.of(2, 2, 2).manhattan_distance(Position3D.of(0, 0, 0)),
12+
6,
13+
)
14+
self.assertEqual(
15+
Position3D.of(2, 2, 2).manhattan_distance(Position3D.of(1, 1, 1)),
16+
3,
17+
)
18+
19+
def test_manhattan_distance_to_origin(self):
20+
self.assertEqual(
21+
Position3D.of(2, 2, 2).manhattan_distance_to_origin(), 6
22+
)
23+
self.assertEqual(
24+
Position3D.of(5, -3, 2).manhattan_distance_to_origin(), 10
25+
)

0 commit comments

Comments
 (0)