Advent of Code 2024 - Day 4

For part 1 I rotates the matrix instead of looking in differente directions.
And for part 2, I went with a sliding window using Enum,chunks =)


defmodule Smaoc.Solution.Day4 do
  def solve(:part1, input) do
    matrix = parse_input(input)

    [
      [],
      [:reverse],
      [:transpose],
      [:transpose, :reverse],
      [:rotate45],
      [:rotate45, :reverse],
      [:reverse, :rotate45],
      [:reverse, :rotate45, :reverse]
    ]
    |> Enum.map(
      &(matrix
        |> apply_operations(&1)
        |> count_XMAS)
    )
    |> Enum.sum()
  end

  def solve(:part2, input) do
    valid_patterns = [
      ~r/M.M.A.S.S/,
      ~r/M.S.A.M.S/,
      ~r/S.M.A.S.M/,
      ~r/S.S.A.M.M/
    ]

    input
    |> parse_input()
    |> Enum.chunk_every(3, 1, :discard)
    |> Enum.map(fn row ->
      row
      |> transpose()
      |> Enum.chunk_every(3, 1, :discard)
      |> Enum.count(fn chunk ->
        Enum.any?(valid_patterns, &Regex.match?(&1, to_string(chunk)))
      end)
    end)
    |> Enum.sum()
  end

  defp parse_input(input) do
    input
    |> String.split("\n", trim: true)
    |> Enum.map(&String.split(&1, "", trim: true))
  end

  def reverse(matrix), do: Enum.map(matrix, &Enum.reverse/1)

  def transpose(matrix) do
    matrix
    |> Enum.zip()
    |> Enum.map(&Tuple.to_list/1)
  end

  def rotate45(matrix) do
    for(
      {row, i} <- Enum.with_index(matrix),
      {v, j} <- Enum.with_index(row),
      do: {i + j, v}
    )
    |> Enum.group_by(&elem(&1, 0), &elem(&1, 1))
    |> Enum.sort()
    |> Enum.map(&elem(&1, 1))
  end

  def count_XMAS(matrix) do
    matrix
    |> Enum.map(fn l ->
      ~r/XMAS/
      |> Regex.scan(to_string(l))
      |> Enum.count()
    end)
    |> Enum.sum()
  end

  defp apply_operations(matrix, ops) do
    Enum.reduce(ops, matrix, fn op, m -> apply(__MODULE__, op, [m]) end)
  end
end