Last active
December 12, 2018 17:05
-
-
Save gungorkocak/15140b4b116d906ce3e7b3d8e1230fa5 to your computer and use it in GitHub Desktop.
#AdventOfCode 2018 #elixir #elixirlang
This file contains 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 Day12 do | |
@initial_state "##.#....#..#......#..######..#.####.....#......##.##.##...#..#....#.#.##..##.##.#.#..#.#....#.#..#.#" | |
@doc """ | |
Resolves part#1. | |
""" | |
@spec index_sum_after_iteration(pos_integer) :: pos_integer | |
def index_sum_after_iteration(iteration \\ 20) do | |
last_state = iterate(@initial_state, iteration) | |
to_sum(last_state) | |
end | |
@doc """ | |
Resolves part#2. | |
""" | |
@spec sum_of_very_far_away_generation(pos_integer) :: integer | |
def sum_of_very_far_away_generation(very_far_away_iteration) do | |
{eq_iteration, sum, diff} = find_equilibrium_iteration() | |
sum + (very_far_away_iteration - eq_iteration + 1) * diff | |
end | |
@doc """ | |
Finds the generation which incrementation stabilizes for the last 10 generations. | |
Returns {generation, sum, diff} | |
""" | |
@spec find_equilibrium_iteration() :: {pos_integer, integer, integer} | |
def find_equilibrium_iteration() do | |
find_equilibrium_iteration(@initial_state, 1, [:infinity]) | |
end | |
@max_iterations 10_000 | |
defp find_equilibrium_iteration(current_state, iteration, diffs) do | |
next_state = iterate(current_state, 1) | |
next_sum = to_sum(next_state) | |
current_sum = to_sum(current_state) | |
next_diff = next_sum - current_sum | |
cond do | |
Enum.all?(diffs, fn diff -> diff == next_diff end) -> | |
{iteration + 1, next_sum, next_diff} | |
iteration == @max_iterations -> | |
{:error, "max iterations reached"} | |
true -> | |
next_diffs = Enum.slice([next_diff | diffs], 0..9) | |
find_equilibrium_iteration(next_state, iteration + 1, next_diffs) | |
end | |
end | |
@doc """ | |
Takes current state string, returns next state (generation) by applying rules to each element. | |
## Examples | |
iex> Day12.iterate("##.#.", 1) | |
".##.#...." | |
""" | |
@spec iterate(String.t(), integer) :: String.t() | |
def iterate(current_state, 0) do | |
current_state | |
end | |
def iterate(current_state, count) do | |
positives = positives_to_set(current_state) | |
next_state = | |
(".." <> current_state <> "..") | |
|> String.graphemes() | |
|> Enum.map(&to_plant_boolean/1) | |
|> Enum.with_index() | |
|> Enum.map(&plant_grows?(&1, positives)) | |
|> Enum.map(&to_plant_string/1) | |
|> Enum.join() | |
iterate(next_state, count - 1) | |
end | |
@doc """ | |
Calculates sum of all positive indexes from plant string. | |
## Examples | |
iex> Day12.to_sum("##.#....#") | |
12 | |
""" | |
@spec to_sum(String.t()) :: integer | |
def to_sum(string) do | |
string | |
|> positives_to_set() | |
|> Enum.sum() | |
end | |
@doc """ | |
Transforms string state into positive MapSet. Starts from 0 index. | |
iex> Day12.positives_to_set("##.#....#") | |
#MapSet<[0, 1, 3, 8]> | |
""" | |
@spec positives_to_set(String.t()) :: MapSet.t() | |
def positives_to_set(string) do | |
string | |
|> String.graphemes() | |
|> Enum.with_index() | |
|> Enum.reduce(MapSet.new(), fn | |
{".", _}, acc -> acc | |
{"#", index}, acc -> MapSet.put(acc, index) | |
end) | |
end | |
defp to_plant_boolean("#"), do: true | |
defp to_plant_boolean(_), do: false | |
defp to_plant_string(true), do: "#" | |
defp to_plant_string(_), do: "." | |
defp plant_grows?({_, index}, positives) do | |
(index - 2)..(index + 2) | |
|> Enum.map(&MapSet.member?(positives, &1)) | |
|> apply_rules() | |
end | |
@doc """ | |
Evalutes item in the middle by checking left and right 2 items, | |
then returns if whether the item will be on the next generation or not. | |
## Current Ruleset (raw): | |
##.# | |
"#####" => "#", | |
".#..#" => "#", | |
"#..#." => "#", | |
"#...#" => "#", | |
"...##" => "#", | |
"##..#" => "#", | |
".#.##" => "#", | |
"#.###" => "#", | |
".##.#" => "#", | |
".#..." => "#", | |
"##..." => "#", | |
"##.##" => "#", | |
"##.#." => "#", | |
"#.##." => "#" | |
... all other combinations are "." | |
""" | |
@spec apply_rules([boolean]) :: boolean | |
def apply_rules([true, true, true, true, true]), do: true | |
def apply_rules([false, true, false, false, true]), do: true | |
def apply_rules([true, false, false, true, false]), do: true | |
def apply_rules([true, false, false, false, true]), do: true | |
def apply_rules([false, false, false, true, true]), do: true | |
def apply_rules([true, true, false, false, true]), do: true | |
def apply_rules([false, true, false, true, true]), do: true | |
def apply_rules([true, false, true, true, true]), do: true | |
def apply_rules([false, true, true, false, true]), do: true | |
def apply_rules([false, true, false, false, false]), do: true | |
def apply_rules([true, true, false, false, false]), do: true | |
def apply_rules([true, true, false, true, true]), do: true | |
def apply_rules([true, true, false, true, false]), do: true | |
def apply_rules([true, false, true, true, false]), do: true | |
def apply_rules(_), do: false | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment