generated from public/aoc_template
Compare commits
3 Commits
7595fa8562
...
e4aff38d54
| Author | SHA1 | Date | |
|---|---|---|---|
| e4aff38d54 | |||
| 2d5553e1c3 | |||
| 55d885f416 |
234
day12.py
234
day12.py
@ -1,212 +1,72 @@
|
||||
from tools.aoc import AOCDay
|
||||
from tools.int_seq import triangular
|
||||
from tools.tools import list_combinations_of_sum
|
||||
from typing import Any
|
||||
|
||||
|
||||
def _get_placement_options(open_groups: list[tuple[int, str]], groups: list[int], not_found: list[tuple[int, int, int]]) -> list:
|
||||
to_determine = []
|
||||
min_group_len = min(groups[x[2]] for x in not_found)
|
||||
for x, group_str in open_groups.copy():
|
||||
if len(group_str) < min_group_len:
|
||||
open_groups.remove((x, group_str))
|
||||
def get_min_len(groups: tuple[int, ...]) -> int:
|
||||
return sum(groups) + len(groups) - 1
|
||||
|
||||
assert not not_found or open_groups, f"No open_groups left to fill with {not_found}"
|
||||
# open_groups = list(sorted(open_groups))
|
||||
# open_groups should always be sorted by default
|
||||
for possible_combination in list_combinations_of_sum(len(not_found), len(open_groups)):
|
||||
# try to put possible_combination[x] in open_groups[x]
|
||||
nf_idx = 0
|
||||
for i, to_place in enumerate(possible_combination):
|
||||
# try to place to_place not_founds in open_groups[i]
|
||||
pass
|
||||
|
||||
for start, group_str in open_groups:
|
||||
print("look in", group_str, "for", [groups[x[2]] for x in not_found])
|
||||
assigned = []
|
||||
for min_x, max_x, idx_need in list(not_found):
|
||||
print("check", min_x, "<", start, "<", max_x, idx_need, groups[idx_need])
|
||||
if groups[idx_need] > len(group_str):
|
||||
continue
|
||||
elif (not assigned or sum(assigned) + len(assigned) + groups[idx_need] <= len(group_str)) and (
|
||||
min_x <= start <= max_x
|
||||
):
|
||||
assigned.append(groups[idx_need])
|
||||
not_found = not_found[1:]
|
||||
else:
|
||||
break
|
||||
print("append", assigned)
|
||||
if assigned:
|
||||
to_determine.append((group_str, assigned))
|
||||
|
||||
return to_determine
|
||||
|
||||
|
||||
def _get_arrangements(springs: str, groups: list[int]):
|
||||
springs, not_found = clean_springs(springs, groups)
|
||||
print("not_found", not_found)
|
||||
open_groups = get_open_groups(springs)
|
||||
to_determine = get_placement_options(open_groups, groups, not_found)
|
||||
arrangements = 1
|
||||
for group_str, values in to_determine:
|
||||
if len(values) == 1 and len(group_str) == values[0]:
|
||||
continue
|
||||
elif sum(values) + len(values) - 1 == len(group_str):
|
||||
continue
|
||||
elif len(values) == 1:
|
||||
arrangements *= len(group_str) - values[0] + 1
|
||||
else:
|
||||
arrangements *= triangular(len(group_str) - (sum(values) + len(values) - 1) + 1)
|
||||
# print(
|
||||
# group_str,
|
||||
# "with",
|
||||
# values,
|
||||
# "has multiple options:",
|
||||
# triangular(len(group_str) - (sum(values) + len(values) - 1) + 1),
|
||||
# )
|
||||
|
||||
if arrangements < 0:
|
||||
print(springs, groups, to_determine)
|
||||
exit(1)
|
||||
|
||||
return arrangements
|
||||
|
||||
|
||||
def _clean_springs(springs: str, groups: list[int]) -> (str, list[tuple[int, int, int]]):
|
||||
hashes = []
|
||||
start = None
|
||||
c_size = 0
|
||||
for i, c in enumerate(springs):
|
||||
if c == "#":
|
||||
if start is None:
|
||||
start = i
|
||||
c_size += 1
|
||||
else:
|
||||
if start is not None:
|
||||
hashes.append((start, c_size))
|
||||
start = None
|
||||
c_size = 0
|
||||
|
||||
if start is not None:
|
||||
hashes.append((start, c_size))
|
||||
|
||||
print("cleaning", springs, "with hashes", hashes, "and groups", groups)
|
||||
found = {}
|
||||
for i, s in enumerate(groups):
|
||||
for h in hashes:
|
||||
if h[0] >= sum(groups[:i]) + i and h[0] <= len(springs) - (sum(groups[i:]) + len(groups) - i) + 1:
|
||||
# print(h[0], ">", sum(groups[:i]) + i)
|
||||
# print(h[0], "<", len(springs) - (sum(groups[i:]) + len(groups) - i) + 1)
|
||||
if h[1] == s:
|
||||
found[i] = (h[0], s)
|
||||
if h[0] == 0:
|
||||
springs = "#" * h[1] + "." + springs[h[0] + h[1] + 1 :]
|
||||
elif h[0] + h[1] == len(springs):
|
||||
springs = springs[: -(h[1] + 1)] + "." + "#" * h[1]
|
||||
else:
|
||||
springs = springs[: h[0] - 1] + "." + "#" * h[1] + "." + springs[h[0] + h[1] + 1 :]
|
||||
print(
|
||||
"found index",
|
||||
i,
|
||||
"value",
|
||||
groups[i],
|
||||
"at",
|
||||
h[0],
|
||||
"new springs",
|
||||
springs,
|
||||
)
|
||||
break
|
||||
|
||||
print("cleaned springs", springs, "found", found)
|
||||
not_found = []
|
||||
# not_found entry = (min_x, max_x, index)
|
||||
nf = []
|
||||
last_min = 0
|
||||
for i in range(len(groups)):
|
||||
if i in found:
|
||||
if nf:
|
||||
for x in nf:
|
||||
not_found.append((last_min, found[i][0], x))
|
||||
nf = []
|
||||
last_min = sum(found[i])
|
||||
else:
|
||||
nf.append(i)
|
||||
if nf:
|
||||
for x in nf:
|
||||
not_found.append((last_min, len(springs), x))
|
||||
|
||||
return springs, not_found
|
||||
|
||||
|
||||
def get_open_groups(springs: str) -> list[tuple[int, str]]:
|
||||
open_groups = []
|
||||
start = None
|
||||
group_str = ""
|
||||
for i, c in enumerate(springs):
|
||||
if c == ".":
|
||||
if start is not None:
|
||||
open_groups.append((start, group_str))
|
||||
start = None
|
||||
group_str = ""
|
||||
else:
|
||||
if start is None:
|
||||
start = i
|
||||
group_str += c
|
||||
|
||||
if start is not None:
|
||||
open_groups.append((start, group_str))
|
||||
|
||||
return open_groups
|
||||
|
||||
|
||||
def get_arrangements(springs: str, groups: list[int]) -> int:
|
||||
if sum(groups) + len(groups) - 1 == len(springs):
|
||||
return 1
|
||||
|
||||
open_groups = get_open_groups(springs)
|
||||
print(f"{groups} => {springs} => {open_groups}")
|
||||
|
||||
return 0
|
||||
|
||||
class Day(AOCDay):
|
||||
inputs = [
|
||||
[
|
||||
(21, "input12_test"),
|
||||
(101, "input12_debug"),
|
||||
(6981, "input12"),
|
||||
],
|
||||
[
|
||||
(525152, "input12_test"),
|
||||
(None, "input12"),
|
||||
(4546215031609, "input12"),
|
||||
],
|
||||
]
|
||||
|
||||
def part1(self) -> Any:
|
||||
ans = 0
|
||||
def parse_input(self, part2: bool = False) -> list[tuple[str, tuple[int, ...]]]:
|
||||
records = []
|
||||
for line in self.getInput():
|
||||
springs, groups = line.split()
|
||||
groups = list(map(int, groups.split(",")))
|
||||
f = get_arrangements(springs, groups)
|
||||
print("RESULT", springs, "=>", f)
|
||||
ans += f
|
||||
groups = tuple(map(int, groups.split(",")))
|
||||
if part2:
|
||||
springs = "?".join([springs] * 5)
|
||||
groups *= 5
|
||||
records.append((springs, groups))
|
||||
|
||||
print("Final result:", ans, "=>", self._current_test_solution)
|
||||
exit(0)
|
||||
return ans
|
||||
return records
|
||||
|
||||
def get_arrangements(self, springs: str, groups: tuple[int, ...]) -> int:
|
||||
if (springs, groups) in self.DP:
|
||||
return self.DP[(springs, groups)]
|
||||
|
||||
if len(groups) == 0:
|
||||
return 0
|
||||
|
||||
count = 0
|
||||
for i in range(len(springs) - get_min_len(groups) + 1):
|
||||
sub_springs = springs[i:]
|
||||
hash_len = groups[0]
|
||||
if (
|
||||
(i > 0 and springs[i - 1] == "#")
|
||||
or "." in sub_springs[:hash_len]
|
||||
or (len(sub_springs) > hash_len and sub_springs[hash_len] == "#")
|
||||
):
|
||||
if "#" in springs[:i]:
|
||||
break
|
||||
else:
|
||||
continue
|
||||
|
||||
if len(groups) > 1:
|
||||
count += self.get_arrangements(springs[i + groups[0] + 1 :], groups[1:])
|
||||
else:
|
||||
if "#" in springs[i + groups[0] :]:
|
||||
continue
|
||||
count += 1
|
||||
|
||||
self.DP[(springs, groups)] = count
|
||||
return self.DP[(springs, groups)]
|
||||
|
||||
def part1(self) -> Any:
|
||||
return sum(self.get_arrangements(springs, groups) for springs, groups in self.parse_input())
|
||||
|
||||
def part2(self) -> Any:
|
||||
if not self.is_test():
|
||||
return ""
|
||||
ans = 0
|
||||
for i, line in enumerate(self.getInput()):
|
||||
springs, groups = line.split()
|
||||
groups = list(map(int, groups.split(",")))
|
||||
springs = "?".join([springs] * 5)
|
||||
groups = groups * 5
|
||||
f = get_arrangements(springs, groups)
|
||||
print(i + 1, "/", len(self.input), "=>", f)
|
||||
ans += f
|
||||
|
||||
return ans
|
||||
return sum(self.get_arrangements(springs, groups) for springs, groups in self.parse_input(part2=True))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
5
day18.py
5
day18.py
@ -33,16 +33,13 @@ class Day(AOCDay):
|
||||
def get_lagoon_area(self, part2: bool = False) -> int:
|
||||
start = Coordinate(0, 0)
|
||||
points = []
|
||||
perimeter = 0
|
||||
for d, l in self.parse_input(part2):
|
||||
perimeter += l
|
||||
end = start + DIRECTIONS[d] * l
|
||||
points.append(end)
|
||||
start = end
|
||||
|
||||
p = Polygon(points)
|
||||
a = abs(p.get_area())
|
||||
return int(p.get_area()) + perimeter // 2 + 1
|
||||
return int(p.get_area()) + int(p.get_circumference()) // 2 + 1
|
||||
|
||||
def part1(self) -> Any:
|
||||
return self.get_lagoon_area()
|
||||
|
||||
6
inputs/input12_debug
Normal file
6
inputs/input12_debug
Normal file
@ -0,0 +1,6 @@
|
||||
??????#??.??.? 2,1,1
|
||||
.??#?#???????.??#? 1,5,2,1,2
|
||||
????##.?????#???? 1,3,1,2,1
|
||||
?.???#?????#?#?? 1,1,2,4
|
||||
??#??????????## 1,2,2
|
||||
?.?????#??? 1,2
|
||||
Loading…
Reference in New Issue
Block a user