Powered by AppSignal & Oban Pro

Untitled notebook

2025/day4/day4.livemd

Untitled notebook

Section

Mix.install([
  {:kino, "~> 0.14.2"}
])
defmodule Griddy do
  def enum_to_map(enum) do
    Stream.iterate(0, &(&1 + 1)) |> Enum.zip(enum) |> Map.new()
  end

  def new_from_string(string) do
    Griddy.enum_to_map(
      string
      |> String.split("\n")
      |> Enum.map(&String.graphemes/1)
      |> Enum.map(&Griddy.enum_to_map/1)
    )
  end

  def reduce(grid, initial, func) do
    grid
    |> Enum.reduce(initial, fn {y, sub_map}, acc ->
      sub_map
      |> Enum.reduce(acc, fn {x, value}, acc ->
        func.(value, acc, x, y)
      end)
    end)
  end

  def map(grid, func) do
  grid
  |> Enum.map(fn {y, sub_map} ->
    new_sub_map =
      sub_map
      |> Enum.map(fn {x, value} ->
        {x, func.(value, x, y)}
      end)
      |> Map.new()

    {y, new_sub_map}
  end)
  |> Map.new()
end


  def get_nearby_cells(grid, x, y) do
    above_below_range = -1..1//1

    above_below_range
    |> Enum.map(fn dy ->
      -1..1//1
      |> Enum.map(fn dx ->
        if dx == 0 && dy == 0 do
          nil
        else
          grid |> Griddy.get(x + dx, y + dy)
        end
      end)
    end)
    |> Enum.flat_map(fn a -> a end)
    |> Enum.filter(fn a -> a end)
  end

  def get(grid, x, y) do
    grid[y][x]
  end

  def put(grid, x, y, value) do
    grid |> Map.update(y, nil, fn sub_map -> sub_map |> Map.put(x, value) end)
  end

  def update(grid, x, y, default, func) do
    grid |> Map.update(y, nil, fn sub_map -> sub_map |> Map.update(x, default, func) end)
  end

  def find(map, value) do
    map
    |> Enum.reduce({0, 0}, fn {y, sub_map}, acc ->
      sub_map
      |> Enum.reduce(acc, fn {x, _}, acc ->
        if map[y][x] == value, do: {x, y}, else: acc
      end)
    end)
  end

  def to_string(grid) do
    {string, _} =
      grid
      |> Griddy.reduce({"", nil}, fn value, {acc, prev_y}, _, y ->
        if y != prev_y do
          {acc <> "\n" <> value, y}
        else
          {acc <> value, y}
        end
      end)

    string
  end

  def print(grid) do
    grid |> Griddy.to_string() |> IO.puts()

    grid
  end
end
input = Kino.Input.textarea("Please paste your input file")
grid =
  input
  |> Kino.Input.read()
  |> String.split("\n", trim: true)
  |> Enum.map(&amp;String.trim/1)
  |> Enum.join("\n")
  |> Griddy.new_from_string()

grid |> Griddy.get_nearby_cells(1, 0) |> IO.inspect() |> Enum.count()

grid
|> Griddy.map(fn value, x, y ->
  if value == "@" do
    grid
    |> Griddy.get_nearby_cells(x, y)
    |> Enum.filter(fn a ->
      a == "@"
    end)
    |> Enum.count()
  else
    nil
  end
end)
|> IO.inspect()
|> Enum.flat_map(fn a -> a end)
|> Enum.reduce(0, fn {key, value}, acc ->
  if value < 4 do
    acc + 1
  else
    acc
  end
end)
grid =
  input
  |> Kino.Input.read()
  |> String.split("\n", trim: true)
  |> Enum.map(&amp;String.trim/1)
  |> Enum.join("\n")
  |> Griddy.new_from_string()

initial =
  grid
  |> Griddy.reduce(0, fn value, acc, x, y ->
    if value == "@" do
      acc + 1
    else
      acc
    end
  end)

final_grid =
  1..200
  |> Enum.reduce(grid, fn _value, current_grid ->
    current_grid
    |> Griddy.map(fn value, x, y ->
      if value == "@" do
        count =
          current_grid
          |> Griddy.get_nearby_cells(x, y)
          |> Enum.filter(fn a ->
            a == "@"
          end)
          |> Enum.count()

        if count < 4 do
          "."
        else
          "@"
        end
      else
        "."
      end
    end)
  end)
final_grid |> Griddy.to_string() |> IO.inspect()
final =
  final_grid
  |> Griddy.reduce(0, fn value, acc, _x, _y ->
    if value == "@" do
      acc + 1
    else
      acc
    end
  end)

initial - final

to low 8950