Powered by AppSignal & Oban Pro

Day 7

2025/07.livemd

Day 7

Mix.install([
  {:kino_aoc,  github: "troynt/kino_aoc", branch: "main" }
])

Setup

{:ok, input} = KinoAOC.download_puzzle("2025", "7", System.fetch_env!("LB_AOC_SESSION"))
{:ok,
 "......................................................................S......................................................................\n.............................................................................................................................................\n......................................................................^......................................................................\n.............................................................................................................................................\n.....................................................................^.^.....................................................................\n.............................................................................................................................................\n....................................................................^.^.^....................................................................\n.............................................................................................................................................\n...................................................................^.^...^...................................................................\n.............................................................................................................................................\n..................................................................^.^...^.^..................................................................\n.............................................................................................................................................\n.................................................................^.^.^.^...^.................................................................\n.............................................................................................................................................\n................................................................^...^...^...^................................................................\n.............................................................................................................................................\n...............................................................^.^.^.^.^.....^...............................................................\n.............................................................................................................................................\n..............................................................^.^.^.^...^.^.^.^..............................................................\n.............................................................................................................................................\n.............................................................^.^.^.^.^.^.^.^...^.............................................................\n.............................................................................................................................................\n............................................................^...^.^.^.^...^.....^............................................................\n.............................................................................................................................................\n...........................................................^...^.^.^.^.^.^.....^.^...........................................................\n.............................................................................................................................................\n..........................................................^.^.^.^.^.^.^.^.^.....^.^..........................................................\n.............................................................................................................................................\n.........................................................^.^.^.^.....^.^...^.^.^...^...................................." <> ...}

Disclaimer!

This code is terrible. Part 2 was tricky due to spacing inconsistencies between my copy & pasted example and downloaded input. Will need to revisit when I have time to investigate. The solution should be simpler.

ex = """
.......S.......
...............
.......^.......
...............
......^.^......
...............
.....^.^.^.....
...............
....^.^...^....
...............
...^.^...^.^...
...............
..^...^.....^..
...............
.^.^.^.^.^...^.
...............
"""
".......S.......\n...............\n.......^.......\n...............\n......^.^......\n...............\n.....^.^.^.....\n...............\n....^.^...^....\n...............\n...^.^...^.^...\n...............\n..^...^.....^..\n...............\n.^.^.^.^.^...^.\n...............\n"

Solution

defmodule Day7 do
  @doc """
      iex> Day7.parse("..S..") |> elem(1)
      {2, 0}
  """
  def parse(str) do
    String.split(str) 
      |> Enum.with_index()
      |> Enum.flat_map(fn {row, y} -> 
        String.split(row, "", trim: true) 
          |> Enum.with_index()
        |> Enum.map(fn {v, x} ->
          {v, {x, y}}
        end)
      end)
    |> Enum.reduce({%{}, nil }, fn {v, pos}, {map, start} -> # acc is grid, start, beam positions
      start = if v == "S" do
        pos
      else
        start
      end
      { Map.put(map, pos, {v, pos}), start }
    end)
  end

  def add_vector({x, y}, {dx, dy}) do
    { x + dx, y + dy }
  end

  @doc """
      iex> Day7.put_beam(%{{0,0} => 1}, {0,0})
      %{{0, 0} => {"|", {0, 0}}}
  """
  def put_beam(grid, pos) do
    if Map.has_key?(grid, pos) do
      Map.put(grid, pos, {"|", pos})
    else
      grid
    end
  end

  def handle_beams(grid, active_beams), do: handle_beams(grid, active_beams, 0)

  @doc """
    iex> Day7.handle_beams(%{{0,0} => 1}, [], 0)
    {%{{0, 0} => 1}, 0}
  """
  def handle_beams(grid, [], split_count), do: { grid, split_count }
  
  def handle_beams(grid, [pos], split_count), do: handle_beam(grid, pos, split_count)
  def handle_beams(grid, [pos | rest], split_count) do
    # print(grid)
    {grid, split_count } = handle_beam(grid, pos, split_count)
    handle_beams(grid, rest, split_count)
  end

  def handle_space(grid, p, split_count) do
    if Map.has_key?(grid, p) do
      handle_beam(put_beam(grid, p), p, split_count)
    else
      handle_beams(grid, [], split_count)
    end
  end

  def handle_split(grid, split_pos, split_count) do
    a = add_vector(split_pos, {-1, 0})
    b = add_vector(split_pos, {1, 0})

    to_add = [a, b] |> Enum.filter(fn p -> Map.has_key?(grid, p) end)

    grid = grid
     |> put_beam(a)
     |> put_beam(b)

    if length(to_add) > 0 do
      handle_beams(grid, [a, b], split_count + 1)
    else
      handle_beams(grid, [], split_count)
    end
  end

  def handle_beam(grid, beam_source, split_count) do
    if Map.has_key?(grid, beam_source) do
      new_pos = add_vector(beam_source, {0, 1})
  
      case Map.get(grid, new_pos, nil) do
          {"^", p } -> handle_split(grid, p, split_count)
          {".", p } -> handle_space(grid, p, split_count)
          {"|", _ } -> { grid, split_count }
          nil ->  { grid, split_count }
      end
    else
      { grid, split_count }
    end
  end

  def print(grid) do
    {max_x, _} = grid |> Map.keys() |> Enum.max_by(fn {x, _} -> x end)
    {_, max_y} = grid |> Map.keys() |> Enum.max_by(fn {_, y} -> y end)
    
    for y <- 0..max_y do
      for x <- 0..max_x do
        {v, _pos } = Map.get(grid, {x, y}, {".", {x, y}})
        IO.write(v)
      end
      IO.puts("")
    end
    IO.puts("")
  end

  def solve(str) do
    {grid, start_pos} = parse(str)

    { _, split_count } = handle_beams(grid, [start_pos])

    split_count
  end
end
{:module, Day7, <<70, 79, 82, 49, 0, 0, 32, ...>>, ...}

Part 1

Day7.solve(input)
1573