Skip to content

Instantly share code, notes, and snippets.

@0xGGGGG
Created December 11, 2018 18:28
Show Gist options
  • Save 0xGGGGG/028e44d92536ddf9841ff5a094e21789 to your computer and use it in GitHub Desktop.
Save 0xGGGGG/028e44d92536ddf9841ff5a094e21789 to your computer and use it in GitHub Desktop.
#AdventOfCode 2018 #elixir #elixirlang
defmodule Day11 do
@ets_table_name :day11_max_powers
@grid {300, 300}
@doc """
Initializes required partial sum caching mechanism (ets table).
"""
def start() do
:ets.new(@ets_table_name, [:set, :named_table, read_concurrency: true])
end
defp lookup({x, y}) do
:ets.lookup(@ets_table_name, {x, y})
end
defp insert({x, y}, {size, power}) do
:ets.insert(@ets_table_name, {{x, y}, {size, power}})
end
@doc """
Calculates total power level based on grid position and serial number.
## Examples
iex> Day11.power_level({3, 5}, 8)
4
iex> Day11.power_level({122, 79}, 57)
-5
iex> Day11.power_level({217, 196}, 39)
0
iex> Day11.power_level({101, 153}, 71)
4
"""
@spec power_level({integer, integer}, integer) :: integer
def power_level({x, y}, serial_num) do
rack_id = x + 10
raw = (rack_id * y + serial_num) * rack_id
third_digit = Integer.digits(raw) |> Enum.reverse() |> Enum.at(2)
third_digit - 5
end
@doc """
Calculates total power of a grid specified by its top left {x, y}, serial_num and size.
## Examples
iex> Day11.power_grid({33, 45}, 18, 3)
29
iex> Day11.power_grid({21, 61}, 42, 3)
30
"""
@spec power_grid({integer, integer}, integer, integer) :: integer
def power_grid({x, y}, serial_num, 1) do
power_level({x, y}, serial_num)
end
def power_grid({x, y}, serial_num, size) do
case lookup({x, y}) do
[] ->
pow =
power_grid({x, y}, serial_num, size - 1) + power_grid_outliers({x, y}, serial_num, size)
insert({x, y}, {size, pow})
pow
[{_, {^size, pow}}] ->
pow
[{_, {prev_size, prev_pow}}] when prev_size == size - 1 ->
pow = prev_pow + power_grid_outliers({x, y}, serial_num, size)
insert({x, y}, {size, pow})
pow
end
end
defp power_grid_outliers({x, y}, serial_num, size) do
x_sum =
Enum.reduce(x..(x + size - 1), 0, fn x, sum ->
sum + power_level({x, y + size - 1}, serial_num)
end)
y_sum =
Enum.reduce(y..(y + size - 2), 0, fn y, sum ->
sum + power_level({x + size - 1, y}, serial_num)
end)
x_sum + y_sum
end
@doc """
Calculates largest total sizexsize subgrid sum then returns sum and {top, left} position of it.
## Examples
iex> Day11.max_power_grid(42, 3)
{{21, 61}, 30}
iex> Day11.max_power_grid(18, 3)
{{33, 45}, 29}
"""
@spec max_power_grid(integer, integer) :: {{integer, integer}, integer}
def max_power_grid(serial_num, size) do
{x, y} = @grid
reduce_grid({1..(x - size), 1..(y - size)}, {{nil, nil}, 0}, fn {x, y}, {max_pos, max_pow} ->
case power_grid({x, y}, serial_num, size) do
pow when pow > max_pow -> {{x, y}, pow}
_ -> {max_pos, max_pow}
end
end)
end
@doc """
Finds and calculates largest total subgrid then returns size and {top, left} position it.
## Examples
# # This is taking too long. Couln't find a proper way to speed it up yet :/
# iex> Day11.max_power_grid_of_all(42)
# {{232, 251}, 119, 12}
"""
@spec max_power_grid_of_all(integer) :: {{integer, integer}, integer, integer}
def max_power_grid_of_all(serial_num) do
{count, _} = @grid
Enum.reduce(1..count, {{nil, nil}, 0, 0}, fn size, {max_pos, max_pow, max_size} ->
IO.puts("Handling size... #{size}")
case max_power_grid(serial_num, size) do
{{x, y}, pow} when pow > max_pow ->
{{x, y}, pow, size}
_ ->
{max_pos, max_pow, max_size}
end
end)
end
defp reduce_grid({minx..maxx, miny..maxy}, acc, fun) do
Enum.reduce(minx..maxx, acc, fn x, acc ->
Enum.reduce(miny..maxy, acc, fn y, acc ->
fun.({x, y}, acc)
end)
end)
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment