You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

64 lines
2.5 KiB
OCaml

(* Common util. Should probably put this in a module or something.. *)
let get_one_line file =
try Some (input_line file) with
End_of_file -> None
let get_lines file =
let rec input lines =
match get_one_line file with
Some line -> input (line :: lines)
| None -> List.rev lines
in input []
let read_file file =
let channel = open_in(file) in
get_lines channel
(* --- *)
type operation = Nop | Acc | Jmp
type instruction = { op: operation; arg: int }
let op_of_string = function | "nop" -> Nop | "acc" -> Acc | "jmp" -> Jmp
let read_instruction = function |
op :: arg :: [] -> { op = op_of_string op; arg = int_of_string arg }
let rec walk line_num acc lines_touched instructions =
if List.length instructions < line_num + 1 (* at the end of the list *)
then string_of_int acc (* success! *)
else let { op; arg } = List.nth instructions line_num in (* match on the instruction *)
if List.mem line_num lines_touched (* if we've already been on this line *)
then raise (Failure (string_of_int acc)) (* raise a Failure of acc *)
else match op with (* otherwise match on the op *)
| Nop -> walk (line_num + 1) acc (line_num :: lines_touched) instructions
| Acc -> walk (line_num + 1) (acc + arg) (line_num :: lines_touched) instructions
| Jmp -> walk (line_num + arg) acc (line_num :: lines_touched) instructions
let swap_instruction ins line_num = (* swap the Nop and Jmp at the given line number *)
List.mapi (fun i x ->
if line_num = i then match x with (* if this is our line *)
| { op = Jmp; arg } -> { op = Nop; arg }
| { op = Nop; arg } -> { op = Jmp; arg }
| a -> a (* if it is any other op, don't change it *)
else x (* if it isn't our line number, don't change it *)
) ins
let rec walk_and_fix line_num instructions =
let new_instructions = swap_instruction instructions line_num in (* swap nop and jmp *)
try walk 0 0 [] new_instructions (* just return the acc this returns, if it doesn't raise *)
with Failure acc -> walk_and_fix (line_num + 1) instructions (* Infinite loop detected. recurse! *)
let () =
let ins = read_file "day8.txt"
|> List.map @@ String.split_on_char ' '
|> List.map read_instruction
in
try walk 0 0 [] ins
|> Printf.printf "Complete! Wait this shouldn't have completed.. %s";
with Failure acc -> (* catch infinite loop *)
Printf.printf "Infinite loop detected. Last acc: %s\n" acc;
walk_and_fix 0 ins
|> Printf.printf "Infinite loop fixed! Last acc: %s\n";