Powered by AppSignal & Oban Pro
Would you like to see your link here? Contact us

Day 4

2024/elixir/day_04.livemd

Day 4

Mix.install([:kino])

Section

content =
  Kino.FS.file_path("input_04.txt")
  |> File.read!()
defmodule Grid do
  alias __MODULE__

  defstruct [:binary, :side]

  def new(str) do
    binary = str |> String.split() |> IO.iodata_to_binary()

    %Grid{
      binary: binary,
      side: binary |> byte_size() |> :math.sqrt() |> floor()
    }
  end

  def matches(%Grid{} = grid, pattern) do
    for {i, _} <- :binary.matches(grid.binary, pattern) do
      to_row_column(grid, i)
    end
  end

  def to_row_column(%Grid{} = grid, i) do
    {div(i, grid.side), rem(i, grid.side)}
  end

  def select(%Grid{} = grid, {row, col}, default \\ nil) do
    cond do
      row < 0 or col < 0 ->
        default

      row >= grid.side or col >= grid.side ->
        default

      :else ->
        :binary.at(grid.binary, row * grid.side + col)
    end
  end
end
grid = Grid.new(content)

Part 1

x_matches = Grid.matches(grid, "X")
octo_directions = for r <- -1..1, c <- -1..1, {r, c} != {0, 0}, do: {r, c}
direction_ray = fn {r, c}, {dr, dc} ->
  Stream.iterate({r, c}, fn {r, c} -> {r + dr, c + dc} end) |> Enum.take(4)
end
xmas_strings =
  for match <- x_matches, direction <- octo_directions do
    for rc <- direction_ray.(match, direction) do
      Grid.select(grid, rc, 0)
    end
  end
xmas_strings |> Enum.filter(&amp; &amp;1 == ~c"XMAS") |> length()

Part 2

a_matches = Grid.matches(grid, "A")
x_selector = [{-1, -1}, {-1, 1}, {0, 0}, {1, -1}, {1, 1}]
mas_strings =
  for {r, c} <- a_matches do
    for {dr, dc} <- x_selector do
      Grid.select(grid, {r + dr, c + dc}, 0)
    end
  end
mas_patterns = [~c"MSAMS", ~c"SSAMM", ~c"MMASS",  ~c"SMASM"]
mas_strings |> Enum.filter(&amp; &amp;1 in mas_patterns) |> length()