generated from public/aoc_template
182 lines
5.5 KiB
Python
182 lines
5.5 KiB
Python
from tools.aoc import AOCDay
|
|
from tools.int_seq import triangular
|
|
from typing import Any
|
|
|
|
|
|
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 = []
|
|
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))
|
|
|
|
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[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 "?" in group_str:
|
|
open_groups.append((start, group_str))
|
|
start = None
|
|
group_str = ""
|
|
else:
|
|
if start is None:
|
|
start = i
|
|
group_str += c
|
|
|
|
if "?" in group_str:
|
|
open_groups.append((start, group_str))
|
|
|
|
return open_groups
|
|
|
|
|
|
class Day(AOCDay):
|
|
inputs = [
|
|
[
|
|
(21, "input12_test"),
|
|
(6981, "input12"),
|
|
],
|
|
[
|
|
(525152, "input12_test"),
|
|
(None, "input12"),
|
|
],
|
|
]
|
|
|
|
def part1(self) -> Any:
|
|
ans = 0
|
|
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
|
|
|
|
return ans
|
|
|
|
def part2(self) -> Any:
|
|
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
|
|
|
|
|
|
if __name__ == "__main__":
|
|
day = Day(2023, 12)
|
|
day.run(verbose=True)
|