Skip to content

Latest commit

 

History

History
135 lines (111 loc) · 2.73 KB

day18.livemd

File metadata and controls

135 lines (111 loc) · 2.73 KB

Day 18

Mix.install([{:kino, "~> 0.5.0"}])

Section

input = """
2,2,2
1,2,2
3,2,2
2,1,2
2,3,2
2,2,1
2,2,3
2,2,4
2,2,6
1,2,5
3,2,5
2,1,5
2,3,5
"""
input = Kino.Input.textarea("paste input here:")
input = Kino.Input.read(input)
cubes =
  input
  |> String.split()
  |> MapSet.new(fn line ->
    line
    |> String.split(",")
    |> Enum.map(&String.to_integer/1)
    |> List.to_tuple()
  end)

neighbors = [{-1, 0, 0}, {1, 0, 0}, {0, -1, 0}, {0, 1, 0}, {0, 0, -1}, {0, 0, 1}]

Part 1

cubes
|> Enum.map(fn {x, y, z} ->
  Enum.count(neighbors, fn {dx, dy, dz} ->
    not MapSet.member?(cubes, {dx + x, dy + y, dz + z})
  end)
end)
|> Enum.sum()

Part 2

defmodule Search do
  @neighbors [{-1, 0, 0}, {1, 0, 0}, {0, -1, 0}, {0, 1, 0}, {0, 0, -1}, {0, 0, 1}]

  def search({x, y, z}, visited, _, {min_x, min_y, min_z}, {max_x, max_y, max_z})
      when x < min_x or y < min_y or z < min_z or x > max_x or y > max_y or z > max_z do
    {0, visited}
  end

  def search({x, y, z}, visited, cubes, min, max) do
    {to_sum, visited} =
      Enum.map_reduce(@neighbors, MapSet.put(visited, {x, y, z}), fn {dx, dy, dz}, visited ->
        neighbor = {x + dx, y + dy, z + dz}

        cond do
          MapSet.member?(cubes, neighbor) -> {1, visited}
          not MapSet.member?(visited, neighbor) -> search(neighbor, visited, cubes, min, max)
          true -> {0, visited}
        end
      end)

    {Enum.sum(to_sum), visited}
  end
end
# with global visited state, is nicer or worse?
defmodule Hacky do
  @neighbors [{-1, 0, 0}, {1, 0, 0}, {0, -1, 0}, {0, 1, 0}, {0, 0, -1}, {0, 0, 1}]

  def search({x, y, z}, _, {min_x, min_y, min_z}, {max_x, max_y, max_z})
      when x < min_x or y < min_y or z < min_z or x > max_x or y > max_y or z > max_z,
      do: 0

  def search({x, y, z} = cur, cubes, min, max) do
    visited = Process.get(:visited)

    if MapSet.member?(visited, cur) do
      0
    else
      Process.put(:visited, MapSet.put(visited, cur))

      Enum.map(@neighbors, fn {dx, dy, dz} ->
        neighbor = {x + dx, y + dy, z + dz}

        if MapSet.member?(cubes, neighbor) do
          1
        else
          search(neighbor, cubes, min, max)
        end
      end)
      |> Enum.sum()
    end
  end
end
{min_x, max_x} = cubes |> Enum.map(&elem(&1, 0)) |> Enum.min_max()
{min_y, max_y} = cubes |> Enum.map(&elem(&1, 1)) |> Enum.min_max()
{min_z, max_z} = cubes |> Enum.map(&elem(&1, 2)) |> Enum.min_max()
min = {min_x - 1, min_y - 1, min_z - 1}
max = {max_x + 1, max_y + 1, max_z + 1}

Process.put(:visited, MapSet.new())
Hacky.search(min, cubes, min, max)
Search.search(min, MapSet.new(), cubes, min, max)
|> elem(0)