defmodule Day5 do def read_program do File.read!("lib/day5/input.txt") |> String.trim() |> String.split(",") |> Enum.map(&String.to_integer/1) end def run_program do read_program() |> run_cli() end def test do run_cli([ 3, 21, 1008, 21, 8, 20, 1005, 20, 22, 107, 8, 21, 20, 1006, 20, 31, 1106, 0, 36, 98, 0, 0, 1002, 21, 125, 20, 4, 20, 1105, 1, 46, 104, 999, 1105, 1, 46, 1101, 1000, 1, 20, 4, 20, 1105, 1, 46, 98, 99 ]) end def run_cli(memory) do parent = self() proc = spawn(fn -> run(memory, parent) end) Process.monitor(proc) handle_cli_messages(proc) end def handle_cli_messages(proc) do receive do {:DOWN, _, _, _, _} -> IO.puts("Exited") {:in, _} -> res = IO.gets("intcode> ") |> String.trim() |> String.to_integer() send(proc, {:in, res}) handle_cli_messages(proc) {:out, _, out} -> IO.puts("Output: #{out}") handle_cli_messages(proc) end end def run(memory, parent, ip \\ 0) def run(memory, parent, ip) when ip < length(memory) do # IO.puts("IP: #{ip}") # IO.inspect(memory) case eval(Enum.drop(memory, ip), memory, parent) do {_memory, :halt} -> send(parent, {:halt, self()}) {memory, :cont, offset} -> run(memory, parent, ip + offset) {memory, :jump, ip} -> run(memory, parent, ip) end end def run(_memory, parent, _) do send(parent, {:ok, self()}) end defmacro opcode(op, expected) do quote do rem(unquote(op), 100) == unquote(expected) end end def get_param(op, param, val, memory) do place = param + 2 if op |> div(pow(10, place)) |> rem(10) == 1 do val else Enum.at(memory, val) end end def pow(base, 1), do: base def pow(base, exp) do base * pow(base, exp - 1) end # halt def eval([99 | _], memory, _) do {memory, :halt} end # add def eval([op, a, b, dest | _], memory, _) when opcode(op, 1) do a_val = get_param(op, 0, a, memory) b_val = get_param(op, 1, b, memory) { List.replace_at(memory, dest, a_val + b_val), :cont, 4 } end # multiply def eval([op, a, b, dest | _], memory, _) when opcode(op, 2) do a_val = get_param(op, 0, a, memory) b_val = get_param(op, 1, b, memory) { List.replace_at(memory, dest, a_val * b_val), :cont, 4 } end # input def eval([3, addr | _], memory, parent) do send(parent, {:in, self()}) res = receive do {:in, val} -> val end { List.replace_at(memory, addr, res), :cont, 2 } end # output def eval([op, param | _], memory, parent) when opcode(op, 4) do out = get_param(op, 0, param, memory) send(parent, {:out, self(), out}) {memory, :cont, 2} end # jump if non-zero def eval([op, condition, ip | _], memory, _) when opcode(op, 5) do case get_param(op, 0, condition, memory) do 0 -> {memory, :cont, 3} _ -> {memory, :jump, get_param(op, 1, ip, memory)} end end # jump if zero def eval([op, condition, ip | _], memory, _) when opcode(op, 6) do case get_param(op, 0, condition, memory) do 0 -> {memory, :jump, get_param(op, 1, ip, memory)} _ -> {memory, :cont, 3} end end # less than def eval([op, a, b, dest | _], memory, _) when opcode(op, 7) do a_val = get_param(op, 0, a, memory) b_val = get_param(op, 1, b, memory) memory = if a_val < b_val do List.replace_at(memory, dest, 1) else List.replace_at(memory, dest, 0) end {memory, :cont, 4} end # equals def eval([op, a, b, dest | _], memory, _) when opcode(op, 8) do a_val = get_param(op, 0, a, memory) b_val = get_param(op, 1, b, memory) memory = if a_val == b_val do List.replace_at(memory, dest, 1) else List.replace_at(memory, dest, 0) end {memory, :cont, 4} end end