Last active
December 7, 2015 22:44
-
-
Save ignaciovazquez/af4ab441f302865f9162 to your computer and use it in GitHub Desktop.
Advent of Code. Day 7.
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 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