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

Day 4

livebook/day4.livemd

Day 4

Mix.install([
  {:aoc2024,  path: "."}
])

Input

sample = """
MMMSXXMASM
MSAMXMSMSA
AMXSXMAAMM
MSAMASMSMX
XMASAMXAMM
XXAMMXXAMA
SMSMSASXSS
SAXAMASAAA
MAMMMXMMMM
MXMXAXMASX
"""

input = "priv/day4.txt"

Part 1

defmodule Part1 do
  def mapper({row, row_i}) do
    row
    |> String.split("", trim: true)
    |> Enum.with_index()
    |> Enum.map(fn {val, col_i} -> {{row_i, col_i}, val} end)
  end

  def get_perms(poss) do
    poss
    |> Enum.flat_map(&flat_mapper/1)
  end

  defp flat_mapper({pos, "X"}) do
    {row_i, col_i} = pos

    perm =
      [
        [{row_i, col_i}, {row_i - 1, col_i - 1}, {row_i - 2, col_i - 2}, {row_i - 3, col_i - 3}],
        [{row_i, col_i}, {row_i - 1, col_i}, {row_i - 2, col_i}, {row_i - 3, col_i}],
        [{row_i, col_i}, {row_i - 1, col_i + 1}, {row_i - 2, col_i + 2}, {row_i - 3, col_i + 3}],
        [{row_i, col_i}, {row_i, col_i - 1}, {row_i, col_i - 2}, {row_i, col_i - 3}],
        [{row_i, col_i}, {row_i, col_i + 1}, {row_i, col_i + 2}, {row_i, col_i + 3}],
        [{row_i, col_i}, {row_i + 1, col_i - 1}, {row_i + 2, col_i - 2}, {row_i + 3, col_i - 3}],
        [{row_i, col_i}, {row_i + 1, col_i}, {row_i + 2, col_i}, {row_i + 3, col_i}],
        [{row_i, col_i}, {row_i + 1, col_i + 1}, {row_i + 2, col_i + 2}, {row_i + 3, col_i + 3}]
      ]

    perm
  end

  defp flat_mapper(_), do: []
end
poss =
  sample
  |> AoC2024.parse(&Part1.mapper/1)
  |> List.flatten()
  |> Map.new()

poss
|> Part1.get_perms()
|> Enum.filter(fn perm ->
  word = Enum.map(perm, & poss[&1])
  word == ["X", "M", "A", "S"] || word == ["S", "A", "M", "X"]
end)
|> length
poss =
  AoC2024.load_text(input)
  |> AoC2024.parse(&Part1.mapper/1)
  |> List.flatten()
  |> Map.new()

poss
|> Part1.get_perms()
|> Enum.filter(fn perm ->
  word = Enum.map(perm, & poss[&1])
  word == ["X", "M", "A", "S"] || word == ["S", "A", "M", "X"]
end)
|> length

Part 2

defmodule Part2 do
  def mapper({row, row_i}) do
    row
    |> String.split("", trim: true)
    |> Enum.with_index()
    |> Enum.map(fn {val, col_i} -> {{row_i, col_i}, val} end)
  end

  def get_perms(poss) do
    poss
    |> Enum.flat_map(&flat_mapper(&1, poss))
  end

  defp flat_mapper({pos, "A"}, poss) do
    {row_i, col_i} = pos

    [[
      {row_i, col_i}, # center
      {row_i - 1, col_i - 1}, # top-left
      {row_i - 1, col_i + 1}, # top-right
      {row_i + 1, col_i - 1}, # bottom-left
      {row_i + 1, col_i + 1} # bottom-right
    ]]

        [
      [
        {row_i, col_i}, # "A" center
        {row_i - 1, col_i - 1}, # Top-left "M"
        {row_i + 1, col_i - 1}, # Bottom-left "M"
        {row_i - 1, col_i + 1}, # Top-right "S"
        {row_i + 1, col_i + 1}  # Bottom-right "S"
      ]
    ]
  end

  defp flat_mapper(_, _), do: []
end
poss =
  sample
  |> AoC2024.parse(&Part2.mapper/1)
  |> List.flatten()
  |> Map.new()

poss
|> Part2.get_perms()
|> Enum.filter(fn perm ->
  word = Enum.map(perm, &poss[&1])

  word in [
    ["A", "M", "S", "M", "S"],
    ["A", "S", "M", "S", "M"],
    ["A", "M", "M", "S", "S"],
    ["A", "S", "S", "M", "M"]
  ]
end)
|> length
poss =
  input
  |> AoC2024.load_text()
  |> AoC2024.parse(&Part2.mapper/1)
  |> List.flatten()
  |> Map.new()

poss
|> Part2.get_perms()
|> Enum.filter(fn perm ->
  word = Enum.map(perm, &poss[&1])

  word in [
    ["A", "M", "S", "M", "S"],
    ["A", "S", "M", "S", "M"],
    ["A", "M", "M", "S", "S"],
    ["A", "S", "S", "M", "M"]
  ]
end)
|> length
defmodule Temp do
  def mapper({row, row_i}) do
    row
    |> String.split("", trim: true)
    |> Enum.with_index()
    |> Enum.map(fn {val, col_i} -> {{row_i, col_i}, val} end)
  end

  def get_perms(poss) do
    poss
    |> Enum.flat_map(&flat_mapper(&1, poss))
  end

  defp flat_mapper({pos, "A"}, poss) do
    {row_i, col_i} = pos

    [
      [
        {row_i, col_i},            # Center (A)
        {row_i - 1, col_i - 1},    # Top-left (M)
        {row_i - 1, col_i + 1},    # Top-right (S)
        {row_i + 1, col_i - 1},    # Bottom-left (S)
        {row_i + 1, col_i + 1}     # Bottom-right (M)
      ]
    ]
    |> Enum.filter(&valid_positions?(&1, poss))
  end

  defp flat_mapper(_, _), do: []

  defp valid_positions?(positions, poss) do
    Enum.all?(positions, &Map.has_key?(poss, &1))
  end

  def count_xmas(sample) do
    # Parse the grid into positions
    poss =
      sample
      |> Enum.with_index()
      |> Enum.flat_map(&mapper/1)
      |> Map.new()

    # Find all "X-MAS" permutations and count valid patterns
    poss
    |> get_perms()
    |> Enum.filter(fn perm ->
      word = Enum.map(perm, &poss[&1])

      word == ["A", "M", "S", "M", "S"] || word == ["A", "S", "M", "S", "M"]
    end)
    |> length()
  end
end

# Original Sample Test Input
sample = [
  "MMMSXXMASM",
  "MSAMXMSMSA",
  "AMXSXMAAMM",
  "MSAMASMSMX",
  "XMASAMXAMM",
  "XXAMMXXAMA",
  "SMSMSASXSS",
  "SAXAMASAAA",
  "MAMMMXMMMM",
  "MXMXAXMASX"
]

# Run the test
result = Temp.count_xmas(sample)
IO.puts("Number of X-MAS patterns: #{result}")