defmodule AOC.Day8 do def get_input do AOC.Util.input_lines(8, 1) |> Enum.map(fn line -> [opcode, arg] = String.split(line) {String.to_atom(opcode), String.to_integer(arg)} end) |> Enum.with_index() |> Enum.into(%{}, fn {v, k} -> {k, v} end) end def find_loop(program), do: find_loop(program, 0, MapSet.new(), 0) def find_loop(program, acc, seen, ip) do cond do MapSet.member?(seen, ip) -> acc ip >= length(Map.keys(program)) -> {true, acc} true -> seen = MapSet.put(seen, ip) {acc, ip} = case program[ip] do {:acc, arg} -> {acc + arg, ip + 1} {:nop, _} -> {acc, ip + 1} {:jmp, arg} -> {acc, ip + arg} end find_loop(program, acc, seen, ip) end end def part1 do get_input() |> find_loop() end def perturb_program(program) do program |> Stream.reject(fn {_, {opcode, _}} -> opcode == :acc end) |> Stream.map(fn {ip, {:nop, arg}} -> {ip, {:jmp, arg}} {ip, {:jmp, arg}} -> {ip, {:nop, arg}} end) |> Stream.map(fn {ip, v} -> Map.put(program, ip, v) end) end def part2 do get_input() |> perturb_program() |> Stream.map(&find_loop/1) |> Stream.filter(&is_tuple/1) |> Enum.to_list() end end