Organize code, remove problematic files

This commit is contained in:
Caleb Webber 2024-03-25 21:36:51 -04:00
parent 107b8c6397
commit 15de5e8283
25 changed files with 279 additions and 174 deletions

View file

@ -1,4 +1,4 @@
import RegexUtil, only: [scan_index_with_binary: 2]
import AoC.Util.Regex, only: [scan_index_with_binary: 2]
defmodule Mix.Tasks.Day3 do
use Mix.Task

View file

@ -19,7 +19,7 @@ defmodule Mix.Tasks.Day5 do
def parse_line(line) when line != "" do
make_range = fn line ->
[dest_start, source_start, len] = line |> split_int_list(" ")
{:range, {RangeUtil.from_start(dest_start, len), RangeUtil.from_start(source_start, len)}}
{:range, {AoC.Util.Range.from_start(dest_start, len), AoC.Util.Range.from_start(source_start, len)}}
end
cap = Regex.run(~r/(\w+)-to-(\w+) map:/, line)
@ -46,7 +46,7 @@ defmodule Mix.Tasks.Day5 do
|> Map.put(type, data)
|> Map.put(
:ranges,
ranges |> Stream.map(fn [start, len] -> RangeUtil.from_start(start, len) end)
ranges |> Stream.map(fn [start, len] -> AoC.Util.Range.from_start(start, len) end)
)
:map_key ->
@ -89,7 +89,7 @@ defmodule Mix.Tasks.Day5 do
transpose =
case ranges
|> Enum.find(fn {_, start} -> n in start end) do
{dest_range, source_range} -> RangeUtil.transpose(n, source_range, dest_range)
{dest_range, source_range} -> AoC.Util.Range.transpose(n, source_range, dest_range)
nil -> n
end
@ -103,16 +103,16 @@ defmodule Mix.Tasks.Day5 do
for {dest_range, source_range} <- ranges,
reduce: {[], [range]} do
{transposed_ranges, leftover} ->
intersection = RangeUtil.intersection(range, source_range)
intersection = AoC.Util.Range.intersection(range, source_range)
offset = intersection.first - source_range.first
transpose_start = dest_range.first + offset
transposed = RangeUtil.from_start(transpose_start, intersection |> Range.size())
transposed = AoC.Util.Range.from_start(transpose_start, intersection |> Range.size())
{
[transposed | transposed_ranges],
leftover
|> Enum.flat_map(fn r -> RangeUtil.difference(r, intersection) end)
|> Enum.flat_map(fn r -> AoC.Util.Range.difference(r, intersection) end)
}
end

View file

@ -1,3 +1,3 @@
{_, puzzle_input} = File.read("./input/2023_11.txt")
IO.puts("part 1 #{Galaxy.solve_pt_1(puzzle_input)}")
IO.puts("part 2 #{Galaxy.shortest_paths_expansion_adjusted(puzzle_input, 10**6)}")
#{_, puzzle_input} = File.read("./input/2023_11.txt")
#IO.puts("part 1 #{Galaxy.solve_pt_1(puzzle_input)}")
#IO.puts("part 2 #{Galaxy.shortest_paths_expansion_adjusted(puzzle_input, 10**6)}")

View file

@ -1,2 +1,2 @@
{_, content} = File.read("./input/2023_12.txt")
SprintParseWorker.solve_in_parallel(content) |> IO.puts()
# {_, content} = File.read("./input/2023_12.txt")
#SprintParseWorker.solve_in_parallel(content) |> IO.puts()

View file

@ -35,12 +35,12 @@ defmodule RollingRocks do
end
def solve_1(input) do
StringUtil.transpose(input)
AoC.Util.String.transpose(input)
|> String.split("\n")
|> Enum.with_index()
|> Enum.map(&line_to_tile_map(&1))
|> Enum.map(&roll_rocks(&1, :north))
|> EnumUtil.transpose()
|> AoC.Util.Enum.transpose()
|> Enum.map(fn tilemap ->
tilemap
|> Enum.map(fn tile -> Map.get(tile, :tile) end)
@ -84,16 +84,16 @@ defmodule RollingRocks do
end
def cycle(grid) do
StringUtil.transpose(grid)
AoC.Util.String.transpose(grid)
|> String.split("\n")
|> Enum.with_index()
|> Enum.map(&line_to_tile_map(&1))
|> Enum.map(&roll_rocks(&1, :north))
|> EnumUtil.transpose()
|> AoC.Util.Enum.transpose()
|> Enum.map(&roll_rocks(&1, :west))
|> EnumUtil.transpose()
|> AoC.Util.Enum.transpose()
|> Enum.map(&roll_rocks(&1, :south))
|> EnumUtil.transpose()
|> AoC.Util.Enum.transpose()
|> Enum.map(&roll_rocks(&1, :east))
|> Enum.map(fn tilemap ->
tilemap

View file

@ -1,3 +1,3 @@
{_, content} = File.read("./input/2023_14.txt")
#{_, content} = File.read("./input/2023_14.txt")
RollingRocks.solve_2(content) |> IO.puts()
#RollingRocks.solve_2(content) |> IO.puts()

View file

@ -162,7 +162,7 @@ defmodule Lightbeam do
|> Enum.map(fn dir ->
{
TupleUtil.pairwise_sum(current_position, @direction_tuple[dir]),
AoC.Util.Tuple.pairwise_sum(current_position, @direction_tuple[dir]),
dir
}
end)

View file

@ -2,9 +2,9 @@ defmodule Mix.Tasks.Day19 do
def run(args) do
[part | _] = args
{_, puzzle_input} = File.read("./input/2023_19.txt")
part = if part == "1", do: :part1, else: :part2
part = if part == "1", do: :part1, else: :part2
{commands, toys} = parse(puzzle_input, case part do
{commands, toys} = parse(puzzle_input, case part do
:part1 -> &build_comparator/1
:part2 -> &build_range_comparator/1
end)
@ -52,7 +52,7 @@ defmodule Mix.Tasks.Day19 do
fn toy ->
toy
|> Map.get(facet)
|> RangeUtil.split_at(thresh, if(comp == ">", do: :lb, else: :ub))
|> AoC.Util.Range.split_at(thresh, if(comp == ">", do: :lb, else: :ub))
|> Enum.map(fn new_range ->
if comp == ">" and new_range.first > thresh do
{new_range, target}

View file

@ -24,7 +24,7 @@ defmodule Mix.Tasks.Day8 do
true ->
receive do
n -> loop_receive(max_len, MathUtil.lcm(acc, n), count + 1)
n -> loop_receive(max_len, AoC.Util.Math.lcm(acc, n), count + 1)
end
end
end

View file

@ -1,9 +0,0 @@
defmodule EnumUtil do
def transpose(enum) do
col_count = enum |> Enum.at(0) |> Enum.count()
for col <- 0..(col_count - 1) do
enum |> Enum.map(&Enum.at(&1, col))
end
end
end

View file

@ -1,23 +1,9 @@
defmodule Mix.Tasks.GetInput do
defp get_session() do
{_, content} = File.read("./.session")
content |> String.trim()
end
defp get_request_headers() do
[Cookie: "session=#{get_session()}"]
end
def run(args) do
[year, day] = args
write_file = &File.write("./input/#{year}_#{day}.txt", &1)
HTTPoison.start()
HTTPoison.get!(
"https://adventofcode.com/#{year}/day/#{day}/input",
get_request_headers()
)
|> Map.get(:body)
AoC.Input.fetch_input!(year, day)
|> then(fn i ->
IO.puts(i)
i

15
lib/input.ex Normal file
View file

@ -0,0 +1,15 @@
defmodule AoC.Input do
defp get_session!(), do: File.read!("./.session") |> String.trim()
defp get_request_headers!(), do: [Cookie: "session=#{get_session!()}"]
defp get_url(year, day), do: "https://adventofcode.com/#{year}/day/#{day}/input"
def fetch_input!(year, day) do
HTTPoison.start()
get_url(year, day)
|> HTTPoison.get!(
get_request_headers!()
)
|> Map.get(:body)
end
end

View file

@ -1,78 +0,0 @@
defmodule RangeUtil do
@spec from_start(integer(), integer()) :: Range.t()
def from_start(start, len) do
if len == 0 do
..
else
start..(start + len - 1)
end
end
def transpose(value, source, dest) do
offset = value - (source |> Enum.at(0))
(dest |> Enum.at(0)) + offset
end
@spec contains?(Range.t(), Range.t()) :: boolean
def contains?(r1, r2) do
r2.first >= r1.first and r2.last <= r1.last
end
@spec difference(Range.t(), Range.t()) :: [Range.t()]
def difference(r1, r2) do
cond do
Range.disjoint?(r1, r2) ->
[r1]
RangeUtil.contains?(r2, r1) ->
[]
RangeUtil.contains?(r1, r2) ->
s1_len = r2.first - r1.first
s2_len = r1.last - r2.last
[
RangeUtil.from_start(r1.first, s1_len),
RangeUtil.from_start(r2.last + 1, s2_len)
]
|> Enum.reject(fn r -> r == .. end)
r2.first <= r1.first and r2.last <= r1.last ->
s1_len = r1.last - r2.last
[RangeUtil.from_start(r2.last + 1, s1_len)]
r2.first >= r1.first and r2.last >= r1.last ->
s1_len = r2.first - r1.first
[RangeUtil.from_start(r1.first, s1_len)]
end
end
@spec intersection(Range.t(), Range.t()) :: Range.t()
def intersection(r1, r2) do
cond do
Range.disjoint?(r1, r2) -> ..
RangeUtil.contains?(r2, r1) -> r1
RangeUtil.contains?(r1, r2) -> r2
r2.first <= r1.first and r2.last <= r1.last -> Range.new(r1.first, r2.last)
r2.first >= r1.first and r2.last >= r1.last -> Range.new(r2.first, r1.last)
end
end
@spec split_at(range :: Range.t(), value :: integer(), bound :: :lb | :ub) :: [Range.t()]
def split_at(range, value, bound) do
if value <= range.first or value >= range.last do
[range]
else
ls = min(range.first, value - 1)
le = min(range.last, value - 1)
rs = max(range.first, value + 1)
re = max(range.last, value + 1)
if bound == :lb do
[ls..value, rs..re]
else
[ls..le, value..re]
end
end
end
end

View file

@ -1,10 +0,0 @@
defmodule StringUtil do
def transpose(str) do
EnumUtil.transpose(
str
|> String.split("\n")
|> Enum.map(&String.graphemes(&1))
)|> Enum.map(&Enum.join(&1,"")) |> Enum.join("\n")
end
end

17
lib/util/enum.ex Normal file
View file

@ -0,0 +1,17 @@
defmodule AoC.Util.Enum do
@doc """
Transposes (in the sense of matrices) a collection of nested enums.
iex> AoC.Util.Enum.transpose(
iex> [[1,2,3], [4,5,6], [7,8,9]]
iex> )
[[1,4,7],[2,5,8],[3,6,9]]
"""
def transpose(enum) do
col_count = enum |> Enum.at(0) |> Enum.count()
for col <- 0..(col_count - 1) do
enum |> Enum.map(&Enum.at(&1, col))
end
end
end

View file

@ -1,4 +1,11 @@
defmodule MathUtil do
defmodule AoC.Util.Math do
@moduledoc """
Math utility methods.
"""
@doc """
Returns the greatest common divisor of integers a and b.
"""
@spec gcd(integer(), integer()) :: integer()
def gcd(0, 0) do
0
@ -19,6 +26,9 @@ defmodule MathUtil do
gcd(a, b - a)
end
@doc """
Returns the least common multiple of integers a and b.
"""
@spec lcm(integer(), integer()) :: integer()
def lcm(a, b) do
div(a * b, gcd(a, b))

141
lib/util/range.ex Normal file
View file

@ -0,0 +1,141 @@
defmodule AoC.Util.Range do
@doc """
Given a `start` and a `length`, returns a Range of
start..(start+len-1).
Returns the empty range if `len` is 0.
## Examples
iex> AoC.Util.Range.from_start(10, 2)
10..11
iex> AoC.Util.Range.from_start(10, 0)
..
iex> AoC.Util.Range.from_start(10, 10)
10..19
"""
@spec from_start(integer(), integer()) :: Range.t()
def from_start(start, len) do
if len == 0 do
..
else
start..(start + len - 1)
end
end
@doc """
Given a `value`, z, and a range (x,y) containing that value.
Return the `value`'s "place" in the `dest` range (s,t),
given by s + (z - x). Note that this does not check that
`source` and `dest` are the same size so it is possible the
returned value is not contained within `dest`.
iex> AoC.Util.Range.transpose(5, 1..10, 30..50)
34
"""
def transpose(value, source, dest) do
offset = value - (source |> Enum.at(0))
(dest |> Enum.at(0)) + offset
end
@doc """
Returns true iff r2 is completely contained within r1.
## Examples
iex> AoC.Util.Range.contains?(1..10, 2..8)
true
"""
@spec contains?(Range.t(), Range.t()) :: boolean
def contains?(r1, r2) do
r2.first >= r1.first and r2.last <= r1.last
end
@doc """
Returns the set difference r1 \\ r2.
## Examples
iex> AoC.Util.Range.difference(1..10, 11..20)
[1..10]
iex> AoC.Util.Range.difference(1..10, 2..5)
[1..1, 6..10]
iex> AoC.Util.Range.difference(1..10, -1..3)
[4..10]
"""
@spec difference(Range.t(), Range.t()) :: [Range.t()]
def difference(r1, r2) do
cond do
Range.disjoint?(r1, r2) ->
[r1]
AoC.Util.Range.contains?(r2, r1) ->
[]
AoC.Util.Range.contains?(r1, r2) ->
s1_len = r2.first - r1.first
s2_len = r1.last - r2.last
[
AoC.Util.Range.from_start(r1.first, s1_len),
AoC.Util.Range.from_start(r2.last + 1, s2_len)
]
|> Enum.reject(fn r -> r == .. end)
r2.first <= r1.first and r2.last <= r1.last ->
s1_len = r1.last - r2.last
[AoC.Util.Range.from_start(r2.last + 1, s1_len)]
r2.first >= r1.first and r2.last >= r1.last ->
s1_len = r2.first - r1.first
[AoC.Util.Range.from_start(r1.first, s1_len)]
end
end
@doc """
Returns the set intersection of r1 and r2.
## Examples
iex> AoC.Util.Range.intersection(1..10, 1..3)
1..3
iex> AoC.Util.Range.intersection(1..10, -1..3)
1..3
iex> AoC.Util.Range.intersection(-1..10, 1..3)
1..3
"""
@spec intersection(Range.t(), Range.t()) :: Range.t()
def intersection(r1, r2) do
cond do
Range.disjoint?(r1, r2) -> ..
AoC.Util.Range.contains?(r2, r1) -> r1
AoC.Util.Range.contains?(r1, r2) -> r2
r2.first <= r1.first and r2.last <= r1.last -> Range.new(r1.first, r2.last)
r2.first >= r1.first and r2.last >= r1.last -> Range.new(r2.first, r1.last)
end
end
@spec split_at(range :: Range.t(), value :: integer(), bound :: :lb | :ub) :: [Range.t()]
def split_at(range, value, bound) do
if value <= range.first or value >= range.last do
[range]
else
ls = min(range.first, value - 1)
le = min(range.last, value - 1)
rs = max(range.first, value + 1)
re = max(range.last, value + 1)
if bound == :lb do
[ls..value, rs..re]
else
[ls..le, value..re]
end
end
end
end

View file

@ -1,4 +1,4 @@
defmodule RegexUtil do
defmodule AoC.Util.Regex do
@spec scan_index_with_binary(Regex.t(), binary()) :: list()
def scan_index_with_binary(regex, binary) do
Regex.scan(regex, binary, return: :index)

24
lib/util/string.ex Normal file
View file

@ -0,0 +1,24 @@
defmodule AoC.Util.String do
@doc """
Transposes the string when it represents
a grid of newline separated rows where each
character is a new column.
iex> AoC.Util.String.transpose(
iex> ~s(abc
iex>def
iex>ghi)
iex>)
~s(adg
beh
cfi)
"""
def transpose(str) do
AoC.Util.Enum.transpose(
str
|> String.split("\n")
|> Enum.map(&String.graphemes(&1))
)|> Enum.map(&Enum.join(&1,"")) |> Enum.join("\n")
end
end

View file

@ -1,4 +1,4 @@
defmodule TupleUtil do
defmodule AoC.Util.Tuple do
def pairwise_sum(t1, t2) do
Tuple.to_list(t1)
|> Stream.zip(Tuple.to_list(t2))

View file

@ -14,11 +14,11 @@ defmodule Day5Tests do
end
test "range util does not lie" do
assert RangeUtil.from_start(120, 300) |> Enum.count() === 300
assert AoC.Util.Range.from_start(120, 300) |> Enum.count() === 300
end
test "range util transpose does not lie" do
assert RangeUtil.transpose(98, 98..99, 50..51) === 50
assert AoC.Util.Range.transpose(98, 98..99, 50..51) === 50
end
test "parse works" do
@ -28,7 +28,7 @@ seed-to-soil map:
50 98 2
52 50 48"
# assert Mix.Tasks.Day5.parse(ex |> String.split("\n")) == %{
# :seeds => [79, 14, 55, 13],
# :seeds => [79, 14, 55, 13],
# :maps => %{:seed => {:soil, [{52..99, 50..97}, {50..51, 98..99}]}},
# :last_map_key => :seed
# }

View file

@ -1,31 +0,0 @@
defmodule RangeUtilTest do
use ExUnit.Case
test "RangeUtil.difference should work with disjoint ranges" do
assert RangeUtil.difference(1..10, 11..20) == [1..10]
end
test "RangeUtil.difference should work with contained ranges" do
assert RangeUtil.difference(1..10, 2..5) == [1..1, 6..10]
assert RangeUtil.difference(1..10, 1..10) == []
assert RangeUtil.difference(1..10, 2..10) == [1..1]
assert RangeUtil.difference(1..10, -1..11) == []
assert RangeUtil.difference(1..10, 1..12) == []
assert RangeUtil.difference(1..10, 2..2) == [1..1, 3..10]
end
test "RangeUtil.difference should work with partially overlapped ranges" do
assert RangeUtil.difference(1..10, -1..3) == [4..10]
assert RangeUtil.difference(1..10, 1..1) == [2..10]
assert RangeUtil.difference(1..10, 8..12) == [1..7]
assert RangeUtil.difference(1..10, 2..12) == [1..1]
end
test "RangeUtil.intersection should work with contained ranges" do
assert RangeUtil.intersection(1..10, 1..3) == 1..3
assert RangeUtil.intersection(1..10, -1..3) == 1..3
assert RangeUtil.intersection(-1..10, 1..3) == 1..3
assert RangeUtil.intersection(-1..10, -100..300) == -1..10
assert RangeUtil.intersection(-1..10, 11..300) == (..)
end
end

4
test/util/enum_test.exs Normal file
View file

@ -0,0 +1,4 @@
defmodule AoC.Util.EnumTest do
use ExUnit.Case
doctest AoC.Util.Enum
end

32
test/util/range_test.exs Normal file
View file

@ -0,0 +1,32 @@
defmodule AoC.Util.RangeTest do
use ExUnit.Case
doctest AoC.Util.Range
test "AoC.Util.Range.difference should work with disjoint ranges" do
assert AoC.Util.Range.difference(1..10, 11..20) == [1..10]
end
test "AoC.Util.Range.difference should work with contained ranges" do
assert AoC.Util.Range.difference(1..10, 2..5) == [1..1, 6..10]
assert AoC.Util.Range.difference(1..10, 1..10) == []
assert AoC.Util.Range.difference(1..10, 2..10) == [1..1]
assert AoC.Util.Range.difference(1..10, -1..11) == []
assert AoC.Util.Range.difference(1..10, 1..12) == []
assert AoC.Util.Range.difference(1..10, 2..2) == [1..1, 3..10]
end
test "AoC.Util.Range.difference should work with partially overlapped ranges" do
assert AoC.Util.Range.difference(1..10, -1..3) == [4..10]
assert AoC.Util.Range.difference(1..10, 1..1) == [2..10]
assert AoC.Util.Range.difference(1..10, 8..12) == [1..7]
assert AoC.Util.Range.difference(1..10, 2..12) == [1..1]
end
test "AoC.Util.Range.intersection should work with contained ranges" do
assert AoC.Util.Range.intersection(1..10, 1..3) == 1..3
assert AoC.Util.Range.intersection(1..10, -1..3) == 1..3
assert AoC.Util.Range.intersection(-1..10, 1..3) == 1..3
assert AoC.Util.Range.intersection(-1..10, -100..300) == -1..10
assert AoC.Util.Range.intersection(-1..10, 11..300) == (..)
end
end

View file

@ -0,0 +1,4 @@
defmodule AoC.Util.StringTest do
use ExUnit.Case
doctest AoC.Util.String
end