Day12
Mix.install([
{:kino_aoc, git: "https://github.com/ljgago/kino_aoc"},
{:benchee, "~> 1.0"},
{:nimble_parsec, "~> 1.0"},
{:libgraph, "~> 0.16.0"},
{:math, "~> 0.7.0"}
])
Get Input
{:ok, data} = KinoAOC.download_puzzle("2023", "12", System.fetch_env!("LB_AOC_SECRET"))
Solve
defmodule Day12 do
def out(res, t), do: IO.puts("Res #{t}: #{res}")
def run(data, :p1) do
data
|> String.split("\n", trim: true)
|> parse()
|> Enum.map(&count/1)
|> Enum.sum()
end
def run(data, :p2) do
data
|> String.split("\n", trim: true)
|> parse()
|> Enum.map(&unfold/1)
|> Enum.map(&count/1)
|> Enum.sum()
end
def parse(rows) do
rows
|> Enum.map(fn row ->
[s, d] = String.split(row)
d = for sym <- String.split(d, ",", trim: true), do: String.to_integer(sym)
{s, List.to_tuple(d), false}
end)
end
def unfold({str, gr, false}) do
str = str |> List.duplicate(5) |> Enum.join("?")
gr = Tuple.to_list(gr) |> List.duplicate(5) |> List.flatten() |> List.to_tuple()
{str, gr, false}
end
def count({"", {}, _}), do: 1
def count({"", {0}, _}), do: 1
def count({"", _rest, _}), do: 0
def count({str, {}, _}), do: (in?("#", str) && 0) || 1
def count(key) do
case :ets.lookup(:springs, key) do
[] ->
{str, strings, in_group} = unpack(key)
[sym | rest] = str
[gr_h | gr_rest] = strings
case sym do
"#" ->
if gr_h <= 0 do
0
else
new_gr = [gr_h - 1 | gr_rest]
nkey = pack({rest, new_gr, true})
res = count(nkey)
:ets.insert(:springs, {nkey, res})
res
end
"." ->
if in_group do
if gr_h == 0 do
nkey = pack({rest, gr_rest, false})
res = count(nkey)
:ets.insert(:springs, {nkey, res})
res
else
0
end
else
nkey = pack({rest, strings, false})
res = count(nkey)
:ets.insert(:springs, {nkey, res})
res
end
"?" ->
key_a = pack({["#" | rest], strings, in_group})
res_a = count(key_a)
:ets.insert(:springs, {key_a, res_a})
key_b = pack({["." | rest], strings, in_group})
res_b = count(key_b)
:ets.insert(:springs, {key_b, res_b})
res_a + res_b
end
[{_key, res}] ->
res
end
end
def unpack({str, tp, flag}) do
{String.graphemes(str), Tuple.to_list(tp), flag}
end
def pack({lstr, gr, flag}) do
{Enum.join(lstr), List.to_tuple(gr), flag}
end
def in?(c, str), do: :binary.match(str, c) != :nomatch
end
td = """
???.### 1,1,3
.??..??...?##. 1,1,3
?#?#?#?#?#?#?#? 1,3,1,6
????.#...#... 4,1,1
????.######..#####. 1,6,5
?###???????? 3,2,1
"""
:ets.new(:springs, [:set, :protected, :named_table])
td |> Day12.run(:p1) |> Day12.out("p1-test")
td |> Day12.run(:p2) |> Day12.out("p2-test")
data |> Day12.run(:p1) |> Day12.out("p1")
data |> Day12.run(:p2) |> Day12.out("p2")
:ets.delete(:springs)