83 lines
1.7 KiB
Elixir
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
|