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() end def test do run([ 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(memory, ip \\ 0) def run(memory, ip) when ip < length(memory) do IO.puts("IP: #{ip}") IO.inspect(memory) case eval(Enum.drop(memory, ip), memory) do {_memory, :halt} -> :halt {memory, :cont, offset} -> run(memory, ip + offset) {memory, :jump, ip} -> run(memory, ip) end :ok end def run(memory, _ip), do: memory 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) do res = IO.gets("intcode> ") |> String.trim() |> String.to_integer() { List.replace_at(memory, addr, res), :cont, 2 } end # output def eval([op, param | _], memory) when opcode(op, 4) do out = get_param(op, 0, param, memory) IO.puts("Output: #{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