Created
December 11, 2018 18:28
-
-
Save 0xGGGGG/028e44d92536ddf9841ff5a094e21789 to your computer and use it in GitHub Desktop.
#AdventOfCode 2018 #elixir #elixirlang
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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