Aetherus

Aetherus

Advent of Code 2023 - Day 12

I tried to use combinatorial to solve today’s puzzles but failed (my brain burned out :exploding_head:). In the end I just used brute force with memoization.

Task.async_stream turned out to be very helpful. It both let me handle each line of input concurrently, and allows me to abuse process dictionaries :grin:

defmodule AoC2023.Day12 do

  # `input` for both parts are things like
  #
  # [
  #   {"???.###", [1,1,3]},
  #   {".??..??...###.", [1,1,3]},
  #   ...
  # ]

  @spec part1([{String.t(), [pos_integer()]}]) :: non_neg_integer()
  def part1(input) do
    input
    |> Task.async_stream(fn {springs, counts} ->
      aux(springs, ".", counts)
    end, ordered: false)
    |> Stream.map(&elem(&1, 1))
    |> Enum.sum()
  end

  @spec part2([{String.t(), [pos_integer()]}]) :: non_neg_integer()
  def part2(input) do
    input
    |> Enum.map(fn {springs, counts} ->
      {
        List.duplicate(springs, 5) |> Enum.join("?"),
        List.duplicate(counts, 5) |> List.flatten()
      }
    end)
    |> part1()
  end

  @spec aux(
    springs :: String.t(),
    previous_spring :: String.t(),
    counts :: [pos_integer()]
  ) :: non_neg_integer()
  defp aux("", _, []), do: 1

  defp aux("", _, [0]), do: 1

  defp aux("", _, _), do: 0

  defp aux("#" <> _, _, []), do: 0

  defp aux("#" <> _, _, [0 | _]), do: 0

  defp aux("#" <> rest, _, [h | t]), do: aux(rest, "#", [h - 1 | t])

  defp aux("." <> rest, _, []), do: aux(rest, ".", [])

  defp aux("." <> rest, "#", [0 | t]), do: aux(rest, ".", t)

  defp aux("." <> _, "#", [_ | _]), do: 0

  defp aux("." <> rest, ".", counts), do: aux(rest, ".", counts)

  defp aux("?" <> rest, "#", []), do: aux(rest, ".", [])

  defp aux("?" <> rest, "#", [0 | t]), do: aux(rest, ".", t)

  defp aux("?" <> rest, "#", [h | t]), do: aux(rest, "#", [h - 1 | t])

  defp aux("?" <> rest, ".", []), do: aux(rest, ".", [])

  defp aux("?" <> rest, ".", [0 | t]), do: aux(rest, ".", t)

  defp aux("?" <> rest, ".", [h | t]) do
    memoized({rest, [h | t]}, fn ->
      aux(rest, "#", [h - 1 | t]) + aux(rest, ".", [h | t])
    end)
  end

  defp memoized(key, fun) do
    with nil <- Process.get(key) do
      fun.() |> tap(&Process.put(key, &1))
    end
  end
end

Most Liked

Aetherus

Aetherus

I still need to try your solution in a debugging way to fully understand it.

Here’s what I think about dynamic programming. It’s just brute force with some sort of caching. When we talk about caching, we know each entry needs a key. I think it doesn’t matter what the key is, as long as it’s consistent. If the keys are continuous non-negative integers or tuples of integers, in imperative languages we usually use an array or a 2D array or a 3D array as our store. But we don’t have to restrict ourselves to that kind of caching mechanism. We can use maps, GenServers, ETS tables, process dictionaries, or even databases as the cache store as long as it provides a fast way of lookup for an exact match, and what the type of the keys are just doesn’t matter.

Aetherus

Aetherus

I don’t know whether it’s O(1) or O(log N) either. According to my benchmark (not for this puzzle), process dictionary is faster than ETS, which in turn is faster than a plain map, and Agent is the slowest.

bjorng

bjorng

Erlang Core Team

I spent most time to get part 1 to work; I solved part 2 by adding memoization:

Where Next?

Popular in Challenges Top

bismark
Took me a minute to remember my binary math :smile: :grimacing:… import Bitwise __DIR__ |&gt; Path.join("puzzle.txt") |&gt; File.stream...
New
bjorng
This topic is about Day 10 of the Advent of Code 2021. We have a private leaderboard (shared with users of Erlang Forums ): https://adv...
New
New
bjorng
This topic is about Day 6 of the Advent of Code 2021. We have a private leaderboard (shared with users of Erlang Forums ): https://adve...
New
bjorng
This topic is about Day 16 of the Advent of Code 2021. We have a private leaderboard (shared with users of Erlang Forums): https://adve...
New
bjorng
This topic is about Day 18 of the Advent of Code 2021. We have a private leaderboard (shared with users of Erlang Forums): https://adve...
New
sukhmeetsd
All in all, from what I understand, it is better not to use GenServer.cast when we want some concurrent operations to happen for sure, be...
New
New
stevensonmt
Anyone else think the prompt for this challenge is contradictory? The rules for comparing packets include If both values are lists, c...
New
bjorng
This topic is about Day 14 of the Advent of Code 2020 . Thanks to @egze, we have a private leaderboard: https://adventofcode.com/2020/l...
New

Other popular topics Top

AstonJ
Posting this to see if we can make things easier for people to get into Neovim. If you use Neovim and have a favourite distro please let ...
New
JorisKok
I have a server on AWS, and was running a load test using artillery. When looking at the Phoenix dashboard I see the Ports going to 100% ...
New
stefanluptak
Hello everybody, usually, I use a 29" ultra-wide monitor for VSCode which can easily accomodate explorer (files panel) + file with code ...
New
dblack
I’ve got an issue with an app and I’ve no idea of how to troubleshoot it. I’m hoping someone here might have seen something similar. I p...
New
romenigld
I am trying to run a deploy with docker and I successfully runned with this command: docker build -t romenigld/blog-prod . but when I t...
New
joaquinalcerro
Hi there, I am working with Ecto-Postgresql and I need to call all of the records from a specific table but the table has 40,000 record...
New
komlanvi
Hi everyone, I was playing with phoenix liveView but I run into an issue. I have a form and want to validate each input text when the te...
New
Brian
What is the proper way to load a module from a file in to IEX? In the python world, doing something like this pretty standard: from ....
New
axelson
This post is a wiki (feel free to hit the edit button near the bottom right of this post to add your own changes!) This post collects co...
239 47849 226
New
PeterCarter
There are pre-rolled solutions for other frameworks that do work. However, Phoenix does not seem to have these. Have people had good expe...
New

We're in Beta

About us Mission Statement