Skip to content

Instantly share code, notes, and snippets.

@smpallen99
Created January 12, 2017 18:25
Show Gist options
  • Select an option

  • Save smpallen99/a840db02971d1051e2375d34d8f21be3 to your computer and use it in GitHub Desktop.

Select an option

Save smpallen99/a840db02971d1051e2375d34d8f21be3 to your computer and use it in GitHub Desktop.
Tree of Life Implementation in Elixir
# Refer to https://www.hackerrank.com/challenges/the-tree-of-life for the problem description
defmodule Life do
use Bitwise
def run do
rule = read_int
initial_state = IO.gets("") |> String.trim
for _ <- 1..read_int() do
[i, node] = IO.gets("") |> String.trim |> String.split(" ")
{String.to_integer(i), node}
end
|> simulate(rule, initial_state)
|> Enum.map(&IO.puts/1)
end
def init, do: %{count: 0, history: %{}}
def simulate(cases, rule, initial_state) do
init
|> build_tree(initial_state)
|> build_rules(rule)
|> run_cases(cases)
end
def build_tree(state, initial_state) do
tree = do_build_tree([], initial_state)
put_in(state, [:tree], tree)
|> put_in([:history,0],tree)
end
def do_build_tree([tree], ""), do: tree
def do_build_tree(stack, " " <> str), do: do_build_tree(stack, str)
def do_build_tree(stack, "(" <> str), do: do_build_tree(stack, str)
def do_build_tree([right, mid, left|stack], ")" <> str) do
[{mid, left, right}|stack]
|> do_build_tree(str)
end
def do_build_tree(stack, <<char::size(8), str::bitstring>>) do
[get_value(char)|stack]
|> do_build_tree(str)
end
def get_value(?.), do: 0
def get_value(?X), do: 1
def get_symbol(0), do: "."
def get_symbol(1), do: "X"
def run_cases(state, cases, acc \\ [])
def run_cases(_state, [], acc), do: Enum.reverse(acc)
def run_cases(%{count: count, history: history} = state, [{num, query}|cases], acc) when num < 0 do
query = parse_query(query)
count = count + num
tree = Map.get(history, count)
state = put_in(state, [:count], count)
|> put_in([:tree], tree)
state
|> run_cases(cases, [find_value(tree, query)|acc])
end
def run_cases(state, [{0, query}|cases], acc) do
run_cases(state, cases, [find_value(state[:tree], parse_query(query))|acc])
end
def run_cases(%{count: count} = state, [{i, query}|cases], acc) do
query = parse_query(query)
{state, tree} = case get_in(state, [:history, count + i]) do
nil ->
do_run_case(state, i)
tree -> {put_in(state, [:count], count + i), tree}
end
put_in(state, [:tree], tree)
|> run_cases(cases, [find_value(tree, query)|acc])
end
def do_run_case(state, i) do
1..i
|> Enum.reduce({state, state[:tree]}, fn _, {state, acc} ->
new_new = run_case(state, acc)
state = update_in(state, [:count], &(&1 + 1))
state = put_in(state, [:history, state[:count]], new_new)
{state, new_new}
end)
end
def run_case(state, tree) do
parent = elem(tree,0)
new_parent = new_value(state, 0, {parent, elem(tree,1), elem(tree,2)})
{new_parent, do_run_case(state, parent, elem(tree,1)), do_run_case(state, parent, elem(tree,2))}
end
def do_run_case(state, one, tree) do
case new_value(state, one, tree) do
false ->
new_value_leaf(state, one, tree)
nv ->
{nv, do_run_case(state, elem(tree,0), elem(tree,1)), do_run_case(state, elem(tree,0), elem(tree,2))}
end
end
def new_value(%{rules: rules}, one, {three, two, four}) do
inx = (one <<< 3) + (get_node_value(two) <<< 2) + (three <<< 1) + get_node_value(four)
rules[inx]
end
def new_value(_, _, _), do: false
def new_value_leaf(%{rules: rules}, one, node) do
inx = (one <<< 3) + 0 + (node <<< 1)
rules[inx]
end
def get_node_value({v, _, _}), do: v
def get_node_value(v), do: v
def find_value(tree, query) do
_find_value(tree, query)
end
def _find_value(tree, []) when is_tuple(tree) do
elem(tree, 0) |> get_symbol
end
def _find_value(tree, []), do: get_symbol(tree)
def _find_value(tree, [:right|query]), do: _find_value(elem(tree,2), query)
def _find_value(tree, [:left|query]), do: _find_value(elem(tree,1), query)
def parse_query(query, acc \\ [])
def parse_query("[" <> str, acc), do: parse_query(str, acc)
def parse_query("]" <> _, acc), do: Enum.reverse(acc)
def parse_query(">" <> str, acc), do: parse_query(str, [:right|acc])
def parse_query("<" <> str, acc), do: parse_query(str, [:left|acc])
def build_rules(state, rule) do
rules = for i <- 0..15 do
{i, (rule >>> i) &&& 1}
end
|> Enum.into(%{})
put_in state, [:rules], rules
end
def read_int do
IO.gets("") |> String.trim |> String.to_integer
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment