defmodule AOCHelpers.Grid do import AOCHelpers defstruct [:map, :bounds] def to_grid(str) do lists = str |> lines() |> Enum.map(&letters/1) map = for {list, y} <- Enum.with_index(lists), {v, x} <- Enum.with_index(list), into: %{} do {{x, y}, v} end max_x = map |> Enum.map(fn {{x, _}, _} -> x end) |> Enum.max() max_y = map |> Enum.map(fn {{_, y}, _} -> y end) |> Enum.max() bounds = {0..max_x, 0..max_y} %__MODULE__{map: map, bounds: bounds} end def in_bounds?(%__MODULE__{bounds: {min_x..max_x, min_y..max_y}}, {x, y}) do min_x <= x and x <= max_x and min_y <= y and y <= max_y end def update!(grid, coords, value) do %__MODULE__{grid | map: Map.update!(grid.map, coords, value)} end def get(grid, coords, default \\ nil) do Map.get(grid.map, coords, default) end def map(grid, fun) do map = Enum.map(grid.map, fn {coords, value} -> {coords, fun.(value)} end) |> Enum.into(%{}) %__MODULE__{grid | map: map} end def inspect(%__MODULE__{map: map, bounds: bounds}) do {xs, ys} = bounds for y <- ys, into: "" do line = for x <- xs, into: "" do Map.get(map, {x, y}, " ") end "#{line}\n" end |> IO.puts() end def find_value(grid, value) do with {coords, _} <- Enum.find(grid.map, fn {_, v} -> v == value end) do coords end end def find(grid, fun) do Enum.find(grid.map, fn {_, v} -> fun.(v) end) end def neighbors(grid, coords, diag? \\ false) do coords |> neighbor_coords() |> Enum.filter(fn {c, dirs} -> if diag? do in_bounds?(grid, c) else in_bounds?(grid, c) and length(dirs) == 1 end end) |> Enum.map(fn {c, dirs} -> {c, dirs, Map.get(grid.map, c)} end) end defp neighbor_coords({x, y}) do for {j, j_dir} <- [{y - 1, [:north]}, {y, []}, {y + 1, [:south]}], {i, i_dir} <- [{x - 1, [:west]}, {x, []}, {x + 1, [:east]}], {x, y} != {i, j} do {{i, j}, j_dir ++ i_dir} end end end # defimpl Inspect, for: AOCHelpers.Grid do # import Inspect.Algebra # def inspect(grid, opts) do # end # end