diff --git a/lib/day9/day9.ex b/lib/day9/day9.ex new file mode 100644 index 0000000..750a688 --- /dev/null +++ b/lib/day9/day9.ex @@ -0,0 +1,239 @@ +defmodule Day9 do + def read_program do + File.read!("lib/day9/input.txt") + |> String.trim() + |> String.split(",") + |> Enum.map(&String.to_integer/1) + end + + def run_program do + read_program() + |> expand_memory() + |> run_cli() + end + + def test do + # prog = [109, 1, 204, -1, 1001, 100, 1, 100, 1008, 100, 16, 101, 1006, 101, 0, 99] + # prog = [1102, 34_915_192, 34_915_192, 7, 4, 7, 99, 0] + prog = [104, 1_125_899_906_842_624, 99] + expanded = expand_memory(prog, 150) + run_cli(expanded) + end + + def run_cli(memory, opts \\ []) do + parent = self() + + proc = + spawn(fn -> + run(memory, parent) + end) + + handle_cli_messages(proc, opts) + end + + def handle_cli_messages(proc, opts) do + receive do + {:halt, _, memory} -> + if Keyword.get(opts, :memory, false) do + memory + else + :ok + end + + {:ok, _, memory} -> + if Keyword.get(opts, :memory, false) do + memory + else + :ok + end + + {:in, _} -> + res = IO.gets("intcode> ") |> String.trim() |> String.to_integer() + send(proc, {:in, res}) + handle_cli_messages(proc, opts) + + {:out, _, out} -> + IO.puts("Output: #{out}") + handle_cli_messages(proc, opts) + end + end + + def expand_memory(memory, length \\ nil) do + memory ++ repeat(0, length || length(memory) * 10) + end + + def repeat(_, 0), do: [] + def repeat(val, count), do: [val | repeat(val, count - 1)] + + def run(state, parent, ip \\ 0) + + def run(memory, parent, ip) when is_list(memory) do + run({memory, 0}, parent, ip) + end + + def run({memory, _relative_base} = state, parent, ip) when ip < length(memory) do + # IO.puts("IP: #{ip}") + # IO.inspect(memory) + + case eval(Enum.drop(memory, ip), state, parent) do + {memory, :halt} -> + send(parent, {:halt, self(), memory}) + + {state, :cont, offset} -> + run(state, parent, ip + offset) + + {state, :jump, ip} -> + run(state, parent, ip) + end + end + + def run(state, parent, _) do + send(parent, {:ok, self(), state}) + end + + defmacro opcode(op, expected) do + quote do + rem(unquote(op), 100) == unquote(expected) + end + end + + def get_param(op, param, val, {memory, relative_base}) do + place = param + 2 + + case op |> div(pow(10, place)) |> rem(10) do + 0 -> Enum.at(memory, val) + 1 -> val + 2 -> Enum.at(memory, relative_base + val) + end + end + + def get_write_param(op, param, val, {memory, relative_base}) do + place = param + 2 + + case op |> div(pow(10, place)) |> rem(10) do + 2 -> relative_base + val + _ -> 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, relative_base} = state, _) when opcode(op, 1) do + a_val = get_param(op, 0, a, state) + b_val = get_param(op, 1, b, state) + dest = get_write_param(op, 2, dest, state) + + { + {List.replace_at(memory, dest, a_val + b_val), relative_base}, + :cont, + 4 + } + end + + # multiply + def eval([op, a, b, dest | _], {memory, relative_base} = state, _) when opcode(op, 2) do + a_val = get_param(op, 0, a, state) + b_val = get_param(op, 1, b, state) + dest = get_write_param(op, 2, dest, state) + + { + {List.replace_at(memory, dest, a_val * b_val), relative_base}, + :cont, + 4 + } + end + + # input + def eval([op, addr | _], {memory, relative_base} = state, parent) when opcode(op, 3) do + addr = get_write_param(op, 0, addr, state) + send(parent, {:in, self()}) + + res = + receive do + {:in, val} -> val + end + + { + {List.replace_at(memory, addr, res), relative_base}, + :cont, + 2 + } + end + + # output + def eval([op, param | _], state, parent) when opcode(op, 4) do + out = get_param(op, 0, param, state) + send(parent, {:out, self(), out}) + + {state, :cont, 2} + end + + # jump if non-zero + def eval([op, condition, ip | _], state, _) when opcode(op, 5) do + case get_param(op, 0, condition, state) do + 0 -> + {state, :cont, 3} + + _ -> + {state, :jump, get_param(op, 1, ip, state)} + end + end + + # jump if zero + def eval([op, condition, ip | _], state, _) when opcode(op, 6) do + case get_param(op, 0, condition, state) do + 0 -> + {state, :jump, get_param(op, 1, ip, state)} + + _ -> + {state, :cont, 3} + end + end + + # less than + def eval([op, a, b, dest | _], {memory, relative_base} = state, _) when opcode(op, 7) do + a_val = get_param(op, 0, a, state) + b_val = get_param(op, 1, b, state) + dest = get_write_param(op, 2, dest, state) + + memory = + if a_val < b_val do + List.replace_at(memory, dest, 1) + else + List.replace_at(memory, dest, 0) + end + + {{memory, relative_base}, :cont, 4} + end + + # equals + def eval([op, a, b, dest | _], {memory, relative_base} = state, _) when opcode(op, 8) do + a_val = get_param(op, 0, a, state) + b_val = get_param(op, 1, b, state) + dest = get_write_param(op, 2, dest, state) + + memory = + if a_val == b_val do + List.replace_at(memory, dest, 1) + else + List.replace_at(memory, dest, 0) + end + + {{memory, relative_base}, :cont, 4} + end + + # set relative base + def eval([op, new_base | _], {memory, relative_base} = state, _) when opcode(op, 9) do + {{memory, relative_base + get_param(op, 0, new_base, state)}, :cont, 2} + end +end diff --git a/lib/day9/input.txt b/lib/day9/input.txt new file mode 100644 index 0000000..1e22df7 --- /dev/null +++ b/lib/day9/input.txt @@ -0,0 +1 @@ +1102,34463338,34463338,63,1007,63,34463338,63,1005,63,53,1101,0,3,1000,109,988,209,12,9,1000,209,6,209,3,203,0,1008,1000,1,63,1005,63,65,1008,1000,2,63,1005,63,904,1008,1000,0,63,1005,63,58,4,25,104,0,99,4,0,104,0,99,4,17,104,0,99,0,0,1102,1,39,1013,1102,1,21,1018,1101,0,336,1027,1102,1,38,1012,1101,534,0,1025,1101,539,0,1024,1101,0,380,1023,1102,1,23,1014,1102,29,1,1000,1102,24,1,1019,1102,1,28,1011,1101,339,0,1026,1101,31,0,1005,1102,36,1,1017,1102,26,1,1007,1102,1,407,1028,1101,387,0,1022,1101,0,30,1001,1101,34,0,1010,1102,1,32,1006,1101,0,1,1021,1102,27,1,1008,1102,22,1,1004,1102,1,20,1015,1101,0,37,1016,1101,0,0,1020,1102,1,398,1029,1101,25,0,1009,1101,0,35,1003,1101,33,0,1002,109,27,1206,-6,197,1001,64,1,64,1105,1,199,4,187,1002,64,2,64,109,-22,2107,26,3,63,1005,63,217,4,205,1105,1,221,1001,64,1,64,1002,64,2,64,109,17,21107,40,39,-8,1005,1014,241,1001,64,1,64,1105,1,243,4,227,1002,64,2,64,109,-8,1206,6,261,4,249,1001,64,1,64,1106,0,261,1002,64,2,64,109,-7,2108,24,0,63,1005,63,281,1001,64,1,64,1105,1,283,4,267,1002,64,2,64,109,11,21102,41,1,-3,1008,1015,42,63,1005,63,303,1105,1,309,4,289,1001,64,1,64,1002,64,2,64,109,1,1205,2,327,4,315,1001,64,1,64,1105,1,327,1002,64,2,64,109,10,2106,0,-2,1106,0,345,4,333,1001,64,1,64,1002,64,2,64,109,-15,21102,42,1,3,1008,1017,42,63,1005,63,367,4,351,1105,1,371,1001,64,1,64,1002,64,2,64,109,-1,2105,1,10,1001,64,1,64,1105,1,389,4,377,1002,64,2,64,109,24,2106,0,-9,4,395,1001,64,1,64,1105,1,407,1002,64,2,64,109,-30,1208,-2,32,63,1005,63,427,1001,64,1,64,1106,0,429,4,413,1002,64,2,64,109,2,1201,0,0,63,1008,63,27,63,1005,63,449,1106,0,455,4,435,1001,64,1,64,1002,64,2,64,109,5,21107,43,44,0,1005,1014,473,4,461,1106,0,477,1001,64,1,64,1002,64,2,64,109,-16,1202,3,1,63,1008,63,33,63,1005,63,501,1001,64,1,64,1106,0,503,4,483,1002,64,2,64,109,10,1207,-4,21,63,1005,63,523,1001,64,1,64,1106,0,525,4,509,1002,64,2,64,109,11,2105,1,5,4,531,1106,0,543,1001,64,1,64,1002,64,2,64,109,-8,21101,44,0,5,1008,1016,47,63,1005,63,563,1106,0,569,4,549,1001,64,1,64,1002,64,2,64,109,-13,2102,1,8,63,1008,63,34,63,1005,63,593,1001,64,1,64,1105,1,595,4,575,1002,64,2,64,109,8,1208,-1,31,63,1005,63,617,4,601,1001,64,1,64,1106,0,617,1002,64,2,64,109,-8,2108,33,4,63,1005,63,635,4,623,1105,1,639,1001,64,1,64,1002,64,2,64,109,10,1202,-1,1,63,1008,63,26,63,1005,63,665,4,645,1001,64,1,64,1105,1,665,1002,64,2,64,109,-9,2107,30,1,63,1005,63,685,1001,64,1,64,1105,1,687,4,671,1002,64,2,64,109,25,1205,-4,703,1001,64,1,64,1105,1,705,4,693,1002,64,2,64,109,-19,2101,0,-5,63,1008,63,26,63,1005,63,725,1105,1,731,4,711,1001,64,1,64,1002,64,2,64,109,6,1207,-2,26,63,1005,63,749,4,737,1105,1,753,1001,64,1,64,1002,64,2,64,109,-10,21108,45,46,9,1005,1010,769,1105,1,775,4,759,1001,64,1,64,1002,64,2,64,109,-10,1201,10,0,63,1008,63,30,63,1005,63,801,4,781,1001,64,1,64,1106,0,801,1002,64,2,64,109,21,21108,46,46,3,1005,1015,819,4,807,1106,0,823,1001,64,1,64,1002,64,2,64,109,-4,2102,1,-3,63,1008,63,31,63,1005,63,849,4,829,1001,64,1,64,1106,0,849,1002,64,2,64,109,-5,2101,0,1,63,1008,63,22,63,1005,63,875,4,855,1001,64,1,64,1105,1,875,1002,64,2,64,109,17,21101,47,0,-3,1008,1017,47,63,1005,63,897,4,881,1105,1,901,1001,64,1,64,4,64,99,21101,0,27,1,21102,1,915,0,1105,1,922,21201,1,38480,1,204,1,99,109,3,1207,-2,3,63,1005,63,964,21201,-2,-1,1,21101,0,942,0,1106,0,922,21202,1,1,-1,21201,-2,-3,1,21101,957,0,0,1105,1,922,22201,1,-1,-2,1106,0,968,22101,0,-2,-2,109,-3,2105,1,0