defmodule AOC.Day12 do use AOC.Day, day: 12, input: "example" # use AOC.Day, day: 12 def parse_input(lines) do Enum.map(lines, fn line -> [conditions, groups] = String.split(line, " ") conditions = String.to_charlist(conditions) groups = groups |> String.split(",") |> Enum.map(&String.to_integer/1) {conditions, groups} end) end def possible_unknown_spring_conditions(unknown_spring_count) when unknown_spring_count == 0, do: [[]] def possible_unknown_spring_conditions(unknown_spring_count) do next_list = possible_unknown_spring_conditions(unknown_spring_count - 1) Enum.map(next_list, &[true | &1]) ++ Enum.map(next_list, &[false | &1]) end def apply_spring_configuration([], []), do: [] def apply_spring_configuration([?. | conditions], spring_configuration) do [?. | apply_spring_configuration(conditions, spring_configuration)] end def apply_spring_configuration([?# | conditions], spring_configuration) do [?# | apply_spring_configuration(conditions, spring_configuration)] end def apply_spring_configuration([?? | conditions], [true | spring_configuration]) do [?. | apply_spring_configuration(conditions, spring_configuration)] end def apply_spring_configuration([?? | conditions], [false | spring_configuration]) do [?# | apply_spring_configuration(conditions, spring_configuration)] end def valid_spring_conditions?(conditions, []) do not Enum.member?(conditions, ?#) end def valid_spring_conditions?([?. | conditions], groups) do valid_spring_conditions?(conditions, groups) end def valid_spring_conditions?(conditions, [count | groups]) do {left, right} = Enum.split(conditions, count) length(left) == count and Enum.all?(left, &(&1 == ?#)) and (length(right) == 0 or hd(right) == ?.) and valid_spring_conditions?(right, groups) end def generate_arrangements(conditions, groups) do unknown_spring_count = Enum.count(conditions, &(&1 == ??)) possible_unknown_spring_conditions(unknown_spring_count) |> Stream.map(fn spring_configuration -> apply_spring_configuration(conditions, spring_configuration) end) |> Stream.filter(fn possible_conditions -> valid_spring_conditions?(possible_conditions, groups) end) # |> Enum.to_list() end def calculate_arrangement_counts(input, duplicates \\ 1) do input |> Enum.map(fn {conditions, groups} -> conditions = conditions |> List.duplicate(duplicates) |> Enum.intersperse(~c"?") |> List.flatten() groups = groups |> List.duplicate(duplicates) |> List.flatten() generate_arrangements(conditions, groups) end) |> Enum.map(&Enum.count/1) |> Enum.sum() end def part1(input) do calculate_arrangement_counts(input) end def part2(input) do calculate_arrangement_counts(input, 5) end end # NOTE TO SELF: memoize the brute force...