Skip to content

Instantly share code, notes, and snippets.

@QuinnWilton
Last active December 5, 2019 08:46
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save QuinnWilton/35d82ae4aab4fb39e2ac622ab54fc460 to your computer and use it in GitHub Desktop.
Save QuinnWilton/35d82ae4aab4fb39e2ac622ab54fc460 to your computer and use it in GitHub Desktop.
defmodule Aoc.Year2019.Day05.SunnywithaChanceofAsteroids do
import Aoc
defmodule CPU do
alias __MODULE__
defstruct pc: 0, memory: %{}
def execute(%CPU{pc: pc} = cpu) do
cpu
|> read(pc)
|> decode()
|> do_execute(cpu)
|> case do
{:done, %CPU{}} -> :ok
{:step, %CPU{} = cpu, offset} -> cpu |> step(offset) |> execute()
{:jump, %CPU{} = cpu, address} -> cpu |> jump(address) |> execute()
end
end
def step(%CPU{pc: pc} = cpu, by) do
%CPU{cpu | pc: pc + by}
end
def jump(%CPU{} = cpu, pc) do
%CPU{cpu | pc: pc}
end
def read(%CPU{memory: memory} = cpu, address, mode \\ :immediate) do
value = Map.get(memory, address)
case mode do
:immediate -> value
:position -> read(cpu, value, :immediate)
end
end
def write(%CPU{memory: memory} = cpu, address, value) do
%CPU{cpu | memory: Map.put(memory, address, value)}
end
defp decode(instruction) do
opcode = rem(instruction, 100)
modes =
div(instruction, 100)
|> Integer.digits()
|> Enum.reverse()
|> (fn modes -> modes ++ [0, 0] end).()
|> Enum.map(&decode_mode/1)
{decoded, arity} =
case opcode do
99 -> {:exit, 0}
01 -> {:add, 2}
02 -> {:multiply, 2}
03 -> {:save, 0}
04 -> {:output, 1}
05 -> {:jnz, 2}
06 -> {:jz, 2}
07 -> {:lt, 2}
08 -> {:eq, 2}
end
{decoded, Enum.take(modes, arity)}
end
defp decode_mode(0), do: :position
defp decode_mode(1), do: :immediate
defp do_execute({:exit, []}, %CPU{} = cpu) do
{:done, cpu}
end
defp do_execute({:save, []}, %CPU{pc: pc} = cpu) do
value = IO.gets("") |> as_i
dest = read(cpu, pc + 1)
{:step, write(cpu, dest, value), 2}
end
defp do_execute({:add, [m1, m2]}, %CPU{pc: pc} = cpu) do
arg1 = read(cpu, pc + 1, m1)
arg2 = read(cpu, pc + 2, m2)
dest = read(cpu, pc + 3)
{:step, write(cpu, dest, arg1 + arg2), 4}
end
defp do_execute({:multiply, [m1, m2]}, %CPU{pc: pc} = cpu) do
arg1 = read(cpu, pc + 1, m1)
arg2 = read(cpu, pc + 2, m2)
dest = read(cpu, pc + 3)
{:step, write(cpu, dest, arg1 * arg2), 4}
end
defp do_execute({:output, [m1]}, %CPU{pc: pc} = cpu) do
cpu
|> read(pc + 1, m1)
|> IO.puts()
{:step, cpu, 2}
end
defp do_execute({:jnz, [m1, m2]}, %CPU{pc: pc} = cpu) do
check = read(cpu, pc + 1, m1)
if check != 0 do
dest = read(cpu, pc + 2, m2)
{:jump, cpu, dest}
else
{:step, cpu, 3}
end
end
defp do_execute({:jz, [m1, m2]}, %CPU{pc: pc} = cpu) do
check = read(cpu, pc + 1, m1)
if check == 0 do
dest = read(cpu, pc + 2, m2)
{:jump, cpu, dest}
else
{:step, cpu, 3}
end
end
defp do_execute({:lt, [m1, m2]}, %CPU{pc: pc} = cpu) do
arg1 = read(cpu, pc + 1, m1)
arg2 = read(cpu, pc + 2, m2)
to = read(cpu, pc + 3)
if arg1 < arg2 do
{:step, write(cpu, to, 1), 4}
else
{:step, write(cpu, to, 0), 4}
end
end
defp do_execute({:eq, [m1, m2]}, %CPU{pc: pc} = cpu) do
arg1 = read(cpu, pc + 1, m1)
arg2 = read(cpu, pc + 2, m2)
to = read(cpu, pc + 3)
if arg1 == arg2 do
{:step, write(cpu, to, 1), 4}
else
{:step, write(cpu, to, 0), 4}
end
end
end
def input do
File.read!("priv/inputs/year_2019/day_05.txt")
end
def solve(input \\ input()) do
memory =
input
|> String.split(",")
|> as_i
|> Enum.with_index()
|> Enum.reduce(%{}, fn {value, address}, acc ->
Map.put(acc, address, value)
end)
%CPU{memory: memory}
|> CPU.execute()
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment