206 lines
3.5 KiB
Elixir
206 lines
3.5 KiB
Elixir
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.inspect(memory)
|
|
|
|
case eval(Enum.drop(memory, ip), memory) do
|
|
{memory, :halt} ->
|
|
memory
|
|
|
|
{memory, :cont, offset} ->
|
|
run(memory, ip + offset)
|
|
|
|
{memory, :jump, ip} ->
|
|
run(memory, ip)
|
|
end
|
|
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
|
|
get_param(op, 0, param, memory) |> IO.inspect()
|
|
|
|
{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
|