Skip to content

Instantly share code, notes, and snippets.

@gungorkocak
Last active December 12, 2018 17:05
Show Gist options
  • Save gungorkocak/15140b4b116d906ce3e7b3d8e1230fa5 to your computer and use it in GitHub Desktop.
Save gungorkocak/15140b4b116d906ce3e7b3d8e1230fa5 to your computer and use it in GitHub Desktop.
#AdventOfCode 2018 #elixir #elixirlang
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