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

--- Day 4: Ceres Search ---

2024/day_4.livemd

— Day 4: Ceres Search —

Mix.install([{:kino_aoc, "~> 0.1"}])

Setup

{:ok, puzzle_input} =
  KinoAOC.download_puzzle("2024", "4", System.fetch_env!("LB_AOC_SESSION_COOKIE"))
test_input = Kino.Input.textarea("test_input")
defmodule CeresSearch do
  def parse_rows(input) do
    String.split(input, "\n", trim: true)
  end

  def matrix(input) do
    input
    |> parse_rows()
    |> Enum.map(&String.graphemes/1)
  end

  def parse_columns(input) do
    input
    |> matrix()
    |> Enum.zip_with(&Enum.join/1)
  end

  def parse_diagonal(matrix) do
    for {row, idx} <- Enum.with_index(matrix),
        {col, ^idx} <- Enum.with_index(row) do
      col
    end
  end

  def xmas_counter(row) do
    xmas = ~r/XMAS/ |> Regex.scan(row) |> length()
    samx = ~r/SAMX/ |> Regex.scan(row) |> length()

    xmas + samx
  end

  def count_diagonals(matrix) do
    counter = fn acc, count ->
      acc |> parse_diagonal() |> Enum.join() |> xmas_counter() |> Kernel.+(count)
    end

    down_count =
      matrix
      |> Enum.reduce({matrix, 0}, fn _row, {acc, count} ->
        {tl(acc), counter.(acc, count)}
      end)
      |> elem(1)

    length = matrix |> hd() |> length()

    accross_count =
      2..length
      |> Enum.reduce({Enum.map(matrix, &amp;tl/1), 0}, fn _i, {acc, count} ->
        {Enum.map(acc, &amp;tl/1), counter.(acc, count)}
      end)
      |> elem(1)

    down_count + accross_count
  end

  def sam_or_mas?("S", "M"), do: true
  def sam_or_mas?("M", "S"), do: true
  def sam_or_mas?(_, _), do: false
end

Part 1

import CeresSearch

test_input = Kino.Input.read(test_input)

accross =
  puzzle_input
  |> parse_rows()
  |> Enum.reduce(0, &amp;(xmas_counter(&amp;1) + &amp;2))

vertical =
  puzzle_input
  |> parse_columns()
  |> Enum.reduce(0, &amp;(xmas_counter(&amp;1) + &amp;2))

matrix = matrix(puzzle_input)

diagonals =
  (
    primary = count_diagonals(matrix)
    secondary = matrix |> Enum.reverse() |> count_diagonals()
    primary + secondary
  )

diagonals + accross + vertical

Part 2

grid =
  puzzle_input
  |> matrix()
  |> Enum.with_index()
  |> Enum.reduce(%{}, fn {row, row_i}, acc ->
    for {letter, col_i} <- Enum.with_index(row), reduce: acc do
      acc -> Map.put(acc, {row_i, col_i}, letter)
    end
  end)

for {{row, col}, "A"} <- grid, row > 0 and col > 0, reduce: 0 do
  acc ->
    primary = sam_or_mas?(grid[{row - 1, col - 1}], grid[{row + 1, col + 1}])
    secondary = sam_or_mas?(grid[{row - 1, col + 1}], grid[{row + 1, col - 1}])

    if primary and secondary, do: acc + 1, else: acc
end