day18 finished
This commit is contained in:
parent
d5edd049ce
commit
c798fa7b58
8 changed files with 2898 additions and 17 deletions
33
README.md
33
README.md
|
|
@ -8,20 +8,23 @@ I use python 3.11 without any libraries beyond the standard.
|
|||
|
||||
| Day | Time | Rank | Score | Time | Rank | Score |
|
||||
| --- | --------- | ----- | ----- | -------- | ----- | ----- |
|
||||
| 15 | 00:58:10 | 3963 | 0 | 02:26:22 | 4011 | 0 |
|
||||
| 14 | 00:58:39 | 4431 | 0 | 01:18:15 | 4620 | 0 |
|
||||
| 13 | 01:23:44 | 5522 | 0 | 01:45:59 | 5610 | 0 |
|
||||
| 12 | 01:43:14 | 6571 | 0 | 01:51:15 | 6246 | 0 |
|
||||
| 11 | 00:56:44 | 5414 | 0 | 02:42:24 | 7558 | 0 |
|
||||
| 10 | 00:38:16 | 7637 | 0 | 01:16:55 | 7961 | 0 |
|
||||
| 9 | 00:54:18 | 7719 | 0 | 01:07:37 | 4901 | 0 |
|
||||
| 8 | 00:41:51 | 7831 | 0 | 00:59:27 | 6325 | 0 |
|
||||
| 7 | 00:34:59 | 2683 | 0 | 00:45:45 | 2943 | 0 |
|
||||
| 6 | 00:14:52 | 9153 | 0 | 00:17:06 | 8413 | 0 |
|
||||
| 5 | 01:01:27 | 11570 | 0 | 01:05:20 | 10701 | 0 |
|
||||
| 4 | 00:12:09 | 5789 | 0 | 00:16:03 | 5187 | 0 |
|
||||
| 3 | 00:47:15 | 13278 | 0 | 01:07:44 | 12947 | 0 |
|
||||
| 2 | 00:21:47 | 8874 | 0 | 00:36:49 | 9949 | 0 |
|
||||
| 1 | 00:10:25 | 5460 | 0 | 00:14:20 | 5269 | 0 |
|
||||
| 18 | 00:31:49 | 3269 | 0 | 01:51:15 | 3121 | 0 |
|
||||
| 17 | 01:21:11 | 2058 | 0 | 02:42:45 | 1665 | 0 |
|
||||
| 16 | 02:44:20 | 2611 | 0 | >24h | 10509 | 0 |
|
||||
| 15 | 00:58:10 | 3963 | 0 | 02:26:22 | 4011 | 0 |
|
||||
| 14 | 00:58:39 | 4431 | 0 | 01:18:15 | 4620 | 0 |
|
||||
| 13 | 01:23:44 | 5522 | 0 | 01:45:59 | 5610 | 0 |
|
||||
| 12 | 01:43:14 | 6571 | 0 | 01:51:15 | 6246 | 0 |
|
||||
| 11 | 00:56:44 | 5414 | 0 | 02:42:24 | 7558 | 0 |
|
||||
| 10 | 00:38:16 | 7637 | 0 | 01:16:55 | 7961 | 0 |
|
||||
| 9 | 00:54:18 | 7719 | 0 | 01:07:37 | 4901 | 0 |
|
||||
| 8 | 00:41:51 | 7831 | 0 | 00:59:27 | 6325 | 0 |
|
||||
| 7 | 00:34:59 | 2683 | 0 | 00:45:45 | 2943 | 0 |
|
||||
| 6 | 00:14:52 | 9153 | 0 | 00:17:06 | 8413 | 0 |
|
||||
| 5 | 01:01:27 |11570 | 0 | 01:05:20 | 10701 | 0 |
|
||||
| 4 | 00:12:09 | 5789 | 0 | 00:16:03 | 5187 | 0 |
|
||||
| 3 | 00:47:15 |13278 | 0 | 01:07:44 | 12947 | 0 |
|
||||
| 2 | 00:21:47 | 8874 | 0 | 00:36:49 | 9949 | 0 |
|
||||
| 1 | 00:10:25 | 5460 | 0 | 00:14:20 | 5269 | 0 |
|
||||
|
||||
All code is published under the [Unlicense](https://unlicense.org/)
|
||||
13
advent/days/day18/data/example01.txt
Normal file
13
advent/days/day18/data/example01.txt
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
2,2,2
|
||||
1,2,2
|
||||
3,2,2
|
||||
2,1,2
|
||||
2,3,2
|
||||
2,2,1
|
||||
2,2,3
|
||||
2,2,4
|
||||
2,2,6
|
||||
1,2,5
|
||||
3,2,5
|
||||
2,1,5
|
||||
2,3,5
|
||||
2698
advent/days/day18/data/input.txt
Normal file
2698
advent/days/day18/data/input.txt
Normal file
File diff suppressed because it is too large
Load diff
129
advent/days/day18/solution.py
Normal file
129
advent/days/day18/solution.py
Normal file
|
|
@ -0,0 +1,129 @@
|
|||
from __future__ import annotations
|
||||
from dataclasses import dataclass
|
||||
from enum import Enum
|
||||
|
||||
from typing import Iterable, Iterator, Self
|
||||
|
||||
day_num = 18
|
||||
|
||||
|
||||
def part1(lines: Iterator[str]) -> int:
|
||||
shower = Shower.create(Position.parse_all(lines))
|
||||
return shower.faces
|
||||
|
||||
|
||||
def part2(lines: Iterator[str]) -> int:
|
||||
shower = Shower.create(Position.parse_all(lines))
|
||||
return shower.faces - shower.count_trapped_droplets()
|
||||
|
||||
|
||||
@dataclass(slots=True, frozen=True)
|
||||
class Position:
|
||||
x: int
|
||||
y: int
|
||||
z: int
|
||||
|
||||
@classmethod
|
||||
def parse(cls, line: str) -> Self:
|
||||
x, y, z = line.split(",")
|
||||
return Position(int(x), int(y), int(z))
|
||||
|
||||
@classmethod
|
||||
def parse_all(cls, lines: Iterable[str]) -> Iterator[Self]:
|
||||
return (cls.parse(line) for line in lines)
|
||||
|
||||
def neighbors(self) -> Iterator[Position]:
|
||||
yield Position(self.x + 1, self.y, self.z)
|
||||
yield Position(self.x - 1, self.y, self.z)
|
||||
yield Position(self.x, self.y + 1, self.z)
|
||||
yield Position(self.x, self.y - 1, self.z)
|
||||
yield Position(self.x, self.y, self.z + 1)
|
||||
yield Position(self.x, self.y, self.z - 1)
|
||||
|
||||
def min_max(self, mm: tuple[Position, Position] | None) -> tuple[Position, Position]:
|
||||
if mm is None:
|
||||
return self, self
|
||||
|
||||
return (Position(min(mm[0].x, self.x),
|
||||
min(mm[0].y, self.y),
|
||||
min(mm[0].z, self.z)),
|
||||
Position(max(mm[1].x, self.x),
|
||||
max(mm[1].y, self.y),
|
||||
max(mm[1].z, self.z)))
|
||||
|
||||
def is_between(self, min: Position, max: Position) -> bool:
|
||||
return (min.x <= self.x <= max.x
|
||||
and min.y <= self.y <= max.y
|
||||
and min.z <= self.z <= max.z)
|
||||
|
||||
|
||||
class DropletType(Enum):
|
||||
DROP = 1
|
||||
UNKNOWN = 2
|
||||
OUTER = 3
|
||||
|
||||
|
||||
@dataclass(slots=True, frozen=True)
|
||||
class Shower:
|
||||
droplets: dict[Position, tuple[DropletType, int]]
|
||||
faces: int
|
||||
|
||||
@classmethod
|
||||
def create(cls, positions: Iterable[Position]) -> Self:
|
||||
droplets: dict[Position, tuple[DropletType, int]] = {}
|
||||
faces = 0
|
||||
for position in positions:
|
||||
candidate = droplets.get(position)
|
||||
if candidate is not None:
|
||||
_, touching_faces = candidate
|
||||
else:
|
||||
touching_faces = 0
|
||||
|
||||
faces += 6
|
||||
for neighbor in position.neighbors():
|
||||
candidate = droplets.get(neighbor)
|
||||
if candidate is not None:
|
||||
droplet_type, neighbor_touching_faces = candidate
|
||||
if droplet_type == DropletType.DROP:
|
||||
touching_faces += 1
|
||||
faces -= 2
|
||||
droplets[neighbor] = droplet_type, neighbor_touching_faces + 1
|
||||
else:
|
||||
droplets[neighbor] = DropletType.UNKNOWN, 1
|
||||
droplets[position] = DropletType.DROP, touching_faces
|
||||
return Shower(droplets, faces)
|
||||
|
||||
def count_trapped_droplets(self) -> int:
|
||||
droplets = self.droplets.copy()
|
||||
minmax: tuple[Position, Position] | None = None
|
||||
for position in droplets.keys():
|
||||
minmax = position.min_max(minmax)
|
||||
if minmax is None:
|
||||
raise Exception("I got no data to work with")
|
||||
min_values, max_values = minmax
|
||||
|
||||
todo: list[Position] = [min_values]
|
||||
while todo:
|
||||
current = todo[0]
|
||||
todo = todo[1:]
|
||||
|
||||
candidate = droplets.get(current)
|
||||
|
||||
skip_further = True
|
||||
if candidate is None:
|
||||
droplets[current] = DropletType.OUTER, 0
|
||||
skip_further = False
|
||||
else:
|
||||
droplet_type, faces = candidate
|
||||
if droplet_type == DropletType.UNKNOWN:
|
||||
droplets[current] = DropletType.OUTER, faces
|
||||
skip_further = False
|
||||
|
||||
if not skip_further:
|
||||
for neighbor in current.neighbors():
|
||||
if neighbor.is_between(min_values, max_values):
|
||||
todo.append(neighbor)
|
||||
|
||||
return sum(touching_faces
|
||||
for droplet_type, touching_faces in droplets.values()
|
||||
if droplet_type == DropletType.UNKNOWN)
|
||||
38
advent/days/day18/test_solution.py
Normal file
38
advent/days/day18/test_solution.py
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
from advent.common import input
|
||||
|
||||
from .solution import Position, Shower, day_num, part1, part2
|
||||
|
||||
|
||||
def test_part1():
|
||||
lines = input.read_lines(day_num, 'example01.txt')
|
||||
expected = 64
|
||||
result = part1(lines)
|
||||
assert result == expected
|
||||
|
||||
|
||||
def test_part2():
|
||||
lines = input.read_lines(day_num, 'example01.txt')
|
||||
expected = 58
|
||||
result = part2(lines)
|
||||
assert result == expected
|
||||
|
||||
|
||||
def test_simple_count():
|
||||
lines = ["1,1,1", "1,1,2"]
|
||||
expected = 10
|
||||
result = Shower.create(Position.parse_all(lines))
|
||||
assert result.faces == expected
|
||||
|
||||
|
||||
def test_example_faces():
|
||||
lines = input.read_lines(day_num, 'example01.txt')
|
||||
expected = 64
|
||||
result = Shower.create(Position.parse_all(lines))
|
||||
assert result.faces == expected
|
||||
|
||||
|
||||
def test_example_trapped():
|
||||
lines = input.read_lines(day_num, 'example01.txt')
|
||||
expected = 6
|
||||
result = Shower.create(Position.parse_all(lines))
|
||||
assert result.count_trapped_droplets() == expected
|
||||
0
advent/days/day__/data/example01.txt
Normal file
0
advent/days/day__/data/example01.txt
Normal file
|
|
@ -4,14 +4,14 @@ from .solution import day_num, part1, part2
|
|||
|
||||
|
||||
def test_part1():
|
||||
lines = input.read_lines(day_num, 'test01.txt')
|
||||
lines = input.read_lines(day_num, 'example01.txt')
|
||||
expected = None
|
||||
result = part1(lines)
|
||||
assert result == expected
|
||||
|
||||
|
||||
def test_part2():
|
||||
lines = input.read_lines(day_num, 'test01.txt')
|
||||
lines = input.read_lines(day_num, 'example01.txt')
|
||||
expected = None
|
||||
result = part2(lines)
|
||||
assert result == expected
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue