Skip to content

Commit

Permalink
Solution day 12 (#10)
Browse files Browse the repository at this point in the history
  • Loading branch information
mkopec87 authored Dec 12, 2023
1 parent f07df50 commit b12a293
Show file tree
Hide file tree
Showing 3 changed files with 109 additions and 1 deletion.
3 changes: 2 additions & 1 deletion requirements-to-freeze.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
advent-of-code-data
black
isort
isort
tqdm
104 changes: 104 additions & 0 deletions src/2023/12/2023_12.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
import functools
import re
from typing import Any, Callable, List, Tuple

from tqdm import tqdm

from src.utils.data import load_data
from src.utils.submission import submit_or_print


def parse_input(line: str, repeats: int = 1) -> Tuple[str, List[int]]:
spl = line.split(" ")
row = "?".join([spl[0]] * repeats).strip(".")
groups = [int(x) for x in spl[1].split(",")]
groups = groups * repeats
return row, groups


def calc_groups(row: str) -> List[int]:
groups = []
group_len: int = 0
prev_char = "."
for r in row:
if r == "#":
group_len += 1
else:
if prev_char == "#":
groups.append(group_len)
group_len = 0
prev_char = r
if group_len > 0:
groups.append(group_len)
return groups


def lists_to_tuples(func: Callable) -> Any:
@functools.wraps(func)
def lists_to_tuples_wrapper(*args: Any, **kwargs: Any) -> bool:
args = [tuple(x) if type(x) == list else x for x in args]
return func(*args, **kwargs)

return lists_to_tuples_wrapper


def tuples_to_lists(func: Callable) -> Any:
@functools.wraps(func)
def lists_to_tuples_wrapper(*args: Any, **kwargs: Any) -> bool:
args = [list(x) if type(x) == tuple else x for x in args]
return func(*args, **kwargs)

return lists_to_tuples_wrapper


@lists_to_tuples
@functools.lru_cache(maxsize=100_000)
@tuples_to_lists
def arrangements(row: str, groups: List[int]) -> int:
row = row.lstrip(".")
if "?" not in row:
expected_groups = calc_groups(row)
if expected_groups != groups:
return 0
return 1

first_unk_index = row.index("?")
total = 0
for opt in [".", "#"]:
new_row = row[:first_unk_index] + opt + row[first_unk_index + 1 :]
new_row = new_row.lstrip(".")

# try dropping initial seq of hashes
if m := re.match(r"(#+)\.", row):
group_len = len(m.group(1))
if not groups or groups[0] != group_len:
continue # not a valid solution
total += arrangements(new_row[group_len:], groups[1:])
else:
total += arrangements(new_row, groups)

return total


def main(debug: bool) -> None:
input_data = load_data(debug)

total = 0
for line in tqdm(input_data.splitlines()):
row, groups = parse_input(line)
total += arrangements(row, groups)
result_part1 = total

total = 0
for line in tqdm(input_data.splitlines()):
row, groups = parse_input(line, 5)
total += arrangements(row, groups)
result_part2 = total

submit_or_print(result_part1, result_part2, debug)


if __name__ == "__main__":
debug_mode = True
debug_mode = False
main(debug_mode)
3 changes: 3 additions & 0 deletions src/2023/12/sample_input.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.#?#.?.#####.? 1,1,5,1
###.#?#.?.#####.? 3,1,1,5
???????????? 1

0 comments on commit b12a293

Please sign in to comment.