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

Advent of Code 2024

2024/day06.livemd

Advent of Code 2024

Mix.install([
  {:req, "~> 0.3.2"}
])

Day 6

input =
  "https://adventofcode.com/2024/day/6/input"
  |> Req.get!(headers: [cookie: "session=#{System.get_env("AOC_COOKIE")}"])
  |> Map.get(:body)
sample = """
....#.....
.........#
..........
..#.......
.......#..
..........
.#..^.....
........#.
#.........
......#...
"""
defmodule Day6 do
  def parse(input) do
    lines =
      input
      |> String.split("\n", trim: true)
      |> Enum.map(&String.split(&1, "", trim: true))

    for {row, y} <- Enum.with_index(lines),
        {val, x} <- Enum.with_index(row),
        into: %{},
        do: {{x, y}, val}
  end

  def step({x, y}, :up), do: {x, y - 1}
  def step({x, y}, :down), do: {x, y + 1}
  def step({x, y}, :left), do: {x - 1, y}
  def step({x, y}, :right), do: {x + 1, y}

  def turn(:up), do: :right
  def turn(:down), do: :left
  def turn(:left), do: :up
  def turn(:right), do: :down
end
import Day6

Part 1

grid =
  input
  |> parse()

{start, _} =
  grid
  |> Enum.find(fn {_k, v} -> v == "^" end)

visited = MapSet.new([start])
grid = Map.put(grid, start, ".")

guard_path =
  Stream.iterate(1, &amp;(&amp;1 + 1))
  |> Enum.reduce_while({start, :up, visited}, fn _, {pos, dir, visited} ->
    next = step(pos, dir)

    case Map.get(grid, next) do
      "." ->
        {:cont, {next, dir, MapSet.put(visited, next)}}

      "#" ->
        {:cont, {pos, turn(dir), visited}}

      nil ->
        {:halt, visited}
    end
  end)

guard_path
|> MapSet.size()

Part 2

# positions_to_try =
#   grid
#   |> Enum.filter(fn {_k, v} -> v == "." end)
#   |> Enum.map(&elem(&1, 0))

visited = MapSet.new([start])
grid = Map.put(grid, start, ".")

# check all positions the guard normally walks
positions_to_try =
  guard_path
  |> MapSet.delete(start)

positions_to_try
|> Task.async_stream(fn position_to_block ->
  grid = Map.put(grid, position_to_block, "#")

  Stream.iterate(1, &amp;(&amp;1 + 1))
  |> Enum.reduce_while({start, :up, Map.new()}, fn _, {pos, dir, visited} ->
    next = step(pos, dir)

    case Map.get(grid, next) do
      "." ->
        {:cont, {next, dir, visited}}

      "#" ->
        if Map.get(visited, pos, 0) > 1 do
          {:halt, true}
        else
          {:cont, {pos, turn(dir), Map.update(visited, pos, 1, &amp;(&amp;1 + 1))}}
        end

      nil ->
        {:halt, false}
    end
  end)
end)
|> Enum.count(&amp;(&amp;1 |> elem(1)))