summaryrefslogtreecommitdiffstats
path: root/day5/__init__.py
diff options
context:
space:
mode:
authorGravatar Tom van der Lee <tom@vanderlee.io>2023-12-05 10:06:58 +0100
committerGravatar Tom van der Lee <tom@vanderlee.io>2023-12-05 10:07:18 +0100
commit3b8a0f9990cbd10170fb563490f28a6bd84eea1b (patch)
tree19278220c81199ea69b201b6d84918ddca96655d /day5/__init__.py
parente688c2b674fc7ad6a964a48df379e5abd01843a7 (diff)
download2023-3b8a0f9990cbd10170fb563490f28a6bd84eea1b.tar.gz
2023-3b8a0f9990cbd10170fb563490f28a6bd84eea1b.tar.bz2
2023-3b8a0f9990cbd10170fb563490f28a6bd84eea1b.zip
Day5 part 1main
Diffstat (limited to 'day5/__init__.py')
-rw-r--r--day5/__init__.py128
1 files changed, 128 insertions, 0 deletions
diff --git a/day5/__init__.py b/day5/__init__.py
new file mode 100644
index 0000000..3189a07
--- /dev/null
+++ b/day5/__init__.py
@@ -0,0 +1,128 @@
1# -*- coding: utf-8 -*-
2from abc import ABC
3from dataclasses import dataclass
4from multiprocessing import Pool
5from typing import Iterator
6
7from aoc import BaseAssignment
8
9
10@dataclass
11class InnerMapping:
12 source: int
13 destination: int
14 length: int
15
16 def __getitem__(self, item):
17 if self.source <= item < self.source + self.length:
18 return self.destination + item - self.source
19 raise KeyError()
20
21
22class Mapping:
23 def __init__(self):
24 self.mappings = []
25
26 def __getitem__(self, item):
27 for mapping in self.mappings:
28 try:
29 return mapping[item]
30 except KeyError:
31 continue
32
33 return item
34
35 def update(self, source: int, destination: int, length: int):
36 self.mappings.append(InnerMapping(source, destination, length))
37
38
39I = tuple[list[int], list[Mapping]]
40T = int
41
42
43class Assignment(BaseAssignment, ABC):
44 @staticmethod
45 def map_seed_position(seed: int, mappings: list[Mapping]) -> int:
46 value = seed
47 for item in mappings:
48 value = item[value]
49
50 return value
51
52 def read_input(self, example=False) -> I:
53 seeds, *almanac = super().read_input(example)
54 _, seeds = seeds.split(": ")
55 seeds = [int(i) for i in seeds.split(" ")]
56
57 maps: list[Mapping] = []
58 map = None
59
60 index = 0
61 while True:
62 try:
63 line = almanac[index]
64 except IndexError:
65 break
66
67 if line == "":
68 index += 2
69
70 if map is not None:
71 maps.append(map)
72 map = None
73
74 continue
75 else:
76 index += 1
77
78 if map is None:
79 map = Mapping()
80
81 destination, source, length = line.split(" ")
82 map.update(int(source), int(destination), int(length))
83
84 return seeds, [*maps, map]
85
86
87class AssignmentOne(Assignment):
88 example_result = 35
89
90 def run(self, input: I) -> T:
91 seeds, almanac = input
92
93 values = []
94 for seed in seeds:
95 values.append(self.map_seed_position(int(seed), almanac))
96
97 return min(values)
98
99
100def run_per_seed_range(index: int, seeds: list[int], almanac: list[Mapping]):
101 seed, length = int(seeds[index]), int(seeds[index + 1])
102
103 value = None
104 for seed in range(seed, seed + length):
105 location = Assignment.map_seed_position(seed, almanac)
106 if value is None or value > location:
107 value = location
108
109 return value
110
111
112class AssignmentTwo(Assignment):
113 example_result = 46
114
115 def run(self, input: I) -> T:
116 seeds, almanac = input
117
118 value = None
119
120 with Pool(processes=len(seeds) // 2) as pool:
121 for v in pool.imap_unordered(
122 lambda index: run_per_seed_range(index, seeds, almanac),
123 range(0, len(seeds), 2),
124 ):
125 if value is None or value > v:
126 value = v
127
128 return value