Created
December 10, 2018 11:07
-
-
Save jemmyw/0d6d0998b4a967bdbfb0b3ba57b79859 to your computer and use it in GitHub Desktop.
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
require IEx | |
defmodule Steps do | |
def parse(str) when is_bitstring(str) do | |
str |> String.split("\n") |> parse() | |
end | |
def parse([]), do: [] | |
def parse([h | t]) do | |
[ | |
~r/Step (.+) must be finished before step (.+) can begin./ | |
|> Regex.run(h) | |
|> List.to_tuple() | |
|> Tuple.delete_at(0) | |
| parse(t) | |
] | |
end | |
def first_step(steps) do | |
leftl = steps |> Enum.map(&elem(&1, 0)) |> MapSet.new() | |
rightl = steps |> Enum.map(&elem(&1, 1)) |> MapSet.new() | |
first_value = MapSet.difference(leftl, rightl) |> Enum.sort() |> Enum.at(0) | |
left = | |
MapSet.union(leftl, rightl) | |
|> MapSet.delete(first_value) | |
|> MapSet.to_list() | |
|> Enum.sort() | |
{first_value, left} | |
end | |
def step_list(steps, done \\ {[], []}) | |
def step_list(steps, {[], _}) do | |
{first_value, left} = first_step(steps) | |
step_list( | |
steps, | |
{ | |
[first_value], | |
left | |
} | |
) | |
|> Enum.reverse() | |
end | |
def step_list(_, {done, []}) do | |
done | |
end | |
def step_list(steps, {done, left}) do | |
{next_done, next_left} = next_step(steps, done, left) | |
case next_done do | |
nil -> | |
done | |
_ -> | |
step_list( | |
steps, | |
{ | |
[next_done | done], | |
next_left | |
} | |
) | |
end | |
end | |
def required_for(steps, letter) do | |
Enum.filter( | |
steps, | |
fn | |
{_, ^letter} -> true | |
_ -> false | |
end | |
) | |
|> Enum.map(&elem(&1, 0)) | |
|> MapSet.new() | |
end | |
def next_step(steps, done, left) | |
def next_step(steps, done, [h | t]) do | |
required = required_for(steps, h) | |
done_set = done |> MapSet.new() | |
cond do | |
MapSet.subset?(required, done_set) -> | |
{h, t} | |
true -> | |
{sn, sl} = next_step(steps, done, t) | |
# Add our head back on the list | |
{sn, [h | sl]} | |
end | |
end | |
def next_step(_, _, []), do: {nil, []} | |
def seconds(step, delay \\ 60) do | |
[h | _] = step |> to_charlist | |
h - 64 + delay | |
end | |
def time(steps, delay \\ 60, num_workers \\ 4) | |
def time(steps, delay, num_workers) do | |
workers = Enum.map(1..num_workers, fn _ -> blank_worker() end) | |
{first_value, left} = first_step(steps) | |
next_second(steps, 0, delay, workers, [], [first_value | left]) | |
end | |
def all_workers_available?(workers, second) do | |
Enum.all?(workers, &worker_available?(&1, second)) | |
end | |
def worker_available?({nil, _}, _), do: true | |
def worker_available?({_, ends_at}, second) when second > ends_at, do: true | |
def worker_available?(_, _), do: false | |
def blank_worker(), do: {nil, nil} | |
def worker_for_step(nil, _, _), do: blank_worker() | |
def worker_for_step(step, second, delay) do | |
{step, second + seconds(step, delay) - 1} | |
end | |
def next_second(steps, second, delay, workers, done, left) do | |
{working_workers, next_done} = | |
Enum.reduce(workers, {[], done}, fn worker, {workers, done} -> | |
case {worker_available?(worker, second), worker |> elem(0)} do | |
{true, nil} -> {[blank_worker() | workers], done} | |
{true, step} -> {[blank_worker() | workers], [step | done]} | |
_ -> {[worker | workers], done} | |
end | |
end) | |
{next_workers, next_left} = | |
Enum.reduce(working_workers, {[], left}, fn worker, {workers, left} -> | |
case worker_available?(worker, second) do | |
false -> | |
{[worker | workers], left} | |
true -> | |
{step, next_left} = next_step(steps, next_done, left) | |
{ | |
[worker_for_step(step, second, delay) | workers], | |
next_left | |
} | |
end | |
end) | |
# IO.write(second |> to_string |> String.pad_leading(3) |> String.pad_trailing(4)) | |
# Enum.each(next_workers, fn worker -> | |
# IO.write(worker |> elem(0) |> to_string |> String.pad_leading(3) |> String.pad_trailing(4)) | |
# end) | |
# IO.write(next_done |> Enum.reverse() |> Enum.join() |> String.pad_leading(4)) | |
# IO.write("\n") | |
case all_workers_available?(next_workers, second) do | |
true -> second | |
_ -> next_second(steps, second + 1, delay, next_workers, next_done, next_left) | |
end | |
end | |
end | |
steps = File.read("7.txt") |> elem(1) |> Steps.parse() | |
# IO.inspect(steps) | |
# Step 1 | |
steps |> Steps.step_list() |> Enum.join() |> IO.inspect() | |
# Step 2 | |
steps |> Steps.time(60, 5) |> IO.inspect() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment