From 7ac6190bc345d72fb13c19204f635aa9baa76f51 Mon Sep 17 00:00:00 2001
From: sloane <1699281+sloanelybutsurely@users.noreply.github.com>
Date: Mon, 11 Dec 2023 11:10:35 -0500
Subject: [PATCH] solve 2023 11.1

---
 2023/README.md          |  4 ++-
 2023/lib/2023/11.ex     | 54 +++++++++++++++++++++++++++++++++++++++++
 2023/lib/aoc_helpers.ex | 33 +++++++++++++++++++++++++
 README.md               |  2 +-
 4 files changed, 91 insertions(+), 2 deletions(-)
 create mode 100644 2023/lib/2023/11.ex

diff --git a/2023/README.md b/2023/README.md
index 9d8f1be..dca55fa 100644
--- a/2023/README.md
+++ b/2023/README.md
@@ -4,7 +4,7 @@
 | :-: | :-: | :-: | :-: | :-: | :-: | :-------------------------: |
 |     |     |     |     |     | [1] |             [2]             |
 | [3] | [4] | [5] |  6  | [7] | [8] | [9]<sup>[haskell][9h]</sup> |
-| 10  | 11  | 12  | 13  | 14  | 15  |              16             |
+| 10  | [11]  | 12  | 13  | 14  | 15  |              16             |
 | 17  | 18  | 19  | 20  | 21  | 22  |              23             |
 | 24  | 25  |     |     |     |     |                             |
 
@@ -20,3 +20,5 @@
 [8]: ./lib/2023/8.ex
 [9]: ./lib/2023/9.ex
 [9h]: ./9.hs
+
+[11]: ./lib/2023/11.ex
diff --git a/2023/lib/2023/11.ex b/2023/lib/2023/11.ex
new file mode 100644
index 0000000..4b2ff73
--- /dev/null
+++ b/2023/lib/2023/11.ex
@@ -0,0 +1,54 @@
+import AOC
+import AOCHelpers
+
+aoc 2023, 11 do
+  def p1(input) do
+    {grid, {x_bounds, y_bounds}} = to_grid(input)
+
+    empty_columns =
+      for x <- x_bounds, Enum.all?(column(grid, x, y_bounds), is?(".")), do: x
+
+    empty_rows =
+      for y <- y_bounds, Enum.all?(row(grid, y, x_bounds), is?(".")), do: y
+
+    galaxies =
+      grid
+      |> Enum.filter(&match?({_, "#"}, &1))
+      |> Enum.map(&elem(&1, 0))
+
+    distance = make_distance_fn(empty_rows, empty_columns)
+
+    for [a, b] <- combinations(galaxies, 2), reduce: 0 do
+      acc -> acc + distance.(a, b)
+    end
+  end
+
+  def p2(_input) do
+  end
+
+  def make_distance_fn(expanded_rows, expanded_columns) do
+    fn {x_a, y_a}, {x_b, y_b} ->
+      x_dist = abs(x_b - x_a)
+      y_dist = abs(y_b - y_a)
+
+      x_range = to_range(x_a, x_b)
+      y_range = to_range(y_a, y_b)
+
+      expanded_x = Enum.count(expanded_columns, &(&1 in x_range))
+      expanded_y = Enum.count(expanded_rows, &(&1 in y_range))
+
+      x_dist + expanded_x + y_dist + expanded_y
+    end
+  end
+
+  def row(grid, y, x_bounds) do
+    for x <- x_bounds, do: Map.get(grid, {x, y})
+  end
+
+  def column(grid, x, y_bounds) do
+    for y <- y_bounds, do: Map.get(grid, {x, y})
+  end
+
+  def to_range(l, r) when l < r, do: Range.new(l, r)
+  def to_range(l, r), do: Range.new(r, l)
+end
diff --git a/2023/lib/aoc_helpers.ex b/2023/lib/aoc_helpers.ex
index 16e0df5..cdda4a3 100644
--- a/2023/lib/aoc_helpers.ex
+++ b/2023/lib/aoc_helpers.ex
@@ -27,6 +27,32 @@ defmodule AOCHelpers do
     |> Enum.map(&integers/1)
   end
 
+  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}
+
+    {map, bounds}
+  end
+
   @doc """
   Take a list of terms and a list of 1-arity functions and apply each function
   to the coresponding term in the list of terms.
@@ -44,4 +70,11 @@ defmodule AOCHelpers do
   def id(x), do: x
   def always(x), do: fn -> x end
   def is?(x), do: &(&1 == x)
+
+  def combinations(_, 0), do: [[]]
+  def combinations([], _), do: []
+
+  def combinations([x | xs], n) do
+    for(tail <- combinations(xs, n - 1), do: [x | tail]) ++ combinations(xs, n)
+  end
 end
diff --git a/README.md b/README.md
index e2f63b1..6f4b1b7 100644
--- a/README.md
+++ b/README.md
@@ -14,7 +14,7 @@
 1. [2020] **17/50** 🌟
 1. [2021] **43/50** 🌟
 1. [2022] **14/50** 🌟
-1. [2023] **15/50** 🌟
+1. [2023] **16/50** 🌟
 
 [2015]: ./2015
 [2017]: ./2017