Skip to content

Instantly share code, notes, and snippets.

@ignaciovazquez
Last active December 7, 2015 22:44
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ignaciovazquez/af4ab441f302865f9162 to your computer and use it in GitHub Desktop.
Save ignaciovazquez/af4ab441f302865f9162 to your computer and use it in GitHub Desktop.
Advent of Code. Day 7.
defmodule Gates do
use Bitwise
import String, only: [to_atom: 1]
def gate(state \\ %{}) do
receive do
{:print_state} -> IO.inspect state
{:set_type, type} -> state = Dict.put(state, :type, type)
{:set_input_1, pid} -> state = Dict.put(state, :input_1, pid)
{:set_input_2, pid} -> state = Dict.put(state, :input_2, pid)
{:reset} -> state = Dict.delete(state, :value)
{:get_value, from_pid} ->
if Dict.has_key?(state, :value) do
value = Dict.get(state, :value)
else
value_1 = get_wire_value(Dict.get(state, :input_1))
if (Dict.get(state, :type) != "NOT") do
value_2 = get_wire_value(Dict.get(state, :input_2))
end
value = case Dict.get(state, :type) do
"AND" -> value_1 &&& value_2
"OR" -> value_1 ||| value_2
"LSHIFT" -> value_1 <<< value_2
"RSHIFT" -> value_1 >>> value_2
"NOT" ->
v = ~~~value_1
if v < 0 do
v = v + round(:math.pow(2,16))
end
end
state = Dict.put(state, :value, value)
end
send from_pid, {:value, value}
end
gate(state)
end
def wire(state \\ %{}) do
receive do
{:set_input, pid} -> state = Dict.put(state, :input, pid)
{:set_output, pid} -> state = Dict.put(state, :output, pid)
{:set_value, val} -> state = Dict.put(state, :value, String.to_integer(val |> to_string))
{:set_name, name} -> state = Dict.put(state, :name, name)
{:get_value, from_pid} ->
cond do
Dict.has_key?(state, :value) ->
value = Dict.get(state, :value)
Dict.has_key?(state, :input) ->
value = get_wire_value(Dict.get(state, :input))
true ->
IO.inspect state
end
send from_pid, {:value, value}
{:print_state} -> IO.inspect state
end
wire(state)
end
def get_wire_value(name) do
wire_pid = get_wire(name)
send wire_pid, {:get_value, self()}
receive do
{:value, val} ->
val = val
end
val
end
def is_int(str) do
case Integer.parse(str) do
{_n, ""} -> true
{_n, _r} -> false
:error -> false
end
end
def get_wire(name) when is_pid(name) do
name
end
def get_wire(name) do
name = to_atom("wire_" <> name)
pid = case Process.whereis(name) do
nil ->
pid = spawn_link(Gates, :wire, [])
Process.register(pid, name)
send pid, {:set_name, name}
pid
pid ->
pid
end
pid
end
def get_virtual_wire_with_value(val) do
pid = spawn_link(Gates, :wire, [])
send pid, {:set_name, "virtual_" <> get_random_str}
send pid, {:set_value, String.to_integer(val)}
pid
end
def get_random_str do
round(:random.uniform() * 100000000) |> to_string
end
def get_input_pid(input_str) do
cond do
is_int(input_str) ->
input_str_pid = get_virtual_wire_with_value(input_str)
true ->
input_str_pid = get_wire(input_str)
end
input_str_pid
end
def reset_circuit do
Enum.each(Process.registered(), fn process ->
if (String.starts_with?(to_string(process), "gate_")) do
send Process.whereis(process), {:reset}
end
if (String.starts_with?(to_string(process), "wire_")) do
send Process.whereis(process), {:reset}
end
end)
end
def parse_line(line) do
gate_regex = {~r|^(\w+) ([A-Z]+) (\w+) -> (\w+)|, :gate}
gate_regex_not = {~r|^NOT (\w+) -> (\w+)|, :gate_not}
gate_regex_val = {~r|^(\d+) -> (\w+)|, :val}
gate_regex_wire = {~r|(\w+) -> (\w+)|, :wire_to_wire}
regexs = [gate_regex, gate_regex_not, gate_regex_val, gate_regex_wire]
parsed_line = Enum.find_value(regexs, fn({regex, type}) ->
case Regex.run(regex, line) do
nil -> false
[_ | results] -> {type, results}
end
end)
ret = nil
case parsed_line do
{:val, [val, wire_name]} ->
wire_pid = get_wire(wire_name)
send wire_pid, {:set_value, val}
{:wire_to_wire, [wire_name_1, wire_name_2]} ->
wire_pid_1 = get_wire(wire_name_1)
wire_pid_2 = get_wire(wire_name_2)
send wire_pid_1, {:set_output, wire_pid_2}
send wire_pid_2, {:set_input, wire_pid_1}
{:gate, [input_1, type, input_2, output]} ->
gate_pid = spawn_link(Gates, :gate, [])
# Register gate
Process.register gate_pid, to_atom("gate_" <> get_random_str)
input_1_pid = get_input_pid(input_1)
input_2_pid = get_input_pid(input_2)
send gate_pid, {:set_input_1, input_1_pid}
send gate_pid, {:set_input_2, input_2_pid}
send gate_pid, {:set_type, type}
output_pid = get_wire(output)
send output_pid, {:set_input, gate_pid}
ret = gate_pid
{:gate_not, [input_1, output]} ->
gate_pid = spawn_link(Gates, :gate, [])
input_1_pid = get_input_pid(input_1)
send gate_pid, {:set_input_1, input_1_pid}
send gate_pid, {:set_type, "NOT"}
output_pid = get_wire(output)
send output_pid, {:set_input, gate_pid}
ret = gate_pid
end
ret
end
def parse_instructions do
input_stream = File.stream!("input.txt")
Enum.each(input_stream, fn line ->
line = String.strip(line)
parse_line(line)
end)
end
end
Gates.parse_instructions
answer = Gates.get_wire_value("a")
IO.puts "Part 1: #{answer}"
b_wire = Gates.get_wire("b")
send b_wire, {:set_value, answer}
Gates.reset_circuit
answer = Gates.get_wire_value("a")
IO.puts "Part 2: #{answer}"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment