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