let read name = let ic = open_in name in let try_read () = try Some (input_line ic) with End_of_file -> None in let rec loop acc = match try_read () with | Some s -> loop (s::acc) | None -> close_in ic; acc in List.rev (loop []) let build lines = let rec aux res idx = function | h :: t -> let [instr; value] = String.split_on_char ' ' h in let () = res.(idx) <- (0, instr, int_of_string value) in aux res (idx + 1) t | [] -> res in aux (Array.make (List.length lines) (0, "nop", 0)) 0 lines let rec step pc acc instructions = let (n, instr, v) = instructions.(pc) in let () = instructions.(pc) <- (n + 1, instr, v) in match instr with | "nop" -> (n + 1, pc + 1, acc) | "acc" -> (n + 1, pc + 1, acc + v) | "jmp" -> step (pc + v) acc instructions | _ -> raise (Failure "unreachable") let run instructions = let rec aux pc acc instructions = match step pc acc instructions with | (2, _, _) -> raise (Failure (Printf.sprintf "Loop detected with accumulator %d!" acc)) | (_, pc, result) when pc = (Array.length instructions - 1) -> result | (_, pc, acc_) -> aux pc acc_ instructions in aux 0 0 instructions let rec swap_from idx ins = match ins.(idx) with | (_, "nop", v) when v <> 0 -> let () = ins.(idx) <- (0, "jmp", v) in (idx + 1, ins) | (_, "jmp", v) -> let () = ins.(idx) <- (0, "nop", v) in (idx + 1, ins) | _ -> swap_from (idx + 1) ins let fix instructions = let rec aux idx ins instructions = try run ins with e -> let (i, ins) = swap_from idx (Array.copy instructions) in aux i ins instructions in aux 0 (Array.copy instructions) instructions let () = try let _ = read "day8.input" |> build |> run in () with Failure msg -> print_endline msg let () = print_endline (string_of_int (read "day8.input" |> build |> fix))