Handheld Halting
defmodule Aoc2020.Day8 do
@moduledoc "Handheld Halting"
def run(), do: part_2()
def part_1() do
parse_input() |> boot()
end
def part_2() do
parse_input()
|> generate_variations()
|> Enum.find_value(fn program ->
case boot(program) do
{:program_exited, acc} -> acc
_ -> false
end
end)
end
def parse_input() do
File.read!("priv/inputs/2020/day8.txt")
|> String.trim()
|> String.split("\n")
|> Enum.map(fn line ->
[instruction, arg] = String.split(line, " ", parts: 2)
{num, _} = Integer.parse(arg)
{String.to_atom(instruction), num}
end)
end
def boot(program), do: run_program(program, 0, 0, [])
def run_program(program, pointer, acc, history \\ []) do
if Enum.member?(history, pointer) do
{:started_loop, acc}
else
case Enum.at(program, pointer) do
{:nop, _} -> run_program(program, pointer + 1, acc, [pointer | history])
{:acc, arg} -> run_program(program, pointer + 1, acc + arg, [pointer | history])
{:jmp, arg} -> run_program(program, pointer + arg, acc, [pointer | history])
nil -> {:program_exited, acc}
end
end
end
# part 2
def generate_variations(program) do
Enum.map(0..(length(program) - 1), fn split_at ->
{head, [next = {inst, arg} | rest]} = Enum.split(program, split_at)
case inst do
:nop ->
head ++ [{:jmp, arg} | rest]
:jmp ->
head ++ [{:nop, arg} | rest]
_ ->
head ++ [next | rest]
end
end)
end
end