AoC20/lib/day8/day8.ex

83 lines
1.7 KiB
Elixir

defmodule Day8 do
@example """
nop +0
acc +1
jmp +4
acc +3
jmp -3
acc -99
acc +1
jmp -4
acc +6
"""
def part1(example \\ false) do
if(example, do: @example, else: File.read!("lib/day8/input.txt"))
|> parse_prog()
|> run_stopping_after_repetition()
end
def part2(example \\ false) do
prog =
if(example, do: @example, else: File.read!("lib/day8/input.txt"))
|> parse_prog()
prog
|> Enum.with_index()
|> Enum.filter(fn
{{"acc", _}, _} -> false
_ -> true
end)
|> Enum.find_value(fn {{insn, val}, index} ->
new_insn = if insn == "nop", do: "jmp", else: "nop"
modified_prog = List.replace_at(prog, index, {new_insn, val})
case run_stopping_after_repetition(modified_prog) do
{:loop, _} -> false
{:halt, acc} -> acc
end
end)
end
def parse_prog(str) do
str
|> String.trim()
|> String.split("\n")
|> Enum.map(&parse_insn/1)
end
def parse_insn(str) do
[_, insn, num] = Regex.run(~r/^(\w+) ([+-]\d+)$/, str)
num = String.to_integer(num)
{insn, num}
end
def run_stopping_after_repetition(prog, {acc, ip, visited_insns} \\ {0, 0, MapSet.new()}) do
cond do
MapSet.member?(visited_insns, ip) ->
{:loop, acc}
ip >= length(prog) ->
{:halt, acc}
true ->
insn = Enum.at(prog, ip)
old_ip = ip
{acc, ip} = run_insn(insn, acc, ip)
run_stopping_after_repetition(prog, {acc, ip, MapSet.put(visited_insns, old_ip)})
end
end
def run_insn({"nop", _}, acc, ip) do
{acc, ip + 1}
end
def run_insn({"acc", val}, acc, ip) do
{acc + val, ip + 1}
end
def run_insn({"jmp", val}, acc, ip) do
{acc, ip + val}
end
end