WIP 2.
This commit is contained in:
parent
edc50cb9c2
commit
c7d2eb2171
227
2023/day12.py
227
2023/day12.py
@ -2,6 +2,8 @@ import itertools
|
|||||||
import sys
|
import sys
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
|
from functools import lru_cache
|
||||||
|
from multiprocessing.pool import ThreadPool
|
||||||
from typing import Iterator
|
from typing import Iterator
|
||||||
|
|
||||||
from tqdm import tqdm
|
from tqdm import tqdm
|
||||||
@ -9,6 +11,25 @@ from tqdm import tqdm
|
|||||||
lines = sys.stdin.read().splitlines()
|
lines = sys.stdin.read().splitlines()
|
||||||
|
|
||||||
|
|
||||||
|
def v1(pattern: str, counts: list[int]) -> int:
|
||||||
|
count = 0
|
||||||
|
missing = [i for i in range(len(pattern)) if pattern[i] == "?"]
|
||||||
|
|
||||||
|
for replacements in itertools.product(".#", repeat=len(missing)):
|
||||||
|
c_pattern = list(pattern)
|
||||||
|
for i_missing, replacement in zip(missing, replacements):
|
||||||
|
c_pattern[i_missing] = replacement
|
||||||
|
|
||||||
|
parts = [p for p in "".join(c_pattern).split(".") if p]
|
||||||
|
|
||||||
|
if len(parts) == len(counts) and all(
|
||||||
|
len(p) == c for p, c in zip(parts, counts)
|
||||||
|
):
|
||||||
|
count += 1
|
||||||
|
|
||||||
|
return count
|
||||||
|
|
||||||
|
|
||||||
def fn1(pattern: str, counts: list[int]) -> Iterator[tuple[str, list[int]]]:
|
def fn1(pattern: str, counts: list[int]) -> Iterator[tuple[str, list[int]]]:
|
||||||
if not pattern:
|
if not pattern:
|
||||||
yield "", counts
|
yield "", counts
|
||||||
@ -23,7 +44,9 @@ def fn1(pattern: str, counts: list[int]) -> Iterator[tuple[str, list[int]]]:
|
|||||||
c_pattern = ".".join("#" * c for c in counts)
|
c_pattern = ".".join("#" * c for c in counts)
|
||||||
if all(pattern[i] == "?" for i in range(len(pattern)) if c_pattern[i] == "."):
|
if all(pattern[i] == "?" for i in range(len(pattern)) if c_pattern[i] == "."):
|
||||||
yield c_pattern, []
|
yield c_pattern, []
|
||||||
return
|
for other_pattern, other_rest in fn1(pattern, counts[:-1]):
|
||||||
|
yield other_pattern, other_rest + [counts[-1]]
|
||||||
|
return
|
||||||
|
|
||||||
if pattern.find("#") == -1:
|
if pattern.find("#") == -1:
|
||||||
yield "." * len(pattern), counts
|
yield "." * len(pattern), counts
|
||||||
@ -33,6 +56,7 @@ def fn1(pattern: str, counts: list[int]) -> Iterator[tuple[str, list[int]]]:
|
|||||||
|
|
||||||
if pattern.find("?") == -1 and counts[0] == len(pattern):
|
if pattern.find("?") == -1 and counts[0] == len(pattern):
|
||||||
yield pattern, counts[1:]
|
yield pattern, counts[1:]
|
||||||
|
return
|
||||||
|
|
||||||
for i in range(len(pattern)):
|
for i in range(len(pattern)):
|
||||||
c_pattern = "." * i + "#" * counts[0]
|
c_pattern = "." * i + "#" * counts[0]
|
||||||
@ -55,7 +79,8 @@ def fn2(
|
|||||||
patterns: list[str], counts: list[int], depth: int = 0
|
patterns: list[str], counts: list[int], depth: int = 0
|
||||||
) -> Iterator[tuple[str, ...]]:
|
) -> Iterator[tuple[str, ...]]:
|
||||||
if not patterns:
|
if not patterns:
|
||||||
yield ()
|
if not counts:
|
||||||
|
yield ()
|
||||||
return
|
return
|
||||||
|
|
||||||
with_hash = sum(1 for p in patterns[1:] if p.find("#") >= 0)
|
with_hash = sum(1 for p in patterns[1:] if p.find("#") >= 0)
|
||||||
@ -65,42 +90,194 @@ def fn2(
|
|||||||
remaining = [] if with_hash == 0 else counts[-with_hash:]
|
remaining = [] if with_hash == 0 else counts[-with_hash:]
|
||||||
|
|
||||||
for fp, fc in fn1(patterns[0], to_fit):
|
for fp, fc in fn1(patterns[0], to_fit):
|
||||||
if depth == 0:
|
# print(f'{"|" * depth} {patterns[0]} ({to_fit}): {fp}, {fc}')
|
||||||
... # print(fp, fc)
|
|
||||||
for fp2 in fn2(patterns[1:], fc + remaining, depth + 1):
|
for fp2 in fn2(patterns[1:], fc + remaining, depth + 1):
|
||||||
# print(fp, fc)
|
# if depth == 0:
|
||||||
|
# print((fp,) + fp2)
|
||||||
yield (fp,) + fp2
|
yield (fp,) + fp2
|
||||||
|
|
||||||
|
|
||||||
# print(list(fn1("??", [1, 1, 3, 1])))
|
def v2(pattern: str, counts: list[int]) -> int:
|
||||||
# exit()
|
blocks = list(filter(len, pattern.split(".")))
|
||||||
|
|
||||||
|
|
||||||
repeat = 5
|
|
||||||
count = 0
|
|
||||||
for i_line, line in enumerate(lines):
|
|
||||||
parts = line.split(" ")
|
|
||||||
pattern = "?".join(parts[0] for _ in range(repeat))
|
|
||||||
counts = [int(c) for c in parts[1].split(",")] * repeat
|
|
||||||
|
|
||||||
# print("---")
|
# print("---")
|
||||||
# print(i_line, parts[0])
|
# print(i_line, parts[0])
|
||||||
# print("--")
|
# print("--")
|
||||||
print("---")
|
# print("---")
|
||||||
print(parts[0], counts[: len(counts) // 5])
|
# print(parts[0], counts[: len(counts) // repeat])
|
||||||
r1 = list(fn2(list(filter(len, pattern.split("."))), counts))
|
fillers = list(fn2(blocks, counts))
|
||||||
r2 = set(r1)
|
|
||||||
print(len(r1), len(r2)) # , r2)
|
|
||||||
count += len(r1)
|
|
||||||
|
|
||||||
# if i_line == 1:
|
# for filler in fillers:
|
||||||
# break
|
# assert len(filler) == len(blocks)
|
||||||
|
# for f, b in zip(filler, blocks):
|
||||||
|
# assert all(fi == bi or bi == "?" for fi, bi in zip(f, b))
|
||||||
|
# # print(b, f)
|
||||||
|
|
||||||
|
# hashes = list(filter(len, ".".join(filler).split(".")))
|
||||||
|
# print(hashes, counts)
|
||||||
|
|
||||||
|
# print(len(fillers))
|
||||||
|
return len(fillers)
|
||||||
|
|
||||||
|
|
||||||
|
@lru_cache
|
||||||
|
def fn3p(pattern: str, counts: tuple[int, ...]) -> int:
|
||||||
|
"""
|
||||||
|
fn3p tries to fit ALL values in counts() inside the pattern.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# no pattern -> ok if nothing to fit, otherwise ko
|
||||||
|
if not pattern:
|
||||||
|
count = 1 if not counts else 0
|
||||||
|
|
||||||
|
# no count -> ok if pattern has no mandatory entry, else ko
|
||||||
|
elif not counts:
|
||||||
|
count = 1 if pattern.find("#") == -1 else 0
|
||||||
|
|
||||||
|
# cannot fit all values -> ko
|
||||||
|
elif len(pattern) < sum(counts) + len(counts) - 1:
|
||||||
|
count = 0
|
||||||
|
|
||||||
|
else:
|
||||||
|
count = 0
|
||||||
|
# if pattern[0] == "?":
|
||||||
|
# count += fn3p(pattern[1:], counts)
|
||||||
|
|
||||||
|
# count += fn3p(pattern[counts[0] + 1], counts[1:])
|
||||||
|
|
||||||
|
for i in range(len(pattern)):
|
||||||
|
c_pattern = "." * i + "#" * counts[0]
|
||||||
|
|
||||||
|
if len(c_pattern) > len(pattern):
|
||||||
|
break
|
||||||
|
|
||||||
|
if len(c_pattern) < len(pattern):
|
||||||
|
c_pattern += "."
|
||||||
|
|
||||||
|
# print(">", c_pattern, counts[0])
|
||||||
|
|
||||||
|
if all(
|
||||||
|
pattern[i] == "?" for i in range(len(c_pattern)) if c_pattern[i] == "."
|
||||||
|
):
|
||||||
|
count += fn3p(pattern[len(c_pattern) :], counts[1:])
|
||||||
|
|
||||||
|
return count
|
||||||
|
|
||||||
|
|
||||||
|
def fn3(pattern: str, counts: tuple[int, ...]) -> Iterator[tuple[int, tuple[int, ...]]]:
|
||||||
|
# empty pattern
|
||||||
|
if not pattern:
|
||||||
|
yield 1, counts
|
||||||
|
return
|
||||||
|
|
||||||
|
if not counts:
|
||||||
|
if pattern.find("#") == -1:
|
||||||
|
yield 1, ()
|
||||||
|
return
|
||||||
|
|
||||||
|
if pattern.find("#") != -1 and len(pattern) < counts[0]:
|
||||||
|
return
|
||||||
|
|
||||||
|
if pattern.find("?") == -1 and counts[0] == len(pattern):
|
||||||
|
yield 1, counts[1:]
|
||||||
|
return
|
||||||
|
|
||||||
|
for i in range(len(counts) + 1):
|
||||||
|
to_fit = counts[:i]
|
||||||
|
|
||||||
|
yield fn3p(pattern, to_fit), counts[i:]
|
||||||
|
|
||||||
|
|
||||||
|
@lru_cache
|
||||||
|
def fn4(patterns: tuple[str], counts: tuple[int, ...], depth: int = 0) -> int:
|
||||||
|
if not patterns:
|
||||||
|
if not counts:
|
||||||
|
return 1
|
||||||
|
return 0
|
||||||
|
|
||||||
|
with_hash = sum(1 for p in patterns[1:] if p.find("#") >= 0)
|
||||||
|
|
||||||
|
if with_hash > len(counts):
|
||||||
|
return 0
|
||||||
|
|
||||||
|
to_fit = counts if with_hash == 0 else counts[:-with_hash]
|
||||||
|
remaining = () if with_hash == 0 else counts[-with_hash:]
|
||||||
|
|
||||||
|
count = 0
|
||||||
|
for fp, fc in fn3(patterns[0], to_fit):
|
||||||
|
if fp == 0:
|
||||||
|
continue
|
||||||
|
# print("|" * depth, patterns[0], to_fit, remaining, fp, fc)
|
||||||
|
count += fp * fn4(patterns[1:], fc + remaining, depth + 1)
|
||||||
|
|
||||||
|
return count
|
||||||
|
|
||||||
|
|
||||||
|
def v3(pattern: str, counts: list[int]) -> int:
|
||||||
|
blocks = list(filter(len, pattern.split(".")))
|
||||||
|
return fn4(tuple(blocks), tuple(counts))
|
||||||
|
|
||||||
|
|
||||||
|
# print(list(fn1("??#?", [2, 1])))
|
||||||
|
# print(list(fn2(["??????", "??"], [2, 1, 1])))
|
||||||
|
# exit()
|
||||||
|
|
||||||
|
# r = 1
|
||||||
|
# print(v2("?".join("??.???#.???" for _ in range(r)), [4, 1] * r))
|
||||||
|
# print(v3("?".join("??.???#.???" for _ in range(r)), [4, 1] * r))
|
||||||
|
# exit()
|
||||||
|
|
||||||
|
# r = 3
|
||||||
|
# print(v2("?".join(".??..??...?##." for _ in range(r)), [1, 1, 3] * r))
|
||||||
|
# print(v3("?".join(".??..??...?##." for _ in range(r)), [1, 1, 3] * r))
|
||||||
|
# exit()
|
||||||
|
|
||||||
|
all_blocks = []
|
||||||
|
set_blocks = set()
|
||||||
|
for line in lines:
|
||||||
|
blocks = list(filter(len, (line.split()[0] * 5).split(".")))
|
||||||
|
all_blocks.extend(blocks)
|
||||||
|
set_blocks.update(blocks)
|
||||||
|
|
||||||
|
print(len(set_blocks), len(all_blocks))
|
||||||
|
|
||||||
|
|
||||||
|
def compute_possible_arrangements(repeat: int) -> int:
|
||||||
|
# with tqdm(total=len(lines)) as pbar:
|
||||||
|
|
||||||
|
# def _fn(line: str) -> int:
|
||||||
|
# parts = line.split(" ")
|
||||||
|
# pattern = "?".join(parts[0] for _ in range(repeat))
|
||||||
|
# counts = [int(c) for c in parts[1].split(",")] * repeat
|
||||||
|
# count = v2(pattern, counts)
|
||||||
|
# pbar.update(1)
|
||||||
|
# return count
|
||||||
|
|
||||||
|
# with ThreadPool() as tp:
|
||||||
|
# return sum(tp.imap_unordered(_fn, lines))
|
||||||
|
|
||||||
|
count = 0
|
||||||
|
|
||||||
|
for i_line, line in enumerate(tqdm(lines)):
|
||||||
|
parts = line.split(" ")
|
||||||
|
pattern = "?".join(parts[0] for _ in range(repeat))
|
||||||
|
counts = [int(c) for c in parts[1].split(",")] * repeat
|
||||||
|
# c1 = v1(pattern, counts)
|
||||||
|
# c2 = v2(pattern, counts)
|
||||||
|
c3 = v3(pattern, counts)
|
||||||
|
|
||||||
|
# if c2 != c3:
|
||||||
|
# print(i_line, line, counts, c2, c3)
|
||||||
|
|
||||||
|
count += c3
|
||||||
|
|
||||||
|
return count
|
||||||
|
|
||||||
|
|
||||||
# part 1
|
# part 1
|
||||||
answer_1 = count
|
answer_1 = compute_possible_arrangements(1)
|
||||||
print(f"answer 1 is {answer_1}")
|
print(f"answer 1 is {answer_1}")
|
||||||
|
|
||||||
# part 2
|
# part 2
|
||||||
answer_2 = ...
|
answer_2 = compute_possible_arrangements(5)
|
||||||
print(f"answer 2 is {answer_2}")
|
print(f"answer 2 is {answer_2}")
|
||||||
|
Loading…
Reference in New Issue
Block a user